OpenGL的正面&背面剔除和深度测试
渲染中可能会出现的问题(不希望出现的几何图形)
默认情况下,我们所渲染的每个点、线或三角形都会在[图片上传中...(opengl1.png-b3c9f6-1625020212941-0)]
屏幕上进行光栅化,并按照在组合图元批次时指定的顺序排列,这在某些情况下会产生问题。
如果我们绘制一个由很多个三角形组成的实体对象,那么第一个绘制的三角形可能会被后面绘制的三角形覆盖。如下图这个像游泳圈似的模型,其中一些三角形在游泳圈的背面,另一些在正面,正常我们应该是看不到背面的(不考虑透明几何体的特殊情况)。这样的话,三角形绘制的顺序可能会一团糟,就变成了下图的样子:
opengl1.png
解决方法
1.油画法(painters algorithm):
对这些三角形排序,先渲染较远的三角形,再在它们上方渲染较近的三角形。但这种方法在图形处理中效率很低,必须在任何发生重叠的地方对每个像素进行两次写操作,速度会变慢。并且对独立的三角形排序的开销会过高。所以一般不推荐使用。
2. 正面&背面剔除:
对正面和背面三角形进行区分的原因之一就是为了进行剔除。背面剔除能极大提高性能,避免上图出现的问题。它很高效,在渲染的图元装配阶段就整体抛弃了一些三角形。
开启背面剔除:
glEnable(GL_CULL_FACE);
关闭背面剔除:
glDisable(GL_CULL_FACE);
请注意,我们并没有知名剔除的是正面还是背面。这是由另外一个函数 glCullFace 控制的。
void glCullFace(GLenum mode);
mode 参数的可用值为 GL_FRONT、GL_BACK 或 GL_FRONT_AND_BACK。这样要消除不透明物体的内部几何图形就需要两行代码:
void glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
在某些情况下,剔除实体几何体的正面也很有必要,比如要显示图形内部渲染的时候。在渲染透明对象时(下面马上就会讲到混合),我们经常会对一个对象进行两次渲染,第一次会开启透明并剔除正面,第二次则消除背面。这样就在渲染正面之前渲染了背面,这也是渲染透明物体的需要。
但是在开启背面剔除后,会发现上面的游泳圈模型还是显示的有问题,原因没有开启深度测试。如图:
opengl2.png
3. 深度测试:
- 深度: 深度就是在openGL坐标系中,像素点的 Z 坐标距离观察者的距离。观察者可能放在坐标系的任何位置,那么,就不能简单的说 Z 数值越大或越小,就是越靠近观察者。
如果观察者在Z轴的正方向,Z 值大的靠近观察者,如果是在Z轴的反方向,则 Z 值小的更靠近观察者。- 深度缓冲区(DepthBuffer): 深度缓冲区原理就是把一个距离观察平面(近裁剪面)的深度值(或距离)与窗口中的每个像素相关联。
首先,使用glClear(GL_DEPTH_BUFFER_BIT),把所有像素的深度值设置为最大值。
如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把它的深度值和已经存储在这个像素的深度值进行比较。如果,新像素深度值 < 原先像素深度值,则新像素值会取代原先的;反之,新像素值被遮挡,它的颜色值和深度将被丢弃。
这个比较、丢弃的过程就叫做 深度测试,深度测试是另一种高效消除隐藏面的技术。
申请一个颜色缓冲区和一个深度缓冲区:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
要启用深度测试,只需调用
glEnable(GL_DEPTH_TEST);
关闭深度测试:
glDisable(GL_DEPTH_TEST);
如果没有深度缓冲区,那么启动深度测试的命令将被忽略。
在绘制场景前,清除颜色缓冲区和深度缓冲区:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
开启了深度测试后,我们终于得到了一个我们想要的游泳圈模型:
opengl3.png
清除深度缓冲区的默认值是1.0,表示最大的深度值,深度值的范围在[0,1]之间。
用户通过glDepthFunc(GLenum func)函数指定深度测试的规则,这个函数包括一个参数,如下表:
参数 | 说明 |
---|---|
GL_ALWAYS | 总是通过测试 |
GL_NEVER | 总是不通过测试 |
GL_LESS | 当前深度值 < 存储的深度值时通过 |
GL_EQUAL | 当前深度值 = 存储的深度值时通过 |
GL_LEQUAL | 当前深度值 <= 存储的深度值时通过 |
GL_GREATER | 当前深度值 > 存储的深度值时通过 |
GL_NOTEQUAL | 当前深度值 != 存储的深度值时通过 |
GL_GEQUAL | 当前深度值 >= 存储的深度值时通过 |
z-fighting(z冲突、闪烁)问题:
当深度值精确度很低时,容易引起ZFighting现象,表现为两个物体靠的很近时确定谁在前,谁在后时出现了歧义。问题表现如图:
opengl4.png
- 在第二次绘制时,插入一个少量的偏移。
- 使用 glPolygonOffset 函数调节片段的深度值,使得深度值偏移而不产生重叠。
- 使用更高位数的深度缓冲区,通常使用的深度缓冲区是24位的,现在有一些硬件使用使用32位的缓冲区,使精确度得到提高。