OpenGL图形图像渲染中的深度缓冲区

  • 什么是深度?

深度其实就是该像素点在3D世界中距离摄像机的距离,即Z值。

  • 什么是深度缓冲区?

深度缓存区,就是一块内存区域,一般由窗口管理系统GLFW创建,专门存储着每个像素点(绘制在屏幕上的)深度值.深度值(Z值)越大, 则离摄像机就越远.

  • 为什么需要深度缓冲区?

在不使⽤深度测试的时候,如果我们先绘制一个距离⽐较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉. 有了深度缓冲区后,绘制物体的顺序就不那么重要了. 实际上,只要存在深度缓冲区,OpenGL都会把像素的深度值写入到缓冲区中。除⾮调用glDepthMask(GL_FALSE)来禁止写入。

  • 深度测试

深度缓冲区(DepthBuffer)和颜色缓存区(ColorBuffer)是对应的.颜色缓存区存储像素的颜⾊色信息,而深度缓冲区存储像素的深度信息. 在决定是否绘制一个物体表面时, ⾸先要将表面对应的像素的深度值与当前深度缓冲区中的值进⾏比较. 如果大于深度缓冲区中的值,则丢弃这部分.否则利用这个像素对应的深度值和颜色值.分别更新深度缓冲区和颜色缓存区.这个过程称为”深度测试“。

简单理解: 从GPU渲染画面的角度来讲就是:从裁剪空间到标准设备空间之后的渲染流程,如果两个不同的片段具有相同的xy坐标值,那么我们可以根据这两个片段的z值选择性丢弃,因为z值更大的那个片段实际上被“遮挡”了,GPU则可以根据z值判断是否需要渲染,这就提高了效率,这在图形学中叫做z消隐。 而比较判断相同xy坐标的片段(经过顶点光栅化,插值贴图着色后片段)的z值的过程叫做深度测试,深度测试成功,则渲染新像素,失败则丢弃。

例如:当然如果我们涉及到透明效果的时候,比如眼前是一个玻璃橱柜,而玻璃橱柜有一个甜甜圈,那么我们还要涉及颜色混合的操作,也就是将玻璃的颜色和甜甜圈的颜色混合起来再更新渲染像素。

深度值计算

深度值一般由16位,24位或者32位值表示,通常是24位。位数越高的话,深度的精确度越高。深度值的范围在[0,1]之间,值越小表示越靠近观察者,值越大表示远离观察者。深度缓冲主要是通过计算深度值来比较⼤小,在深度缓冲区中包含深度值介于0.0和1.0之间,从观察者看到其内容与场景中的所有对象的z值进行了比较。这些视图空间中的z值可以在投影平头截体的近平面和远平⾯之间的任何值。我们因此需要一些⽅法来转换这些视图空间z值到[0,1]的范围内,下⾯的(线性)方程把z值转换为0.0和1.0之间的值:


深度值转换公式.png

其中far和near是提供到投影矩阵设置可⻅见视图截锥的远近值

开启深度测试

开启深度测试:

   glEnable(GL_DEPTH_TEST);

在绘制场景前,清除颜⾊缓存区和深度缓冲区,清除深度缓冲区默认值为1.0,表示最大的深度值。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

指定深度测试判断模式:

void glDepthFunc(GLEnum mode);
深度测试模式说明.png

开启深度测试前,z值小的会被z值大的覆盖掉,展示出来的带缺口.png

开启深度测试后,z值小的(离观察者近的)不会被z值大的覆盖掉,大于深度缓冲区的值被丢弃,展示出来的不带缺口.png

深度测试模式说明:

void glDepthMask(GLBool value);
value : GL_TURE 开启深度缓冲区写⼊入; GL_FALSE 关闭深度缓冲区写⼊

开启深度测试可能带来的问题

  • ZFighting闪烁问题

当开启深度测试后,OpenGL就不会再去绘制模型被遮挡的部分. 这样实现的显示更加真实.但是由于深度缓冲区精度的限制对于深度相差非常小的情况下.(例如在同⼀一平面上进行2次绘制),OpenGL 就可能出现不能正确判断两者的深度值,会导致深度测试的结果不可预测.显示图像时可能会有两个画面交错闪烁。

  • ZFighting闪烁问题的问题解决

让深度值之间产生间隔.如果2个图形之间有间隔,是不是意味着就不会产生⼲涉.可以理解为在执行深度测试前将⽴方体的深度值做一些细微的增加.于是就能将重叠的2个图形深度值之间有所区分

  1. 第⼀步: 启⽤用 Polygon Offset
//启⽤用Polygon Offset ⽅方式 
glEnable(GL_POLYGON_OFFSET_FILL)
参数列列表: 
1. GL_POLYGON_OFFSET_POINT  对应光栅化模式: GL_POINT
2. GL_POLYGON_OFFSET_LINE   对应光栅化模式: GL_LINE
3. GL_POLYGON_OFFSET_FILL 对应光栅化模式: GL_FILL
  1. 第⼆步: 指定偏移量量
void glPolygonOffset(Glfloat factor,Glfloat units);

glPolygonOffset 需要2个参数: factor , units.
每个Fragment 的深度值都会增加如下所示的偏移量量:
Offset = ( m * factor ) + ( r * units);
m : 多边形的深度的斜率的最大值,理解一个多边形越是与近裁剪面平行,m 就越接近于0.
r : 能产生于窗口坐标系的深度值中可分辨的差异最⼩小值.r 是由具体是由具体OpenGL 平台指定的⼀个常量.

一个大于0的Offset 会把模型推到离你(摄像机)更远的位置,相应的一个小于0的Offset 会把模型拉近;

一般⽽言,只需要将-1.0 和 -1 这样简单赋值给glPolygonOffset 基本可以满⾜需求.

  1. 第三步: 关闭Polygon Offset
glDisable(GL_POLYGON_OFFSET_FILL)
  • ZFighting闪烁问题预防

    • 不要将两个物体靠的太近,避免渲染时三角形叠在一起。这种⽅式要求对场景中物体插入一个少量的偏移,那么就可能避免ZFighting现象。
    • 尽可能将近裁剪面设置得离观察者远一些。上⾯面我们看到,在近裁剪平面附近,深度的精确度是很⾼的,因此尽可能让近裁剪面远⼀些的话,会使整个裁剪范围内的精确度变高一些。但是这种⽅式会使离观察者较近的物体被裁减掉,因此需要调试好裁剪面参数。
    • 使⽤更高位数的深度缓冲区,通常使用的深度缓冲区是24位的,现在有一些硬件是使用32位的缓冲区,使精确度得到提⾼。

你可能感兴趣的:(OpenGL图形图像渲染中的深度缓冲区)