【安卓OpenGLES】 开发入门(五):深度测试

4,149 阅读4分钟

本文正在参加「金石计划」

什么是深度测试?

前面我们在讲解屏幕坐标的时候,使用的是3D坐标,包括:x,y,z三个轴,其中x和y表示屏幕的x和y坐标,z坐标是当前片段Fragment离屏幕的距离,由于我们平时使用的都是右手坐标系:则z值越大,片段越靠近屏幕,z轴越小,片段离屏幕的距离就越远,而这里的远和近在OpenGL的概念里面就是深度的意思。

右手系

这里需要注意的一点就是,深度范围是在[0,1]之间的,深度值越大,则说明当前片段离屏幕距离越远,而此时z轴的值就越小。

深度测试是指在片段着色器运行之后在屏幕空间中执行的操作:意在对当前片段的深度值和深度缓冲区的值进行比较,然后决定是否丢弃的过程

什么是深度缓冲区?

深度缓冲就像颜色缓冲区一样用来存储每个片段的信息,只是其是用来存储当前片段的深度信息,深度可以使用16,24,32位为精度,大多数系统选择折中的24位。

当开启深度缓冲之后,OpenGL会自动测试当前片段的深度值,然后根据对应的策略来决定片段的丢弃。

如何实现深度测试?

要启用深度测试,我们需要用GL_DEPTH_TEST选项来打开它:

glEnable(GL_DEPTH_TEST);

一旦启用深度测试,如果片段通过深度测试,OpenGL自动在深度缓冲区存储片段的 z 值,如果深度测试失败,那么相应地丢弃该片段。如果启用深度测试,那么在每个渲染之前还应使用GL_DEPTH_BUFFER_BIT清除深度缓冲区,否则深度缓冲区将保留上一次进行深度测试时所写的深度值

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

当然在某些特殊情况下,我们在开启深度测试之后,需要丢弃片段,但是又不去更新缓冲区,可使用函数glDepthMask设置为GL_FALSE禁用深度缓冲区的写入。

glDepthMask(GL_FALSE);

上面只是开启深度测试的几个方式,默认情况下开启深度测试后,在深度测试过程中,会自动丢弃深度值越小的片段。那可不可以自己定义深度测试的策略呢?

我们可以通过调用glDepthFunc来设置比较运算符 (或叫做深度函数(depth function)):

glDepthFunc(GL_LESS);

该函数接受在下表中列出的几个比较运算符:

运算符描述
GL_ALWAYS永远通过测试
GL_NEVER永远不通过测试
GL_LESS在片段深度值小于缓冲区的深度时通过测试
GL_EQUAL在片段深度值等于缓冲区的深度时通过测试
GL_LEQUAL在片段深度值小于等于缓冲区的深度时通过测试
GL_GREATER在片段深度值大于缓冲区的深度时通过测试
GL_NOTEQUAL在片段深度值不等于缓冲区的深度时通过测试
GL_GEQUAL在片段深度值大于等于缓冲区的深度时通过测试

默认情况下使用GL_LESS,这将丢弃深度值高于或等于当前深度缓冲区的值的片段。

下面我们来比较下,不同深度测试策略下的图像比较:

  • 默认情况下:GL_LESS

    image-20230407112745190

  • GL_ALWAYS

    image-20230407112858944

  • GL_NEVER

    image-20230407113007475

可以看到

在GL_NEVER模式下:显示的是黑屏,这是因为每次都不通过,所有片段都会被丢弃。

在GL_LESS的模式下3D效果更加真实

在GL_ALWAYS:每次都通过测试,后面的片段会覆盖之前的片段。

深度冲突现象

深度测试中,深度冲突现象需要值得注意。深度冲突(Z-fighting)是指两个平面(或三角形)相互平行且靠近的过于紧密,模板缓冲区不具有足够的精度确定哪一个平面靠前,导致这两个平面的内容不断交替显示,看上去像平面内容争夺顶靠前的位置

防止深度冲突的方法

  • 不要让物体之间靠得过近,以免它们的三角形面片发生重叠;
  • 把近平面设置得远一些(越靠近近平面的位置精度越高);
  • 牺牲一些性能,使用更高精度的深度值。