作者:奉灬孝
链接:https://www.jianshu.com/p/1c34ba2fba9b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在上一篇OpenGL正背面剔除中提到了正背面剔除的弊端:如果前后两个点都是正面或是背面,这时OpenGL无法区分哪个面在前,哪个面在后,就可能出现下图所示的问题。
为了解决这个问题,就引出了深度测试。在二维图形渲染时一般是用不到深度测试的。
一、什么是深度?
1. 定义
深度其实就是该像素点在3D世界中距离观察者的距离—Z值。
2.深度值计算
深度值⼀般由16位,24位或者32位值表示,通常是24位。位数越⾼高的话,深度的精确度越好。
深度缓冲主要是通过计算深度值来比较⼤小,在深度缓冲区中包含深度值介于 0.0 和 1.0 之间, 从观察者看到其内容与场景中的所有对象的 z 值进⾏了⽐较。这些视图空间中的 z 值可以在投影平头截体的近平面和远平面之间的任何值。我们因此需要一些方法来转换这些视图空间 z 值到 [0,1] 的范围内,下面的 (线性) 方程把 z 值转换为 0.0 和 1.0 之间的值 :
3. Z值与观察者的关系
观察者可以放在坐标系的任意位置。所以,不能简单的说Z值越大或越小,观察者就越靠近物体。
- 如果观察者在Z轴的正方向,Z值越大则越靠近观察者;
- 如果观察者在Z轴的负方向,Z值越小则越靠近观察者
二、什么是深度缓冲区?
1. 定义
深度缓存区,就是⼀块内存区域,专⻔门存储着每个像素点(绘制在屏幕上的)深度值。
2. 深度缓冲区在哪里?
深度缓冲区存储在显存中。
3. 深度缓冲区原理
深度缓冲区就是把距离观察者平面(近裁剪面)的深度值与窗口中的每个像素点1对1进行关联和存储。分辨率为 120 * 120 图片则存储 120 * 120 个深度值。
三、深度测试
深度缓冲区(DepthBuffer)和颜⾊色缓存区(ColorBuffer)是对应的。颜⾊缓存区存储像素的颜⾊信息,⽽深度缓冲区存储像素的深度信息.。在决定是否绘制一个物体表面时,首先要将要绘制像素的深度值(新值)与当前深度缓冲区的深度值(旧值)进行比较。如果 新值
> 旧值
,则丢弃这部分不进行绘制。否则,将这个像素对应的深度值和颜⾊色值—新值
,更新到深度缓冲区和颜色缓存区。这个过程称为深度测试。
深度测试主要函数
- 开启深度测试
glEnable(GL_DEPTH_TEST);
在绘制场景前,清除颜⾊缓存区,深度缓冲。清除深度缓冲区默认值为1.0。
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- 指定深度测试判断模式
void glDepthFunc(GLEnum mode);
函数 | 说明 |
---|---|
GL_ALWAYS | 总是通过测试 |
GL_NEVER | 总是不通过测试 |
GL_LESS | 在当前深度值 < 存储的深度值时通过 |
GL_GREATER | 在当前深度值 > 存储的深度值时通过 |
GL_EQUAL | 在当前深度值 == 存储的深度值时通过 |
GL_LEQUAL | 在当前深度值 <= 存储的深度值时通过 |
GL_GEQUAL | 在当前深度值 >= 存储的深度值时通过 |
GL_NOTEQUAL | 在当前深度值 != 存储的深度值时通过 |
- 深度缓冲区写入开关
//value: GL_TURE,开启深度缓冲区写入; GL_FALSE,关闭深度缓冲区写⼊
void glDepthMask(GLBool value);
四、正背面剔除问题解决
到这里上图中正背面剔除弊端的问题就迎刃而解了,开启深度测试将新旧深度值的对比,决定像素点是绘制还是丢弃,就不会出现分不清正背面的问题了。
设置深度测试开关
开启和关闭的逻辑,主要取决于右键菜单的点击次数,第一次打开,第二次关闭,以此类推
//根据设置iDepth标记来判断是否开启深度测试
if(iDepth)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
五、深度测试ZFighting闪烁问题
开启深度缓冲区后,由于深度值精度的限制,对于相差非常小的深度值(比如在同一个深度进行2次渲染),就可能出现不能正确区分两个深度值的问题,导致测试的结果随机出现。所以,显示时2个画⾯交错出现,就会出现闪烁问题。如下图所示:
1. 解决方案 — 多边形偏移(Polygon Offset)
其问题产生的主要原因是由于图形靠的太近,导致无法区分出图层先后次序,针对该问题,OpenGL提供了一种多边形偏移(Polygon Offset)
方案
- 启用
多边形偏移(Polygon Offset)
glEnable(GL_POLYGON_OFFSET_FILL)
多边形偏移枚举值 | 对应的光栅化模式 |
---|---|
GL_POLYGON_OFFSET_FILL | GL_FILL |
GL_POLYGON_OFFSET_LINE | GL_LINE |
GL_POLYGON_OFFSET_POINT | GL_POINT |
- 指定偏移量 (参数一般填 -1 和 -1)
void glPolygonOffset(Glfloat factor, Glfloat units);
- 关闭
多边形偏移(Polygon Offset)
// 参数和开启的参数相同
glDisable(GL_POLYGON_OFFSET_FILL);
2. 问题预防
- 不要将两个物体靠的太近。
避免渲染时三⻆形叠在一起。 - 尽可能将近裁剪面设置得离观察者远一些。
如果观察者离近裁剪平⾯很近,那么深度测试对精确度要求很⾼。因此,可以适当推远近裁剪平⾯的位置来避免这个问题,但是可能导致离观察者较近的物体被裁减掉,使用时需要小心。 - 使用更高位数的深度缓冲区。
默认的深度缓冲区精度是24位的,如果硬件支持32位的缓冲区,就可以提供更高的精度。