3.3 深度测试 vs 面剔除 本节摘要:面剔除在图元阶段按绕序丢掉背面三角形,省掉它们的片元着色。深度测试在片元阶段比较 z,决定新片元是否挡住旧片元。两者都是“丢弃”,但砍的层级不同:剔除砍几何,深度砍片元。关剔除会让封闭网格付双倍片元;关深度会让远近顺序变成画家算法。透明物体常常要测试深度但仍不写入深度。把两个开关当成一个“3D 开关”会调不明白遮挡。 你能学到什么 阅读完本节,你应当能够: 指出面剔除与深度测试分别发生在管线哪一段 对照 CCW 正面约定与镜像缩放导致的“缺面” 说明 LEQUAL 与深度写入关闭的典型组合 解释 z-fighting 与近远平面选择的关系 为不透明、透明、天空盒给出不同的深度策略 两把刀,一把砍三角,一把砍像素 面剔除看三角形绕序。
本节摘要:面剔除在图元阶段按绕序丢掉背面三角形,省掉它们的片元着色。深度测试在片元阶段比较 z,决定新片元是否挡住旧片元。两者都是“丢弃”,但砍的层级不同:剔除砍几何,深度砍片元。关剔除会让封闭网格付双倍片元;关深度会让远近顺序变成画家算法。透明物体常常要测试深度但仍不写入深度。把两个开关当成一个“3D 开关”会调不明白遮挡。
阅读完本节,你应当能够:
depthFunc LEQUAL 与深度写入关闭的典型组合面剔除看三角形绕序。默认认为逆时针为正面,背面丢掉。封闭凸网格从外面看,背面本来也不该看见,丢掉等于免费。开了剔除却看见破洞,常是绕序不统一,或缩放有负值把绕序翻了。这是第 2.2 节组装合同的延迟爆炸。
深度测试看片元的窗口 z 与深度附件里已有的值。更近的留下,更远的丢。它不认识物体,只认识像素级的数。两个共面多边形会打架,因为深度精度有限,这叫 z-fighting。CPU 把近平面设得太近、远平面太远,等于把精度预算浪费在你不关心的范围。
图元装配后: 绕序不对 → 剔除整三角 → 不进光栅 光栅之后: z 比不过 → 丢掉片元 → 少跑或早停 FS
早深度:若 FS 没有 discard、不改深度,硬件可能在 FS 前就测深度,省着色。透明物体的 discard、alpha 测试风格代码会破坏这条优化。又是可编程与固定阶段互相拖后腿的例子。
| 状态 | 默认直觉 | 不透明网格 | 透明玻璃 | 天空盒 |
|---|---|---|---|---|
| CULL_FACE | 开,砍背面 | 开 | 看是否双面 | 常关或只砍前 |
| frontFace | CCW | 与资产绕序一致 | 同左 | 同左 |
| DEPTH_TEST | 开 | 开 | 开 | 开或等值 |
| depthMask | 真,写入 | 真 | 假 | 假或最后画 |
| depthFunc | LESS 或 LEQUAL | LEQUAL 常用 | 同 | EQUAL 有时用于延迟 |
天空盒若先画且深度写入,后面物体可能全被挡,或相反。常见策略:最后画天空,深度函数设为 LEQUAL,盒子几何在远平面附近。这是深度合同,不是“天空着色器特殊”。
gl.enable(gl.CULL_FACE); gl.cullFace(gl.BACK); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.depthMask(true);
WebGL 深度范围默认 0 到 1。NDC 的 z 被映射进来。透视矩阵把精度挤向近处,远处物体更容易 fighting。解决对照:拉近远平面距离、polygon offset、把共面的贴花改成混合而不是第二张共面网格。不要第一反应关深度——那是把 3D 退回 2D 画家。

