深度缓冲精度
每个图形开发者迟早会问:“近平面和远平面到底该设多远?”
更激进的会问:“我把近平面设成 0.0001、远平面设成一个天文单位不行吗?”
当他们第一次看到由此产生的 z-fighting(深度冲突)时,那震惊的表情——无价!
9.1 深度缓冲出错的根源
9.1.1 透视投影的非线性
- 正交投影:zeye 与 zwindow 呈线性关系,精度均匀。
- 透视投影:zeye 与 zwindow 呈 1/zeye 关系,近处精度极高,远处精度骤降。
推导过程(OpenGL 透视矩阵)
设近平面 n、远平面 f,则
zndc=f+nf−n+2fnzeye(f−n)
z_{\text{ndc}} = \frac{f+n}{f-n} + \frac{2fn}{z_{\text{eye}}(f-n)}
zndc=f−nf+n+zeye(f−n)2fn
再经视口变换(nrange=0, frange=1):
zwindow=zndc+12=12(f+nf−n+2fnzeye(f−n))
z_{\text{window}} = \frac{z_{\text{ndc}}+1}{2} = \frac12\left(\frac{f+n}{f-n} + \frac{2fn}{z_{\text{eye}}(f-n)}\right)
zwindow=2zndc+1=21(f−nf+n+zeye(f−n)2fn)
因此
zwindow∝1zeye
z_{\text{window}} \propto \frac1{z_{\text{eye}}}
zwindow∝zeye1
当 f/n 很大,远处 zwindow 的离散值极少 → z-fighting(图 6.1(a))。
最小可分辨距离(Baker 近似,x-bit 定点深度缓冲):
Smin(zeye)≈zeye22xn−zeye
S_{\min}(z_{\text{eye}})\approx \frac{z_{\text{eye}}^2}{2^x n - z_{\text{eye}}}
Smin(zeye)≈2xn−zeyezeye2
越远 Smin 越大,越难区分两张紧挨的片元。
9.2 基本对策
-
把近平面推远(最有效)
- 每把 n 推远 1 m,效果 ≈ 把 f 拉近 500 m(当 n ≪ f)。
- 可用雾或淡出避免近裁剪突兀。
-
把远平面拉近
- 次优,但可配合雾/淡出隐藏远处物体。
-
动态调整 n/f
- 根据相机高度实时更新,保证 f/n ≤ 1000(经验值)。
-
剔除/淡出远处对象
- 既减少 f,又清理画面。
9.3 互补深度缓冲(Complementary Depth Buffer)
- 思路:翻转深度映射,使浮点精度最高的 0 值用在最远片元。
- 清除深度值为 0(而非 1);
- 深度比较改为 GL_GREATER;
- 构造透视矩阵时互换 n 与 f。
- 结果:远平面获得浮点最高精度,近平面精度下降,整体更均衡。
- 24 位定点缓冲也有一定收益。图 6.7 展示了对比效果。
9.4 对数深度缓冲(Logarithmic Depth Buffer)
在顶点/片元着色器中把 zclip 替换为
zclip′=2ln(C zclip+1)ln(C f+1)−1
z_{\text{clip}}' = 2\frac{\ln(C\,z_{\text{clip}}+1)}{\ln(C\,f+1)} - 1
zclip′=2ln(Cf+1)ln(Czclip+1)−1
- C 越小 → 远处精度越高,近处精度越低。
- 推荐在片元着色器写入
gl_FragDepth,避免顶点插值带来的误差。 - 图 6.8-6.9 展示了不同 C 的效果。
9.5 多视锥体渲染(Multifrustum Rendering / Depth Partitioning)
把超大视距切成 多个视锥体(depth partition),每个 f/n ≤ 1000:
| 视锥体 | near | far |
|---|---|---|
| 1 | 1 m | 1 000 m |
| 2 | 1 000 m | 1 000 000 m |
| 3 | 1 000 000 m | 100 000 000 m |
- 渲染顺序:先远后近,每段清空深度缓冲。
- 重叠少许(如 1%)防止裂缝。
- 优点:
- 兼容老显卡,理论视距无限。
- 极少需要雾/淡出。
- 缺点:
- 同一物体可能跨多个视锥体,需渲染多次 → CPU/带宽/片段开销增加。
- 半透明重叠区域会出现颜色带(图 6.11)。
- 对延迟渲染、early-z 优化不友好。
STK 实践:默认 1 m / 100 Mm,f/n = 1000,通常 3-4 个视锥体即可。
9.6 W-Buffer(已废弃)
- 原理:使用线性 wclip(即 zeye)作为深度值,分布均匀。
- 问题:w 在窗口空间非线性,插值昂贵,硬件不再支持。
9.7 方案对照表(表 6.2)
| 技术 | 要点 | 适用场景 |
|---|---|---|
| 调整近平面/远平面 | 简单、快速;推远 n 最有效 | 所有场景起点 |
| 剔除/淡出远处物体 | 降低 f,同时清理画面 | 高空视角、LOD |
| 互补深度缓冲 | 翻转深度映射,浮点精度最大化 | 支持浮点深度的硬件 |
| 对数深度缓冲 | 顶点/片元着色器改 z,远距精度高 | 单视锥体、长视距 |
| 多视锥体渲染 | 分区视距,兼容老显卡 | 虚拟地球、太空场景 |
| W-Buffer | 已淘汰,不再讨论 | — |
组合使用:
- 先推远 n、拉近 f;
- 若仍不足,再叠加对数深度或多视锥体;
- 互补深度缓冲可与多视锥体并用,减少分区数量。
914

被折叠的 条评论
为什么被折叠?



