什么是深度?
深度其实就是该像素点在3D世界中距离摄像机的距离,Z值
什么是深度缓冲区?
深度缓冲区就是一块内存区域,专门存储着每个像素点(绘制在屏幕上的)深度值,深度值越大,则离摄像机就越远。
为什么需要深度缓冲区?
在不使⽤深度测试的时候,如果我们先绘制⼀个距离⽐较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉。有了深度缓冲区后,绘制物体的顺序就不那么重要了。 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中,除⾮调用glDepthMask(GL_FALSE)来禁⽌写⼊。
什么是深度测试?
深度缓冲区(DepthBuffer)和颜⾊缓存区(ColorBuffer)是对应的,颜⾊缓存区存储像素的颜⾊信息,⽽深度缓冲区存储像素的深度信息。在决定是否绘制一个物体表面时,首先要将表面对应的像素的深度值与当前深度缓冲区中的值进行⽐较,如果大于深度缓冲区中的值,则丢弃这部分,否则利用这个像素对应的深度值和颜色值,分别更新深度缓冲区和颜⾊缓存区。这个过程称为”深度测试”。
深度值计算
- 深度值⼀般由16位,24位或者32位值表示,通常是24位。位数越⾼高的话,深度的精确度越好。深度值的范围在[0,1]之间,值越小表示越靠近观察者,值越⼤表示越远离观察者。深度缓冲主要是通过计算深度值来⽐较⼤小,在深度缓冲区中包含深度值介于0.0和1.0之间,从观察者看到其内容与场景中的所有对象的 z 值进⾏了⽐较。这些视图空间中的 z 值可以是投影平头截体的近平面和远平面之间的任何值。我们因此需要一些⽅法来转换这些视图空间 z 值 到 [0,1] 的范围内,下面的 (线性) ⽅程把 z 值转换为 0.0 和 1.0 之间的值 :
far和near是提供到投影矩阵设置好可见视图截锥的远近值
使用深度测试
- 深度缓冲区,一般由窗口管理系统GLFW创建,深度值一般由16位,24位和32位值表示,通常是24位,位数越高,深度精确度更好。
- 开启深度测试
glEnable(GL_DEPTH_TEST);
- 在绘制场景前,清除颜色缓冲区和深度缓冲区
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
指定深度测试判断式
void glDepthFunc(GLEnum mode); //正常使用GL_LESS/GL_LEQUAL
打开/阻断深度缓冲区写入
void glDepthMask(BLBool value);
value: GL_TRUE 开启深度缓冲区写入;GL_FALSE 关闭深度缓冲区写入
ZFighting闪烁问题
原因
因为开启深度测试后,OpenGL 就不会再去绘制模型被遮挡的部分,这样实现的显示更加真实,但是由于深度缓冲区精度的限制对于深度相差⾮常小的情况下(例如在同一平面上进⾏行2次绘制),OpenGL就可能出现不能正确判断两者的深度值,会导致深度测试的结果不可预测,显示出来的现象是交错闪烁,前面2个画⾯交错出现。
解决方法
- 启用Polygon Offset方式解决 让深度值之间产⽣间隔,如果2个图形之间有间隔,是不是意味着就不会产⽣干涉,可以理解为在执行深度测试前将⽴⽅体的深度值做⼀些细微的增加,于是就能将重叠的2个图形深度值有所区分。
//启用Polygon Offset方式
glEnable(GL_POLYGON_OFFSET_FILL)
参数列表:
GL_POLYGON_OFFSET_POINT 对应光栅化模式:GL_POINT
GL_POLYGON_OFFSET_LINE 对应光栅化模式:GL_LINE
GL_POLYGON_OFFSET_FILL 对应光栅化模式:GL_FILL
- 指定偏移量
- 通过glPolygonOffset来指定,glPolygonOffset需要2个参数: factor,units
- 每个Fragment的深度值都会增加如下所示的偏移量: Offset = ( m * factor ) + ( r * units); m : 多边形的深度的斜率的最⼤值,理解一个多边形越是与近裁剪面平行,m就越接近于0。 r : 能产生于窗⼝坐标系的深度值中可分辨的差异最小值。r 是由具体OpenGL平台指定的⼀个常量。
- 一个⼤于0的Offset 会把模型推到离你(摄像机)更远的位置,相应的⼀个小于0的Offset会把模型拉近。
- 一般而言,只需要将-1.0 和 -1 这样简单赋值给glPolygonOffset基本可以满足需求。
void glPolygonOffset(GLfloat factor, GLfloat units);
应用到片段上总偏移计算方程式:
Depth Offset = (DZ *factor) + (r * units);
DZ: 深度值(Z值)
r: 使得深度缓冲区产生变化的最小值
负值,将使得z值距离我们更近,而正值将使得z值距离我们更远。
- 关闭Polygon Offset
glDisable(GL_POLYGON_OFFSET_FILL);
预防
- 不要将两个物体靠的太近,避免渲染时三⻆形叠在一起。这种方式要求对场景中物体插⼊一个少量的偏移,那么就可能避免ZFighting现象。例如上面的⽴⽅体和平⾯问题中,将平⾯下移0.001f就可以解决这个问题。当然⼿动去插⼊入这个⼩小的偏移是要付出代价的。
- 尽可能将近裁剪面设置得离观察者远一些。上面我们看到,在近裁剪平面附近,深度的精确度是很高的,因此尽可能让近裁剪面远⼀些的话,会使整个裁剪范围内的精确度变高一些。但是这种方式会使离观察者较近的物体被裁减掉,因此需要调试好裁剪面参数。
- 使用更高位数的深度缓冲区,通常使用的深度缓冲区是24位的,现在有一些硬件使用32位的缓冲区,使精确度得到提高。