一、渲染中可能会出现的问题(不希望出现的几何图形)
默认情况下,我们所渲染的每个点、线或三角形都会再屏幕上进行光栅化,并按照在组合图元批次时指定的顺序排列,这在某些情况下会产生问题。
二、解决方法
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);
在某些情况下,剔除实体几何体的正面也很有必要,比如要显示图形内部渲染的时候。在渲染透明对象时(下面马上就会讲到混合),我们经常会对一个对象进行两次渲染,第一次会开启透明并剔除正面,第二次则消除背面。这样就在渲染正面之前渲染了背面,这也是渲染透明物体的需要。
但是在开启背面剔除后,会发现上面的游泳圈模型还是显示的有问题,如图:这是因为没有开启深度测试。
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);
开启了深度测试后,我们终于得到了一个我们想要的游泳圈模型:清除深度缓冲区的默认值是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现象,表现为两个物体靠的很近时确定谁在前,谁在后时出现了歧义。
- 避免深度值相同造成的z-fighting冲突问题的几种做法:
- 在第二次绘制时,插入一个少量的偏移。
- 使用
glPolygonOffset
函数调节片段的深度值,使得深度值偏移而不产生重叠。
- 使用更高位数的深度缓冲区,通常使用的深度缓冲区是24位的,现在有一些硬件使用使用32位的缓冲区,使精确度得到提高。
以上的总结参考了并部分摘抄了以下文章,非常感谢以下作者的分享!:
1、《OpenGL超级宝典 第5版》
2、《OpenGL编程指南(第八版)》
3、作者 The fool 的《OpenGL学习脚印:深度测试(depth testing)》
4、作者 lysc_forever的《OpenGL中的深度、深度缓存、深度测试》
转载请备注原文出处,不得用于商业传播——凡几多