深度测试
深度缓冲(Depth Buffer)来防止被阻挡的面渲染到其它面的前面
深度缓冲就像颜色缓冲,深度缓冲是由窗口系统自动创建的,它会以16、24或32位float的形式储存它的深度值。在大部分的系统中,深度缓冲的精度都是24位的。
当深度测试(Depth Testing)被启用的时候,OpenGL会将一个片段的深度值与深度缓冲的内容进行对比。OpenGL会执行一个深度测试,如果这个测试通过了的话,深度缓冲将会更新为新的深度值。如果深度测试失败了,片段将会被丢弃。
深度缓冲是在片段着色器运行之后,在屏幕空间中运行,gl_FragCoord的x和y分量代表了片段的屏幕空间坐标(其中(0, 0)位于左下角)
gl_FragCoord中也包含了一个z分量,它包含了片段真正的深度值。z值
就是需要与深度缓冲内容所对比的那个值。
现在大部分的GPU都提供一个叫做提前深度测试(Early Depth Testing)的硬件特性
深度测试默认是禁用的,所以如果要启用深度测试的话,我们需要用GL_DEPTH_TEST选项来启用它:
glEnable(GL_DEPTH_TEST);
如果你启用了深度缓冲,你还应该在每个渲染迭代之前使用GL_DEPTH_BUFFER_BIT来清除深度缓冲,否则你会仍在使用上一次渲染迭代中的写入的深度值:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
在某些情况下你会需要对所有片段都执行深度测试并丢弃相应的片段,但不希望更新深度缓冲。OpenGL允许我们禁用深度缓冲的写入,只需要设置它的深度掩码(Depth Mask)设置为GL_FALSE就可以了:
glDepthMask(GL_FALSE);
调用glDepthFunc函数来设置比较运算符(或者说深度函数(Depth Function)):
glDepthFunc(GL_LESS);
函数 | 描述 |
---|---|
GL_ALWAYS | 永远通过深度测试 |
GL_NEVER | 永远不通过深度测试 |
GL_LESS | 在片段深度值小于缓冲的深度值时通过测试 |
GL_EQUAL | 在片段深度值等于缓冲区的深度值时通过测试 |
GL_LEQUAL | 在片段深度值小于等于缓冲区的深度值时通过测试 |
GL_GREATER | 在片段深度值大于缓冲区的深度值时通过测试 |
GL_NOTEQUAL | 在片段深度值不等于缓冲区的深度值时通过测试 |
GL_GEQUAL | 在片段深度值大于等于缓冲区的深度值时通过测试 |
默认情况下使用的深度函数是 GL_LESS
,它将会丢弃深度值大于等于当前深度缓冲值的所有片段。
Fdepth=
在实践中是几乎永远不会使用这样的线性深度缓冲(Linear Depth Buffer), 它是与 1/z 成正比的。它做的就是在z值很小的时候提供非常高的精度,而在z值很远的时候提供更少的精度。
Fdepth=
image.png让片段非线性的深度值变换为线性
float depth = gl_FragCoord.z;
float z = depth * 2.0 - 1.0;
float linearDepth = (2.0 * near * far) / (far + near - z * (far - near));
测试代码
#version 330 core
out vec4 FragColor;
float near = 0.1;
float far = 100.0;
float LinearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0; // back to NDC
return (2.0 * near * far) / (far + near - z * (far - near));
}
void main()
{
float depth = LinearizeDepth(gl_FragCoord.z) / far; // 为了演示除以 far
FragColor = vec4(vec3(depth), 1.0);
}
Z-fighting: 深度缓冲没有足够的精度来决定两个形状哪个在前面。结果就是这两个形状不断地在切换前后顺序,这会导致很奇怪的花纹。这个现象叫做深度冲突(Z-fighting)
防止深度冲突:
- 远不要把多个物体摆得太靠近,以至于它们的一些三角形会重叠
- 尽可能将近平面设置远一些。
- 使用更高精度的深度缓冲。
FYI:https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/01%20Depth%20testing/