3D渲染分层机制 Layers 的原理分析(Threejs)

在使用 three.js 的过程中,我们经常会遇到这样的需求:

  • 有些物体 只想让某个相机看到

  • 有些辅助线、调试对象 不想被最终渲染

  • 多相机渲染时,希望 不同相机关注不同的物体

  • 做选中 / 拾取时,只想检测部分物体

three.js 并没有提供"分组渲染开关"这样的显式 API,但其实它内部提供了一套非常轻量、但功能强大的机制 ------ Layers(层)

这篇文章将从概念、原理、用法、常见误区、实战场景几个方面,完整讲清 three.js 的 Layers。

一、什么是 Layers?

Layers 是 three.js 中用于控制对象是否会被相机看到的一套机制。

一句话总结:

只有当「物体的 layer」和「相机的 layer」有交集时,物体才会被渲染

一个直观的类比(非常重要)

你可以把 Layers 理解为:

"物体和相机身上贴的标签"

  • 物体:贴了哪些标签

  • 相机:只看哪些标签

只有标签匹配,物体才会被看到。

二、Layers 的底层结构:其实是一个位掩码

three.js 的 Layers 本质上是一个 32 位的位掩码(bitmask)

javascript 复制代码
object.layers.mask // number 
camera.layers.mask // number 
  • three.js 最多支持 32 个 layer

  • layer 编号范围是:0 ~ 31

  • 每一位表示是否属于某一层

默认情况

javascript 复制代码
layer 0:开启

layer 1~31:关闭

也就是说:

  • 所有 Object3D 默认都在 layer 0

  • 所有 Camera 默认也只看 layer 0

所以你平时不用 layers,也能正常渲染

三、一个最基础的示意图

javascript 复制代码
┌─────────────┐        ┌─────────────┐
│   Object A  │        │   Object B  │
│   layer 0   │        │   layer 1   │
└──────┬──────┘        └──────┬──────┘
       │                      │
       ▼                      ▼
┌────────────────────────────────────┐
│            Camera                  │
│        enabled: layer 0            │
└────────────────────────────────────┘

结果:

  • Object A ✅ 可见

  • Object B ❌ 不可见

四、Layers 的常用 API

1️⃣ 设置物体所在的层

javascript 复制代码
mesh.layers.set(1);    // 只属于 layer 1

⚠️ 注意:set清空原有层

2️⃣ 追加一个 layer(常用)

javascript 复制代码
mesh.layers.enable(2); // 同时属于 layer 0 和 2

3️⃣ 移除某个 layer

javascript 复制代码
mesh.layers.disable(0);

4️⃣ 相机只渲染某一层

javascript 复制代码
camera.layers.set(1);

5️⃣ 判断 layer 是否匹配(底层逻辑)

javascript 复制代码
object.layers.test(camera.layers);

只有返回 true,物体才会被渲染。

五、渲染阶段 Layers 到底发生在什么时候?

这是一个非常容易被忽略,但非常关键的点

three.js 在渲染时,逻辑大致是:

javascript 复制代码
scene.traverse(object)
  └─ if object.layers.test(camera.layers)
       └─ push 到渲染队列

也就是说:

Layers 是在"渲染收集阶段"生效的

而不是在 GPU 阶段

带来的好处

  • 几乎 没有性能开销

  • 比 visible = false 更"底层"

  • 对拾取、辅助相机特别友好

六、一个最经典的例子:主相机 + 辅助相机

场景需求

  • 主相机:渲染正常场景

  • 辅助相机:只渲染辅助线 / Gizmo / 调试物体

javascript 复制代码
// 主相机
const mainCamera = new THREE.PerspectiveCamera();
mainCamera.layers.enable(0);

// 辅助相机
const helperCamera = new THREE.PerspectiveCamera();
helperCamera.layers.set(1);

// 普通物体
mesh.layers.set(0);

// 辅助线
helperLine.layers.set(1);

渲染流程

javascript 复制代码
renderer.render(scene, mainCamera);
renderer.render(scene, helperCamera);

结构示意图

七、Layers 和 visible 的区别(重点)

对比点 layers visible
是否参与遍历
是否被相机控制
是否支持多相机
语义 "谁能看到" "是否存在"

一个非常重要的结论

visible 更像是"物体存在与否"
layers 更像是"谁能看到它"

八、Layers 在 Raycaster(拾取)中的用法

这是 layers 的一个高级但极其实用的用法

javascript 复制代码
raycaster.layers.set(1); 

此时:

  • Raycaster 只会检测 layer 1 的物体

  • 其他物体直接被忽略

常见应用

  • 只拾取可交互物体

  • 忽略背景 / 装饰模型

  • 点云拾取时分层处理

九、一个容易踩的坑(很多人中招)

❌ 误区:以为 set 是"追加"

javascript 复制代码
mesh.layers.set(1); 
mesh.layers.set(2); 

结果:

  • mesh 只在 layer 2

  • layer 1 被清掉了

✅ 正确方式

javascript 复制代码
mesh.layers.set(1); 
mesh.layers.enable(2);

十、一个完整的小 Demo(可直接跑)

javascript 复制代码
const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(
  60,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

// 只看 layer 1
camera.layers.set(1);

const renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);

// 红色立方体(不可见)
const cube1 = new THREE.Mesh(
  new THREE.BoxGeometry(),
  new THREE.MeshBasicMaterial({ color: 'red' })
);
cube1.layers.set(0);
scene.add(cube1);

// 绿色立方体(可见)
const cube2 = new THREE.Mesh(
  new THREE.BoxGeometry(),
  new THREE.MeshBasicMaterial({ color: 'green' })
);
cube2.layers.set(1);
cube2.position.x = 2;
scene.add(cube2);

renderer.render(scene, camera);

十一、什么时候应该用 Layers?

✅ 推荐使用场景:

  • 多相机渲染

  • 辅助对象 / Gizmo

  • Raycaster 精确控制

  • Debug / Overlay 渲染

  • 点云 / 标注系统分层

❌ 不适合用 Layers 的场景:

  • 单纯开关物体显示(用 visible

  • 逻辑分组(用 Group

十二、总结

Layers 是 three.js 中一个"存在感很低,但设计非常优雅"的功能

它本质上是:

  • 位掩码

  • 相机过滤机制

  • 渲染前裁剪

一旦理解了它的原理,你会发现:

  • 多相机渲染会变得非常干净

  • 调试工具的实现会优雅很多

  • 拾取逻辑会更可控

相关推荐
踩坑记录5 小时前
leetcode hot100 146. LRU 缓存 medium OrderedDict 双向链表 双向字典 哈希表
数据结构·链表
应用市场5 小时前
【自动驾驶感知】基于3D部件引导的图像编辑:细粒度车辆状态理解技术详解
人工智能·3d·自动驾驶
季明洵5 小时前
Java实现顺序表
java·数据结构·算法·顺序表
im_AMBER5 小时前
Leetcode 110 奇偶链表
数据结构·学习·算法·leetcode
智码未来学堂15 小时前
探秘 C 语言算法之枚举:解锁解题新思路
c语言·数据结构·算法
青桔柠薯片16 小时前
数据结构:顺序表与链表
数据结构·链表
金枪不摆鳍17 小时前
算法--二叉搜索树
数据结构·c++·算法
向哆哆18 小时前
画栈 · 跨端画师接稿平台:基于 Flutter × OpenHarmony 的整体设计与数据结构解析
数据结构·flutter·开源·鸿蒙·openharmony·开源鸿蒙
季明洵18 小时前
C语言实现顺序表
数据结构·算法·c·顺序表