1. 流程
在前面的教程中,我们有简略介绍过深度测试,它的存在帮助我们最终渲染的画面不会存在那些本应被遮挡住了的图像,其实若深究其中的原理的话,倒也没多复杂,就是每次渲染一个图形时测测当前这个要渲染的像素是否是目前离屏幕最近的,若是,就说明没有被其他物体遮挡,就可以把它的颜色映射到屏幕上,而这个距离就是由depth_buffer所存储,每次渲染一个新图形的时候我们就去比较和更新这个buffer。
1.1 深度测试
一般情况下,深度测试默认是禁用的,所以如果要启用深度测试的话,我们需要用GL_DEPTH_TEST选项来启用它:
glEnable(GL_DEPTH_TEST);
同时我们还应该在每个渲染迭代之前使用GL_DEPTH_BUFFER_BIT来清除深度缓冲,否则会仍在使用上一次渲染迭代中的写入的深度值:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
当我们要固定对某个深度距离进行设置时(比如深度大于某个固定的值时不渲染),可以让它每次只进行深度测试,不进行深度更新:
glDepthMask(GL_FALSE);
当然,默认情况下我们都是用当前的深度与buffer中的深度比较,若是当前的深度更大就不渲染,这符合我们的日常经验,那么如果要改变它的判断条件该怎么办呢?我们可以通过glDepthFunc函数进行更改:
glDepthFunc(GL_LESS);
它有以下几种参数:
1.2 深度值精度
深度缓存中存储的深度值是一个归一化的值,在0-1之间,线性的归一化由下式算出:
但是实际上,我们用于计算的z值是经过MVP变换的z值,已经在前面的透视投影矩阵中已经发生了非线性变换:
透视投影矩阵的推导
这就使得在近处的物体进行深度测试需要的精度更大!
我们就以另一种方式来近似这种非线性:
可以看到,深度值很大一部分是由很小的z值所决定的,这给了近处的物体很大的深度精度。这个(从观察者的视角)变换z值的方程是嵌入在投影矩阵中的,所以当我们想将一个顶点坐标从观察空间至裁剪空间的时候这个非线性方程就被应用了。
1.3 深度冲突
在使用各种建模软件时,如果我们将两个物体的面放的过于接近,由于上节提到的精度因素,它们可能会无法判断哪个物体更前,表面就会发生闪烁,远处的深度冲突还会更甚:
我们因为计算机的精度限制,没有办法彻底解决这种问题,只能尽量避免,下面给出几种方法:
第一个也是最重要的技巧是永远不要把多个物体摆得太靠近,以至于它们的一些三角形会重叠。
第二个技巧是尽可能将近平面设置远一些。在前面我们提到了精度在靠近近平面时是非常高的,所以如果我们将近平面远离观察者,我们将会对整个平截头体有着更大的精度。然而,将近平面设置太远将会导致近处的物体被裁剪掉,所以这通常需要实验和微调来决定最适合你的场景的近平面距离。
另外一个很好的技巧是牺牲一些性能,使用更高精度的深度缓冲。大部分深度缓冲的精度都是24位的,但现在大部分的显卡都支持32位的深度缓冲,这将会极大地提高精度。所以,牺牲掉一些性能,你就能获得更高精度的深度测试,减少深度冲突。