美文网首页
OpenGL学习15——深度测试

OpenGL学习15——深度测试

作者: 蓬篙人 | 来源:发表于2021-07-03 07:25 被阅读0次

深度测试(Depth Testing)

  • 深度缓冲区(depth-buffer) 是与颜色缓冲区类似的一种缓冲区,它按每个片元存储信息,并且与颜色缓冲区拥有一样的宽度和高度。深度缓冲区由窗体系统自动创建,并将深度值存储为16,24或32位的浮点值。大部分系统的深度缓冲区的精度为24位。

  • 当启动深度测试时,OpenGL将片元的深度值与深度缓冲区内容进行测试。如果测试通过,片元将被渲染且其深度值被更新到缓冲区,如果失败则丢弃该片元。

  • 深度测试是在片元着色器运行之后模板测试之前,在屏幕空间中进行。

  • GLSL内置一个gl_FragCoord变量,代表片元在屏幕的坐标,其中x和y分量为左下角为原点的屏幕坐标,z分量包含片元的深度值。

  • OpenGL中启动深度测试。

glEnable(GL_DEPTH_TEST);
  • 清除深度缓冲区。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  • OpenGL允许我们通过将深度掩码(mask)设置为GL_FALSE来禁用深度缓冲区写入。这只能在缓冲区启用的情况下设置才有意义。
glDepthMask(GL_FALSE);

1. 深度测试函数

  • OpenGL允许我们通过glDepthFunc函数修改深度测试的比较操作符。
glDepthFunc(GL_LESS);
  • 深度测试比较操作符选项列表。
函数 描述
GL_ALWAYS 深度测试总是通过
GL_NEVER 深度测试从不通过
GL_LESS 如果片元深度值小于存储的深度值则通过(默认
GL_EQUAL 如果片元深度值等于存储的深度值则通过
GL_LEQUAL 如果片元深度值小于或等于存储的深度值则通过
GL_GREATER 如果片元深度值大于存储的深度值则通过
GL_NOTEQUAL 如果片元深度值不等于存储的深度值则通过
GL_GEQUAL 如果片元深度值大于或等于存储的深度值则通过

下面我们创建一个场景来展示不同深度测试的效果。场景是一个平面上有两个立方体,平面和立方体分别设置不同的纹理。

  • 立方体的顶点数据,位置坐标和纹理坐标参考前面章节。平面的位置坐标和纹理坐标如下:
float planeVertices[] = {
    // positions          // texture 
     5.0f, -0.5f,  5.0f,  2.0f, 0.0f,
    -5.0f, -0.5f,  5.0f,  0.0f, 0.0f,
    -5.0f, -0.5f, -5.0f,  0.0f, 2.0f,

     5.0f, -0.5f,  5.0f,  2.0f, 0.0f,
    -5.0f, -0.5f, -5.0f,  0.0f, 2.0f,
     5.0f, -0.5f, -5.0f,  2.0f, 2.0f
};
  • 核心渲染代码如下(视矩阵和投影矩阵皆使用单位矩阵):
// cubes
glBindVertexArray(cubeVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, cubeTexture);
model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));
shader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));
shader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
// floor
glBindVertexArray(planeVAO);
glBindTexture(GL_TEXTURE_2D, floorTexture);
shader.setMat4("model", glm::mat4(1.0f));
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
  • GL_AWLAYS效果。
    GL_ALWAYS效果
  • GL_LESS效果。
    GL_LESS效果