布料、树叶要双面,关剔除或在资产里复制翻转面。关剔除最简单,片元翻倍。复制面增加顶点。移动端树叶我更倾向专门的双面着色器加 alpha discard,并接受早深度变差,而不是无脑关剔除还加高模。
polygon offset 给深度加一点偏置,用来画贴花、阴影 acne 缓解。参数凭场景调,没有全局真理。它改的是深度写入与比较,不改位置 Attribute。对照:想让贴花“贴住”用 offset;想让物体分开用挪位置。别用 offset 修装配错误。
调试深度:把 FS 输出成可视化的 z,或用引擎的深度材质。CPU 读深度要用 FBO 深度纹理,默认画布深度不一定能采。看见“近处物体被远处挡住”,先确认是否画了透明顺序、是否 depthMask 在上一 pass 被关了没开回来。状态泄漏比公式错误更常见。
⚠️ 常见坑:透明 pass 关了
depthMask之后,不透明 UI 或下一帧物体开始乱挡。状态机没有作用域。pass 结束恢复默认表。💡 关键直觉:剔除问“这三角有没有必要变成像素”;深度问“这像素有没有资格改颜色”。先开对不透明默认,再为特例关写入。
2D 和排序过的 UI 可以。3D 相交的网格无法用单一顺序表达遮挡。深度缓冲存在的理由就是相交。关深度等于要求场景可排序且不相交。游戏世界几乎从不满足。性能优化里“减少 overdraw”是让深度尽早失败,不是删除深度。
NEAR/FAR 数值:单位随便,但比值不能太大。世界以米计时,near 0.1、far 1000 常常比 near 0.001、far 1e7 健康。CPU 选投影参数,是在给深度附件分配比特。第 3.1 节的投影对照在这里变成画质。
除了剔除与深度,还有剪刀测试与视口。视口决定 NDC 怎么映到像素;剪刀限制写哪些像素。分屏、小地图用视口或剪刀。忘了恢复视口,主画面只剩一角。这与 3.4 的 FBO 尺寸问题同类:都是 CPU 状态泄漏。每次回默认 FBO 就把视口设回 drawingBuffer 尺寸,写成函数,不要靠记性。
看不见物体的五种常见原因,建议按这份清单查:MVP 把物体送到视锥外(3.1);绕序加剔除把面砍光(本节);深度函数或 near/far 让比较永失败(本节);混合与颜色掩码没把颜色写出来(3.2);FBO incomplete 或绑错(3.4)。不要一上来改着色器公式。公式错通常是颜色怪,不是完全看不见。完全看不见优先查提交与测试。
倒法线的网格在剔除开启时像破洞,关闭剔除后两面都在但光照仍黑。那是法线方向与绕序同时反了。资产管线要统一。运行时双面光照是补丁,会付双倍片元。补丁可以应急,不能当导出规范。
远平面外的物体被投影裁剪,不是深度测试丢掉的。裁剪发生在图元阶段,更早。把 far 加大能看见,但深度精度更差,fighting 增加。应用雾或层次细节,而不是无脑拉 far。投影参数是深度比特的分配,第 3.1 与本节是同一张饼。
反向 Z 把近处精度重新分配,WebGL 里要改投影与 depthFunc,附件最好用浮点深度。能做,但合同改动大。先收 near/far 比值,往往足够。比值仍爆再评估反向 Z。不要在看不懂深度条纹时直接上反向 Z,那会同时改 3.1 与本节,调试空间为零。先可视化深度纹理,确认打架的是共面还是精度。共面用 offset 或合并网格;精度用投影参数。对症。
绕序不统一或法线与绕序不一致。统一资产。运行时关剔除是双倍片元税,只能应急。双面布料应明确关,并接受填充率。破洞不要用混合补,那是把 3.2 当胶布。
共面 fighting。polygon offset 或把贴花沿法线抬一丁点。抬太多会漂。offset 参数按场景,写进材质。合并进地面纹理则无 fighting,是资产路。运行时抬是程序路。两条对照,按能否改资产选。
先画了天空且深度写入为真,深度附件已是远平面,后面物体比不过。改为最后画、关写入或用 LEQUAL 配远平面几何。天空不是特殊着色器特权,是深度合同。调试可视化深度,一眼能看见整屏被天空写满。
提供一键输出深度可视化。团队不会用可视化就会关深度。关深度是 2D 画家,3D 相交立刻错。near/far 比值门禁:超过一万打警告。关卡编辑器改相机时就能看见。天空盒最后画写进渲染器核心,不要靠每个场景记得。倒法线资产在导入时计算面积与法线一致性,不一致就拒绝。运行时双面是补丁,补丁要打日志,方便事后修资产。fighting 的贴花优先合并进地面,运行时 offset 当二选。门禁让 3.3 从调参变成工程。
深度验收场景:两个交叉盒子、一张贴花、一个天空、一层玻璃。交叉必须对;贴花不闪或可接受 offset;天空在后;玻璃不挡后面不透明。四条过了再谈精细模型。near/far 由关卡半径计算,禁止写死 0.001 与 1e7。编辑器改半径时重算。倒法线拒绝导入,附带网格名。运行时双面打计数,计数进遥测,方便找资产债。剪刀与视口恢复写进 restoreDefaults。分屏小地图漏恢复会让主画面缩到一角,像投影矩阵坏了。先看视口,再看矩阵。清单顺序能省半小时。可视化深度是本节的示波器,没有示波器不准调 offset。
深度可视化做成渲染器核心键,而不是某个人的本地补丁。核心键保证新人也能看见附件里有什么。看不见附件就只能关测试。关测试是把 3D 退回 2D。退回一次就会有人依赖错误顺序,再也开不回来。开不回来的项目不是缺公式,是缺示波器。本节把示波器当成纪律。
现场对照时只改一个变量。同时改版本、封装、通道,归因会消失。消失之后只能靠感觉,感觉正是本书要拆掉的东西。把改动写成一条,测完再写下一条。一条一条,四组对照才用得上。用不上的对照表只是装饰。装饰填不满格子,格子空着就不要标完工。深度这一格改完,用交叉盒子验收,不要直接上城市关卡。near 与 far 由关卡半径算出,禁止抄一套万能数。万能数会在下一关把精度饼分薄,fighting 再回来。回来时不要关测试,去收比值。比值门禁超过一万就警告,警告比事后 fighting 便宜。交叉盒子过了再上复杂网格,复杂网格过不了时先看绕序,再看 near far。绕序资产债用导入拒绝,不要用关剔除当长期药。长期药是资产绕序与投影比值,测试开关只应急。应急要打日志,方便事后把债还给资产。
frontFace 或修资产。discard 与改深度会破坏,透明更贵不仅因为混合。下一节对照输出写到哪里:默认 canvas,还是你自己绑定的离屏帧缓冲。