对数深度缓冲

在超大型场景下,远近裁剪平面一半要很远的距离,使用普通的深度缓冲经常会因为误差,造成一些很难看的锯齿,本来不相交的图元相互穿透,Z-fighting等现象。

普通的Z/W深度缓冲,深度值不是线性的。在近处,深度比较精确,所使用的深度区间也比较大。在远处,仅会使用很小一部分深度区间,误差就会变得很大,产生难看的现象。

例如,在近裁剪平面为10,远裁剪平面为10000时:

Z Z/W深度
90.09009 0.900901
490.4905 0.980981
990.991 0.990991
10000 1.0

可见近处的所用到的区间大,90以内的就用了0-0.9,越远越小,精度越低。

 

一种办法就是将深度转化为线性的,在D3D中用Shader自己算一个深度。

由于Z最终会被除以W,所以,可以在VS中,完成WorldViewProj之后,先对Z上乘以W,等最后被除以W的时候,就相当于什么事情也没有发生。

当然,也要除以远裁剪平面距离。使深度值映射到0-1之间。也可以通过修改投影矩阵来完成。

线性的深度分布,会使远处和近处的精度一样。即传说中的W缓冲。在超大场景下,远处就没有那么多锯齿了。但是,这样有时候,近处的精度就会不足。

还有一种办法就是在VS中用对数计算深度。

公式z = log(C*z + 1) / log(C*Far + 1) * w

其中,C是常量,不同的值,会影响深度的精度。

在z处的分辨率为

log(C*Far + 1) /  (2^n * C/(C*z+1))

 

一下是一个参考的精度表,使用24位深度缓冲,远裁剪平面10000km

  1 10 100 1k 10k 100k 1M 10M
C=1 1.9e-6 1.1e-5 9.7e-5 0.001 0.01 0.096 0.96 9.6
C=0.001 0.0005 0.0005 0.0006 0.001 0.006 0.055 0.549 5.49

使用对数深度问题是深度是被线性插值的,而不是对数。所以网格必须足够精确……或者直接在PS中算深度,不是很费性能,如果可以接受的话……

这种办法效果非常好,做真实比例的星球系统没啥问题。

 

 

使用这些非标准的深度,虽然效果好,但也是有坏处的。

首先,普通Z/W深度,在屏幕空间是线性的,如图所示:

depth_grad

这是使用ddx和ddy对Z/W深度求的导数图。

由颜色相同可以看出,深度是线性的(在屏幕空间)。原始图片为无损png,可以用画图颜料桶测试颜色是否相同。

 

 

对线性深度使用相同办法,效果如图。

depth_grad_nonlinear

可见是非线性的。

这对于一些基于屏幕空间深度的Post Effect来说不利。例如SSAO,HDAO,景深等。

posted @ 2010-01-14 03:02  Yuri  阅读(1303)  评论(0编辑  收藏  举报