05 深度写入:透明烟片为何相互硬裁
前置知识:第 04 章的深度比较、第 03 章的贴图 Alpha。
本章示例:两片重叠烟雾 · 源码。
预期画面:默认前片先画且写深度,蓝色后片被方形区域硬裁;关闭烟片的depthWrite后,蓝色重新出现,前方灰柱仍遮挡烟雾。
先看现象
红烟位于 z=+0.15,蓝烟在 z=-0.15,相机在正 z 方向。示例故意先提交前方红烟、再提交后方蓝烟。两片都是带柔和 Alpha 贴图的方形平面。默认烟片 depthTest 与 depthWrite 都为 true:红片先写深度,蓝片在重叠的方形区域被拒绝,于是蓝烟出现硬切边。把 depthWrite 关掉,蓝片就不会被红片留下的深度拒绝,可以参与颜色混合。
右侧灰柱是不透明物体,位置比两片烟都靠前。即使烟片关闭深度写入,只要仍有 depthTest: true,灰柱先留下的深度照样挡住烟片。这是最有用的组合:读场景深度,但不让透明层之间互相占据深度缓冲。
1. 测试与写入是两道独立设置
已有深度 D ── 比较候选深度 d(depthTest + depthFunc)
├─ 失败:该片元不更新颜色/深度
└─ 通过:可更新颜色;depthWrite=true 时把 d 写入深度缓冲
depthTest 决定是否拿已有值作比较;depthWrite 决定通过后是否更新已有值。写入不是“开启遮挡”的同义词:遮挡后续片元的效果要等后续片元提交并测试时才发生。depthWrite: false 也不等于“总在最前”,它依然可能被早先的不透明几何挡住。
最常见的透明烟雾设置:
const smokeMaterial = new THREE.MeshBasicMaterial( {
map: smokeTexture,
transparent: true,
opacity: 0.74,
depthTest: true,
depthWrite: false
} );
这些是材质状态,设置在 JavaScript 初始化时即可。透明度和贴图 Alpha 影响颜色混合,但不会自动把低 Alpha 片元从深度写入中排除。在当前示例中,即使贴图边缘 Alpha 接近或等于 0,只要片元没有通过 discard、alphaTest 等方式真正丢弃,它仍可能写深度。这就是为什么方形平面的轮廓能暴露出来。
2. 四种组合如何读
depthTest |
depthWrite |
典型意义 |
|---|---|---|
| true | true | 测试已有深度,通过后写入;适合多数不透明表面,也适合某些裁切后的硬轮廓 |
| true | false | 保留与已有场景几何的遮挡,不让当前透明层污染深度;软烟常用 |
| false | false | 不看已有深度,也不写深度;用于有意穿墙的覆盖效果 |
| false | true | 在 Three.js r174 的 WebGL 路径下,测试关闭会使实际深度写入也关闭;不能靠它建立遮挡 |
切换本例的“后片先画”:在 depthWrite: true 下,蓝片先写的是较远深度,后来更近的红片可通过默认 LessEqualDepth,两片能叠加;但这个结果依赖正确的绘制顺序。一旦烟片相交、旋转或数量增多,对象级排序就难以保证每个片元都正确。关掉深度写入解决了硬裁,却不能自动解决颜色混合的前后顺序;下一章继续分析。
3. 与 alphaTest、alphaHash 的关系
若图案是叶片、碎片等硬轮廓,可以用 alphaTest 使低 Alpha 片元被真正丢弃,保留部分按不透明材料写深度。这通常比让完整方形透明平面写深度合理。但烟雾需要连续软边,硬裁会失去柔和过渡。alphaHash 以随机覆盖近似透明,可能允许更稳定的深度遮挡,不过会引入可见颗粒,且仍要考虑采样和抗锯齿。
对普通 Alpha 烟雾,先选 depthTest: true, depthWrite: false,再检查排序、混合方式与几何规模。加法混合的亮光对排序较不敏感,但不是所有材质的替代品。后续粒子章节会把这个组合用于 THREE.Points 和 Billboard 烟片。
4. 深度写入还能做什么
不透明墙、地面和其他实体通常保持写深度,帮助后续片元被正确挡住。遮挡代理也可以写深度但不写颜色,第 06 章会演示。相反,某些叠加层、透明火花、UI 提示可以有意不写深度,以免它们错误遮住后来绘制的内容。
这些选择都依赖何时绘制:如果遮挡代理画得太晚,之前已经画好的颜色不能自动撤销;如果透明层关闭深度写入,GPU 不会凭空完成逐粒子的排序。需要把测试、写入、透明混合和顺序一起诊断。
常见问题
把烟片 depthWrite 关掉后,为什么蓝烟仍可能盖在红烟上? 两片都通过测试后,普通 Alpha 混合按提交顺序算颜色。示例故意前片先画,所以蓝片后混合;更可靠的排序问题见第 07 章。
为什么关闭 depthTest 也消除了硬裁? 后片不再比较红片留下的深度;代价是它也穿过灰柱。并且在 r174 中关闭测试使实际写入失效。
有 depthWrite: false 就没有性能开销了吗? 没有。烟片仍提交并产生片元,测试、着色、混合与 overdraw 仍有成本;它只控制深度缓冲更新。
继续做第 05 章练习。参考:Material 深度属性。