1. 深度测试

深度测试的目的是防止被其他面遮挡的面显示出来。开启深度测试后,会对新的片段进行测试,测试通过的片段放进深度缓冲区,不通过的则被丢弃。

1.1 深度

  • 表示的是像素在Z轴上距离观察者的距离
  • 取值范围是0~1,默认值是1,通常精度是24位,也可以设置为16位或者32位,位数越大,精度越高,越可以避免 Z-Fighting , 同时消耗资源越多。
  • 一般情况下,我们作为观察者,看手机屏幕时,是处于Z轴的负半轴。从这个角度上看,深度值(Z值)越小,离观察者越近
  • 屏幕深度值具有非线性特性(在投影矩阵应用之前是线性的),即深度值 = 0.5 不代表物体 z 值在投影平截头体的中间。

1.2 深度缓冲区

是一块内存区域,存储每一个像素点的深度值,一个像素只有一个深度。

1.3 深度测试

  • 深度缓冲区与颜色缓冲区是对应的,一个存储像素的深度值,一个存储像素的颜色值。
  • 每当有新的图形图像传入时,先判断它的深度值,如果大于该像素在深度缓存区的深度值则丢弃。反之将新的深度值和颜色值,更新到对应的深度缓冲区及颜色缓冲区。(因为一般情况深度值越小的离观察者越近,这个规则也可以更改)。这个过程,称为深度测试。
  1. // 在绘制场景前,清除颜⾊缓存区,深度缓冲
  2. glClearColor(0.0f,0.0f,0.0f,1.0f);
  3. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  4. // 开启深度测试
  5. glEnable(GL_DEPTH_TEST);
  6. // 关闭深度测试
  7. glDisable(GL_DEPTH_TEST);
  8. // 打开/关闭深度缓冲区写入 ,GL_FALSE: 关闭写入 GL_TRUE:打开写入
  9. glDepthMask(GLBool value);
  10. // 指定深度测试判断模式,默认GS_LESS,当前深度值 < 存储深度值时通过
  11. glDepthFunc(GLEnum mode);

九、OpenGL渲染技巧 - 深度测试&多边形偏移&裁剪&混合 - 图1

1.4 甜甜圈开启深度测试

上篇博客中的甜甜圈案例

  1. // 开启深度测试
  2. glEnable(GL_DEPTH_TEST);
  3. //5.绘制
  4. torusBatch.Draw();
  5. // 关闭深度测试
  6. glDisable(GL_DEPTH_TEST);

九、OpenGL渲染技巧 - 深度测试&多边形偏移&裁剪&混合 - 图2

现在甜甜圈的展示,已经非常好了!
其实开启了深度测试后,我们甚至不需要使用背面剔除功能。因为深度测试的原理是一个像素内只绘制离观察者最近的图形图像。甜甜圈的背面离观察者远,自然不会绘制。

1.5 深度测试存在 Z-Fighting / Z闪烁 / Z冲突 的问题

  • 如果AB两个深度值特别相近,已经超出了深度值的精度位数,就无法比较两个值的大小,会出现可能一会儿显示A的图像,一会儿显示B的图像,造成一种闪烁的现象,这个现象叫做Z-Fighting 或者 Z闪烁。
  • 我们通过一个金字塔的案例,来观察Z冲突现象。先画一个金字塔,默认使用多边形填充模式。然后再为三角形画上黑色的边。这是在边的位置上,由于既有黑边也有颜色填充,且两者的深度值一样。变会产生下图中的闪烁现象。一会儿可以从底部看到黑色边线,一会儿看不到。
  1. // 使用多边形填充模式
  2. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  3. // 设置平面着色器,颜色为绿色
  4. shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
  5. // 绘制
  6. pBatch->Draw();
  7. // 使用多边形线框模式
  8. glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  9. //设置线条宽度
  10. glLineWidth(2.5f);
  11. // 设置平面着色器,颜色为黑色
  12. shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
  13. // 绘图
  14. pBatch->Draw();
  15. // 复原原本的设置
  16. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

九、OpenGL渲染技巧 - 深度测试&多边形偏移&裁剪&混合 - 图3

