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 中一个"存在感很低,但设计非常优雅"的功能

它本质上是:

  • 位掩码

  • 相机过滤机制

  • 渲染前裁剪

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

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

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

  • 拾取逻辑会更可控

相关推荐
郝学胜-神的一滴1 小时前
Python中的“==“与“is“:深入解析与Vibe Coding时代的优化实践
开发语言·数据结构·c++·python·算法
视觉人机器视觉2 小时前
海康机器人3D 机器人引导 —— 空间基础篇一
3d·机器人
程序员林北北2 小时前
【前端进阶之旅】Vue3 + Three.js 实战:从零构建交互式 3D 立方体场景
前端·javascript·vue.js·react.js·3d·typescript
智者很聪明2 小时前
数据结构之栈和队列
c语言·数据结构
uesowys2 小时前
算法开发指导-数据结构-Tree
数据结构·算法·
古译汉书2 小时前
【IoT死磕系列】Day 3:学习HTTP!实战:STM32手写GET请求获取天气实战(附源码+八股文)
数据结构·stm32·物联网·网络协议·学习·算法·http
郝学胜-神的一滴2 小时前
计算思维:数字时代的超级能力
开发语言·数据结构·c++·人工智能·python·算法
二年级程序员3 小时前
一篇文章掌握“双向链表”
c语言·数据结构·链表
元亓亓亓3 小时前
考研408--数据结构--day14--B树&B+树&散列表
数据结构·b树·散列表·b+树·408
季明洵3 小时前
Java实现循环队列、栈实现队列、队列实现栈
java·数据结构·算法··队列