最近在shadow map进程中卡住了。由于我想在OpenGL ES 2.0兼容的平台上运行shadow map,而要顺利运行shadow map,通常的情况是具有GL_OES_depth_texture扩大,而有些兼容的机器是没有这个扩大,这给我们开发人员带来了1些难度。不过办法还是有的,条件是要对计算机图形学的Z-Buffer要有1个深入的了解才行。
蒋彩阳原创文章,首发地址:http://blog.csdn.net/gamesdev/article/details/44939923。欢迎同行前来探讨。
参考维基百科上对Z-Buffer的介绍(这里第1个公式极可能是毛病的,感觉是2far・near而不是⑵far・near),当场景中的顶点在做MVP变换的时候,它的Z值也会遭到相应的变化。这里我们特别关注的是在视角空间下的顶点位置(Xe, Ye, Ze),在经过投影矩阵的变换时候会产生甚么变化。
首先是根据投影矩阵,对顶点进行投影变换:
然后将齐次坐标转换成为普通坐标,即同除以-Ze进行规格化,让第4个份量为1:
这个时候Zndc=了。
在NDC(Normalized Device Coordinates)坐标系转化为屏幕坐标系的进程中,Z值也要被线性插值。此时的Zndc∈[⑴,1]的,首先需要插值到[0,1]中,然后根据深度的位数(1般是16、24和32位,几近不存在8位的深度了),来平均映照过去。
这时候有:Zw=S・[(Zndc/2)+0.5]
其中S=2n⑴,n是系统设定的深度位数。
即
以Ze作为Zw的函数,并化简,则为:
我们可以大致地看到,Ze和Zw是成反比的(和1/Zw成正比)。为了详细了解相干情况,我们对它求导数:
令
则
计算后得到
我们可以很快看出,当
时,获得最小值。这也就说明无穷多的相机空间的距离只影响几近于0的Z缓存,换句话说,1个位的Z缓存变化就要反应无穷长的相机空间距离变化。
那末这个最小值点能否取到呢?由于计算机的Z缓存的深度变化是Zw∈[0,2n⑴],那末1定满足
这里左侧的不等式满足的充分必要条件是near
这要求near和far异号,但是我们通常是不会设置near和far异号的。所以右侧的不等式不满足。因此
在Zw∈[0,2n⑴]上是单调递增的,而Ze在Zw∈[0,2n⑴]上是单调递减的。所以这反应出,Z缓存中将更多的精度(2进制位数)给了距离摄像机(或zNear)较近的物体,而远处(或zFar较近)的物体,留给的Z缓存精度很少。所以Z缓存与zNear于zFar的远近精度关系其实不是线性的。这要求我们将更多的物体放在近平面处,远处的物体,可使用LOD手法将其疏忽显示,避免出现z-fighting。同时由于这样的特性,shadow map在不同远近出也会显现分辨率不1致的情况。这就要求我们使用Cascade Shadow Map(CSM)这样的技术来解决问题了。
参考文献:
OpenGL FAQ:https://www.opengl.org/archives/resources/faq/technical/depthbuffer.htm
深入探索透视矩阵:http://blog.csdn.net/popy007/article/details/1797121
Z-Buffering on Wikipedia:http://en.wikipedia.org/wiki/Z-buffering#Mathematics
疑问:
OpenGL FAQ里面的
d(ze / we) / d zw = - f * (f-n) * (1/s) / n
= -f * (f/n⑴) / s
多是毛病的,应当加上1对括号。成为
= -f * (f/(n⑴)) / s