1.6 Z-Fighting / Z闪烁 解决方案

  • 多边形偏移。如果两个相差的很小,可以人为的设置一个偏移值,使得两个值变大,变大后符合精度位数限制,就可以正确的比较大小了。
  1. // 1. 开启多边形偏移
  2. glEnable(GL_POLYGON_OFFSET_FILL)
  3. // 2. 关闭多边形偏移
  4. glDisable(GL_POLYGON_OFFSET_FILL)
  5. 参数列列表:
  6. GL_POLYGON_OFFSET_POINT 对应光栅化模式: GL_POINT
  7. GL_POLYGON_OFFSET_LINE 对应光栅化模式: GL_LINE
  8. GL_POLYGON_OFFSET_FILL 对应光栅化模式: GL_FILL
  9. // 2. 设定偏移量(一般情况下,设置-1, -1)
  10. glPolygonOffset(Glfloat factor,Glfloat units)

上面金字塔案例中,增加如下修改:

  1. // 设置偏移值
  2. glPolygonOffset(-1.0f, -1.0f);
  3. // 开启多边形偏移
  4. glEnable(GL_POLYGON_OFFSET_LINE);
  5. ...
  6. // 绘制后,关闭多边形偏移
  7. glDisable(GL_POLYGON_OFFSET_LINE);

增加偏移值后的效果如下:
九、OpenGL渲染技巧 - 深度测试&多边形偏移&裁剪&混合 - 图4

1.7 Z-Fighting / Z闪烁 预防方案

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

2. 裁剪测试

  • 裁剪测试是OpenGL提高渲染性能的一种方式。只刷新屏幕上变化的部分。
  • 原理是设置一个裁剪区域,只渲染裁剪区域内的图形图像。也就是只有在范围内的片元,才会到达帧缓冲区,超出范围的将被丢弃。
  1. //1 开启裁剪测试
  2. glEnable(GL_SCISSOR_TEST);
  3. //2.关闭裁剪测试
  4. glDisable(GL_SCISSOR_TEST);
  5. //3.指定裁剪窗⼝, x,y:指定裁剪框左下角位置; width , height:指定裁剪尺⼨
  6. void glScissor(Glint x,Glint y,GLSize width,GLSize height);

3. 混合

  • 在像素缓冲区中,每一个像素有一个对应的颜色值。
  • 如果未开启深度测试,新的颜色值将会改变颜色缓冲区的颜色值;如果开启了,新的颜色值离观察者更近的才会改变。
  • 当新的颜色透明度为 1 时,新的颜色值需要改变颜色缓冲区的颜色值时,会直接覆盖。 小于 1 时,两种颜色需要混合显示。
  1. // 启用颜色混合
  2. glEnable(GL_BlEND);
  3. // 关闭颜色混合
  4. glDisable(GL_BlEND);

3.1 混合方程式

Cf = (Cs S) + (Cd D) Cf :最终计算参数的颜⾊ Cs : 源颜⾊ Cd :⽬标颜色 S:源混合因⼦子 D:⽬标混合因⼦

  1. // 选择混合方程式
  2. void glbBlendEquation(GLenum mode);
  3. // 设置混合因⼦, S:源混合因子 D:⽬标混合因⼦
  4. void glBlendFunc(GLenum S,GLenum D);
  5. // 常见的混合因子
  6. void glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
  7. // 设置混合因⼦及Alpha因⼦
  8. // strRGB: 源颜⾊的混合因子
  9. // dstRGB: ⽬标颜色的混合因子
  10. // strAlpha: 源颜⾊的Alpha因子
  11. // dstAlpha: ⽬标颜色的Alpha因⼦
  12. void glBlendFuncSeparate(GLenum strRGB,GLenum dstRGB ,GLenum strAlpha,GLenum dstAlpha);
  13. // 设置常量混合颜色,默认黑色
  14. void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclampf alpha );

九、OpenGL渲染技巧 - 深度测试&多边形偏移&裁剪&混合 - 图5

九、OpenGL渲染技巧 - 深度测试&多边形偏移&裁剪&混合 - 图6

  • 表中R、G、B、A 分别代表 红、绿、蓝、alpha。
  • 表中下标S、D,分别代表S:源混合因子 D:⽬标混合因⼦
  • 表中C 代表常量颜色(默认⿊色)