基础知识
1.depth-buffer
深度缓冲区是一个缓冲区,就像颜色缓冲区(存储所有片段颜色:视觉输出)一样,存储每个片段的信息,并且具有与颜色缓冲区相同的宽度和高度。深度缓冲区由窗口系统自动创建,并将其深度值存储为 16、24 或 32 位浮点数。在大多数系统中,您会看到精度为 24 位的深度缓冲区。
启用深度测试后,OpenGL 会根据深度缓冲区的内容测试片段的深度值。 OpenGL 执行深度测试,如果该测试通过(此深度未被其他深度覆盖),则渲染片段并使用新的深度值更新深度缓冲区(覆盖别人)。如果深度测试失败,直接不渲染这个片段了。
今天,大多数 GPU 都支持一种称为早期深度测试的硬件功能。 早期深度测试允许在片段着色器运行之前运行深度测试。 每当很明显一个片段将不可见(它在其他对象后面)时,我们可以早点地丢弃该片段。
片段着色器通常非常昂贵,所以我们应该尽量避免运行它们。 用于早期深度测试的片段着色器的一个限制是您不应给片段设置深度值。 如果片段着色器将写入其深度值,则不可能进行早期深度测试; OpenGL 将无法事先计算出深度值。
开启深度测试:次片段测试通过才显示
glEnable(GL_DEPTH_TEST);
如果您启用了深度测试,您还应该在每帧之前使用 GL_DEPTH_BUFFER_BIT 清除深度缓冲区; 否则你会被最后一帧的深度值困住:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
某些时刻,你只想获取深度信息,不像更新写入他们,此时应该禁用深度信息的写入
glDepthMask(GL_FALSE);
1.Depth test function (深度测试函数)
我们可以通过调用 glDepthFunc 来设置比较运算符(或深度函数):
glDepthFunc(GL_LESS);
Function | Description |
---|---|
GL_ALWAYS | The depth test always passes.(这个fragment总是显示) |
GL_NEVER | The depth test never passes.(这个fragment总是被跳过不显示) |
GL_LESS | Passes if the fragment's depth value is less than the stored depth value.(比当前存在的其他人的depth小,则覆盖他们,正常一般都用这个) |
GL_EQUAL | Passes if the fragment's depth value is equal to the stored depth value.(和当前存在的其他人的depth相等,则覆盖他们) |
GL_LEQUAL | Passes if the fragment's depth value is less than or equal to the stored depth value.(<=) |
GL_GREATER | Passes if the fragment's depth value is greater than the stored depth value.( >) |
GL_NOTEQUAL | Passes if the fragment's depth value is not equal to the stored depth value.(!=) |
GL_GEQUAL | Passes if the fragment's depth value is greater than or equal to the stored depth value (>=) |
GL_ALWAYS 总是通过,则后来的fragment总是覆盖前面的
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
用GL_LESS,depth小的放前面,就正常了
2.Depth value precision (深度值精度)
深度缓冲区包含介于 0.0 和 1.0 之间的深度值。我们需要某种方法将这些视图空间 z 值转换到 [0,1] 的范围内, 以下(线性)方程将 z 值转换为介于 0.0 和 1.0 之间的深度值:
near and far values we used to provide to the projection matrix to set the visible frustum (视锥体的近值near和远值far).
The equation takes a depth value z within the frustum and transforms it to the range [0,1]
. (在视锥体中取得的深度z)
实践中的方法:
然而在实践中,几乎不用这种线性深度缓冲区。 由于投影特性,使用了与 1/z 成比例的非线性深度方程。 当 z 很小时我们要得到极大的精度,而当 z 很远时我们得到的精度要求就低一些。
3.Visualizing the depth buffer (可视化-深度缓冲区)
让越近的越黑(#000000黑色),越远的越白(#ffffff白色),和前文的曲线拟合
fs代码:
#version 330 core
out vec4 FragColor;
float near = 0.1;
float far = 100.0;
//根据深度,来获取颜色值,把depth信息变换成0-1范围内的信息
float LinearizeDepth(float depth)
{
//将0-1的信息,投影平铺至-1到1的坐标系中
float z = depth * 2.0 - 1.0; // back to NDC Normalize Device Space 标准化设备空间,范围只有0到1,中央为原点
//曲线化
return (2.0 * near * far) / (far + near - z * (far - near));
}
void main()
{
//获取深度,以及颜色值信息
float depth = LinearizeDepth(gl_FragCoord.z) / far; // divide by far to get depth in range [0,1] for visualization purposes
FragColor = vec4(vec3(depth), 1.0);
}
4.Z-fighting (Z值打架)
z靠得太近,而缓冲区精度不够的时候,就会出这种问题: