深度测试

深度测试用于解决像素的遮挡问题,即:哪个像素应该被显示在屏vanishing.cc20040422
幕上.

深度测试原理如下:

  • 用一张纹理来存储信息
  • 将每个片段与深度缓冲中的值信息进行深度测试,如果测试通过,则更新深度缓冲中的值信息,否则丢弃该片段.

启用深度测试

在OpenGL中,使用以下代码来启用深度测试:

glEnable(GL_DEPTH_TEST);

为了防止渲染帧使用上一帧的深度缓冲,你应该在每个渲染迭代之前清除深度缓冲:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

禁用写入深度缓冲

有时你可能需要禁止写入深度缓冲,例如,当你只需要渲染一个透明物体时,你可能不希望它影响深度测试.

在OpenGL中,使用以下代码来禁止写入深度缓冲:

glDepthMask(GL_FALSE);

深度测试函数

深度测试函数用于控制OpenGL如何判断是否通过深度测试.

有以下几种深度测试函数:

  • GL_ALWAYS: 总是通过测试
  • GL_NEVER: 总是丢弃片段
  • GL_LESS: 如果片段的深度值小于深度缓冲中的值,则通过测试
  • GL_EQUAL: 如果片段的深度值等于深度缓冲中的值,则通过测试
  • GL_LEQUAL: 如果片段的深度值小于或等于深度缓冲中的值,则通过测试
  • GL_GREATER: 如果片段的深度值大于深度缓冲中的值,则通过测试
  • GL_NOTEQUAL: 如果片段的深度值不等于深度缓冲中的值,则通过测试
  • GL_GEQUAL: 如果片段的深度值大于或等于深度缓冲中的值,则通过测试

使用此函数来设置深度测试函数:

glDepthFunc(GL_LESS);

深度映射

我们知道深度值是[0,1]的范围.

因此深度值和z值的关系可以使用以下公式:

线性映射

问题在于: 对于远处的物体,我们对精度的要求没有近处的高.

所以实践中都是使用非线性的方程:

非线性映射

这个方程和1/z成正比.

看看你的深度缓冲

如果我们直接将深度缓冲的值作为颜色输出,我们就可以直观看到场景的深度值.

void main() {
gl_FragColor = vec4(gl_FragCoord.z, 0.0, 0.0, 1.0);
}

gl_FragCoord 是OpenGL提供的内置变量,它包含了片段的位置信息.

深度冲突

当深度值精度不够时,就会出现深度冲突.
比如一幅画贴在墙上,好巧不巧,墙离摄像机距离又很远(精度更低).
就很容易出现墙和画交叠闪烁的画面.

深度冲突是一个系统性误差,换言之无法解决只能尽量避免.
有这些手段:

  • 使用更高精度的深度缓冲
  • 将近平面尽可能设置远
  • 不要把物体摆太近