2. 深度值精度

  • 因为视空间物体的z-值可以是投影锥体远近平面之间的任何值,因此我们需要将其转换为范围为[0, 1]之间的值。下面是其中一种转换方式的公式:
    F_{depth}=\frac{z - near}{far - near}
    其中的nearfar值就是我们提供给投影矩阵用于产生可见截锥体的远近平面值。

  • 实际应用中很少使用上述线性转换,因为投影属性的关系,一般使用一个非线性公式但与1/z成比例,结果就是z值很小时精度较高,z值很大时精度很小。公式如下所示:
    F_{depth}=\frac{1/z - 1/near}{1/far - 1/near}
    从上述公式可知,z值在1.0到2.0被映射到1.0到0.5,占用了[0, 1]一半,精度很高;但是z值在50.0和100.0之间则大概只占2%的[0, 1]范围。效果如下图所示:(图片取自书中

    深度值非线性转换图

3. 可视化深度缓冲区

  • 如果我们要将片元的深度值显示为颜色,我们可以通过内置的gl_FragCoord矢量进行颜色计算。
FragColor = vec4(vec3(gl_FragCoord.z), 1.0);
  • 深度值显示效果,大家可以感受移动物体过程中深度值颜色的非线性变化。


    非线性深度值颜色显示
  • 对于片元深度值,我也可以通过逆转换,来显示其原来的线性深度值。首先,我们需要将深度值从[0, 1]转换为标准设备坐标范围[-1, 1]。
float ndc = depth * 2.0 - 1.0;
  • 然后,我们逆转上述第二个公式,将非线性深度值转换为投影矩阵远近平面之间的值。
float linearDepth = (2.0 * near * far) / (far + near - ndc * (far - near));
  • 我们将转换过程封装成GLSL函数,最终片元着色器中的计算如下:
float near = 0.1;
float far = 100.0;

float LinearizeDepth(float depth)
{
    float z = depth * 2.0 - 1.0;
    return (2.0 * near * far) / (far + near - z * (far - near));
}

void main()
{    
    // 因为near和far之间深度值大部分大于1,会显示为白色,因此这里除以far,映射到[0, 1]
    float depth = LinearizeDepth(gl_FragCoord.z) / far;
    FragColor = vec4(vec3(depth), 1.0);
}
  • 线性深度值效果。


    线性深度值颜色显示

4. 深度冲突(Z-fighting)

  • 当两个平面或三角形靠的太近,而深度缓冲区没有足够的精度进行测试时会出现一种常见的视觉伪影——感觉两个图形一直在切换顺序从而导致一种奇怪的故障模式,这称为深度冲突(z-fighting)
  • 防止深度冲突的技巧:
      1. 从不将物体放置得太接近从而导致它们的三角形面片彼此重叠,如对物体位置做一定的偏移。
      1. 将近平面设置得尽可能的远,因为从上述非线性公式,我们知道越靠近近平面精度越大。
      1. 使用更高精度的深度缓冲区(降低性能)。

相关文章

  • OpenGL学习15——深度测试

    深度测试(Depth Testing) 深度缓冲区(depth-buffer) 是与颜色缓冲区类似的一种缓冲区,它...

  • OpenGL:背面剔除和深度测试

    OpenGL:背面剔除和深度测试

  • 六、OpenGL 渲染技巧:深度测试、多边形偏移、 混合

    OpenGL + OpenGL ES +Metal 系列文章汇总 深度测试 在上一篇五、OpenGL 渲染技巧:正...

  • OpenGL学习之路(3.0):OpenGL 深度测试

    [TOC] 学习目标 渲染过程中可能产生的问题 油画渲染 正面和背面剔除 深度测试 多边形模型 多边形偏移 裁剪 ...

  • OpenGL ES for Android(深度测试)

    简介 从这章开始学习高级OpenGL的内容。关于深度测试的理论知识,请参考https://learnopengl-...

  • 13. opengl高级-深度测试

    1. opengl深度测试原理,“测试”理解为“检测规则”更容易懂 关闭深度测试,后绘制的团会覆盖先绘制的纹理: ...

  • OpenGL 深度测试

    什么叫深度? 所谓深度,就在坐标系中,像素Z坐标距离观察者的距离.观察者,可以在任何位置.OpenGL 会有专门有...

  • OpenGL 深度测试

    正背面剔除遗留问题 进行了背面剔除之后,当旋转到侧边时,出现了缺口的现象. 当环旋转到侧边时观察者和环的关系如上图...

  • OpenGL深度测试

    一、概念 深度:深度其实就是该像素点在3D世界中距离摄像机的距离,Z值。 深度缓冲区:深度缓存区,就是一块内存区域...

  • OpenGL深度测试

    在上一篇OpenGL正背面剔除中提到了正背面剔除的弊端:如果前后两个点都是正面或是背面,这时OpenGL无法区分哪...

网友评论

      本文标题:OpenGL学习15——深度测试

      本文链接:https://www.haomeiwen.com/subject/jokuultx.html