透明物体renderOrder:深度缓冲区depthWrite、depthTest
子级继承父级renderOrder:
大概意思就是 如果父级renderOrder不是默认值 会先考虑父级的renderOrder,之后在父级对应的renderOrder下,再依据renderOrder进行渲染
在 Three.js 中,当一个 mesh
的材质属性设置为 depthWrite: false
和 depthTest: true
时,其渲染顺序不仅仅由自身的 renderOrder
决定,还可能受到父级对象的 renderOrder
和渲染顺序逻辑的影响。这是因为 Three.js 的渲染流程会综合考虑场景层次结构、物体排序以及透明物体的渲染规则。
1. 继承性
Three.js 渲染的流程遵循场景图的层次结构(Scene Graph)。子对象的渲染行为可能受到父对象的一些设置影响,特别是以下情况:
-
父级
renderOrder
的优先级高于子级: 如果父级的renderOrder
被设置为一个非默认值,Three.js 会优先根据父级的renderOrder
确定渲染顺序,然后再按照子级的renderOrder
排序。 -
父级影响整个子树: 父级的
renderOrder
会成为子对象排序的一个参考基准,从而影响子对象的实际渲染顺序。
2. 透明物体的特殊排序规则
透明物体的渲染逻辑与不透明物体不同:
-
不透明物体的渲染顺序是基于深度缓冲区的,无需特别的
renderOrder
控制。 -
透明物体由于需要混合颜色,需要按照距离摄像机的远近排序(默认行为),除非
renderOrder
被显式设置。
在层次场景中,透明物体的渲染顺序会参考父级的 renderOrder
,因为透明物体通常需要遵循场景的整体渲染逻辑。
3. 渲染逻辑中的场景树遍历
Three.js 的渲染器会按照以下顺序处理场景树:
-
遍历整个场景树,收集所有需要渲染的物体。
-
将物体按照
renderOrder
和深度信息(depthTest
)排序。 -
执行绘制。
在这种遍历和排序过程中:
-
父对象的
renderOrder
被优先采纳,作为子对象渲染的“参考框架”。 -
如果父级的
renderOrder
不为默认值0
,子级即使有自己的renderOrder
,也可能无法完全脱离父级的影响。
4. 解决方法
方法 1:分离父子关系
方法 2:显式控制子对象的 renderOrder
(待考量)
方法 3:通过 onBeforeRender
动态调整渲染顺序
如果复杂场景中无法分离,可以使用 onBeforeRender
在渲染时动态调整物体的属性:
childMesh.onBeforeRender = function () {
this.renderOrder = 5; // 临时调整
};
depthTest
和 depthWrite
是控制 WebGL 深度缓冲区的两个关键属性,它们直接影响 3D 场景中物体的渲染顺序和遮挡关系。
1. depthTest
的作用
depthTest
决定了一个片段在绘制时,是否需要与深度缓冲区的值进行比较。
-
默认值:
true
启用深度测试,片段会根据当前深度缓冲区的值决定是否需要渲染。 -
关闭:
false
禁用深度测试,片段会直接渲染到屏幕,无视深度缓冲区的值。
常见用途
-
启用 (
depthTest: true
):适用于大多数非透明物体,确保近处的物体能够正确遮挡远处的物体。 -
禁用 (
depthTest: false
):适用于始终需要显示的物体(例如 HUD、光效等),无论深度缓冲区如何都直接渲染。
示例
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
material.depthTest = false; // 禁用深度测试
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
-
启用
depthTest
:当前片段仅在通过深度测试时渲染(即片段深度值小于等于深度缓冲区的值)。 -
禁用
depthTest
:当前片段直接渲染,无视深度缓冲。
2. depthWrite
的作用
depthWrite
决定了一个片段在绘制时,是否将其深度值写入深度缓冲区。
-
默认值:
true
启用深度写入,片段的深度值会存储到深度缓冲区中,用于后续渲染的深度测试。 -
关闭:
false
禁用深度写入,片段的深度值不会存储到深度缓冲区中,不会影响后续的深度测试。
常见用途
-
启用 (
depthWrite: true
):适用于大多数非透明物体,使它们参与深度缓冲区的写入和后续深度测试。 -
禁用 (
depthWrite: false
):适用于透明物体或特殊效果,避免它们覆盖深度缓冲区,允许后续物体覆盖。
示例
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.5 });
material.depthWrite = false; // 禁用深度写入
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
-
启用
depthWrite
:片段深度会记录在缓冲区中,影响后续物体的渲染。 -
禁用
depthWrite
:片段深度不会记录,但片段仍可被渲染。
3. depthTest
与 depthWrite
的关系
两者独立工作,但常配合使用以实现复杂的渲染效果:
depthTest |
depthWrite |
效果说明 |
---|---|---|
true |
true |
默认设置,物体会进行深度测试并写入深度缓冲区,确保遮挡正确。 |
true |
false |
物体参与深度测试但不更新深度缓冲区,适合透明物体。 |
false |
true |
物体无视深度缓冲区直接渲染,但更新深度缓冲区,效果不常见。 |
false |
false |
物体无视深度缓冲区直接渲染,且不影响深度缓冲区,适合 HUD 或特殊效果。 |
4. 示例应用场景
非透明物体
-
典型设置:
depthTest: true, depthWrite: true
-
确保正确的遮挡关系,常用于普通物体。
透明物体
-
推荐设置:
depthTest: true, depthWrite: false
-
透明物体通常需要参与深度测试(避免穿模),但不写入深度缓冲区(避免影响后续物体)。
始终显示的物体
-
推荐设置:
depthTest: false, depthWrite: false
-
HUD、光效等始终显示的物体。
复杂效果
-
例如描边效果,可以禁用深度测试并启用深度写入:
const outlineMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
outlineMaterial.depthTest = false; // 不进行深度测试
outlineMaterial.depthWrite = true; // 写入深度缓冲区