Open GL常见渲染技巧
1.正背面剔除
2.深度测试
3.多边形偏移
4.颜色混合
1.正背面剔除
1.1 任何一个3D的物体,比如立方体,球,多边形多面立方体,等等,就像地球有昼夜一样,太阳光永远只能照射在地球的一个面上,在任何情况下我们都只能看到其中的一个面,那么对于看不到的背面在计算机里图形学里面如果不被渲染,那么性能会提高50%,即使渲染了也不看不到,而且性能下降,所以,完全没有必要。这种对于看不到的面不去渲染的情况叫做“隐藏面消除”
Open GL 里面渲染3D物体,在有光照的情况下,如果没有开启正背面剔除会有很大的问题存在,下面6张图分别展示了未开启正背面剔除和开启正背面剔除的效果,面线点三种填充模式下的情形
分析
• 图1,未开启正背面剔除出现的异常情况
• 图2,开启正背面剔除,异常情况消失
• 图3,未启正背面剔除,线填充模式,背面的线被渲染了
• 图4,开启正背面剔除,线填充模式,背面的线没有被渲染
• 图5,未启正背面剔除,点填充模式,背面的点被渲染了
• 图6,开启正背面剔除,点填充模式,背面的点没有被渲染
• 图7,开启正背面剔除,再次异常
• 图2,4,6,7都是开启的背面剔除
图1,异常产生的原因是由于正背面都需要渲染,这样就增加了深度(z值,距离观察的距离,z越小距离观察者越近,反之亦然)极限接近的(Open GL系统能够正确识别的最小精度)像素点的数量和概率都大大增加,以至于Open GL系统不能准确识别某一个像素点的具体颜色,导致出现混乱情况。
那么正背面剔除恰恰很大程度上减少了这样的像素点和概率(至少一半的像素点不需要渲染),但是,有些观察者的角度可能还会有这样的像素点和概率出现,虽然大多数角度都是可以被正常的渲染,如图7所示,解决这个问题的办法是开启深度测试
1.2 Open GL里面,如何界定一个3D物体的正背面呢,两个影响的因素,一个是顶点的链接顺序,一个是观察者的角度。
1.2.1 顶点顺序-正背面区分
正面:按照逆时针顶点链接顺序的三角形面
背面:按照顺时针顶点链接顺序的三角形面
当然,正背面的时针顺序在代码里面可以设置,正背面相对于观察者的角度是相对的,角度相反正背面互换。
正背面如下2图所示
分析
• 左侧三⻆形顶点顺序为: 1—> 2—> 3 ; 右侧三角形的顶点顺序为: 1—> 2—> 3
• 当观察者在右侧时,则右边的三⻆形方向为逆时针,右侧则为正⾯,⽽左侧的三角形为顺时针,方向则为背面
• 当观察者在左侧时,则左边的三⻆形方向为逆时针,左侧则为正⾯,⽽右侧的三角形为顺时针,方向则为背面
总结
• 正⾯和背面是由三⻆形的顶点定义顺序和观察者方向共同决定的.随着观察者的角度方向的改变,正面和背面也会跟着改变
• 观察者的角度很好理解,站在不同的角度方位看一个物体,能看到的一面是正面,看不到的是背面
1.3 正背面剔除常用函数
// 1.开启正背面剔除(默认背面剔除)void glEnable(GL_CUll_FACE);
glEnable(GL_CULL_FACE);
// 2.关闭正背面剔除(默认背面剔除)void glDisable(GL_CUll_FACE);
glDisable(GL_CULL_FACE);
// 3.选择剔除的那个面(正面/背面)void glFrontFace(GLenum mode)
glCullFace(GL_BACK); //剔除背面(默认值)
glCullFace(GL_FRONT); //剔除正面
glCullFace(GL_FRONT_AND_BACK); //剔除正面背面
// 4.用户指定顺时针或者逆时针为正面 glFrontFace(GLenum mode)
glFrontFace(GL_CCW); // 逆时针为正面(默认值)
glFrontFace(GL_CW); // 顺时针为正面
// 5.剔除背面方式1 - 正常情况下使用默认的情况即可
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// 5.剔除背面方式2
glEnable(GL_CULL_FACE);
glFrontFace(GL_CW);
glCullFace(GL_FRONT);
// 5.剔除正面方式1
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
// 5.剔除正面方式2
glEnable(GL_CULL_FACE);
glFrontFace(GL_CW);
glCullFace(GL_BACK);
2. 深度测试
分析
•Open GL中 观察者的坐标系是如上图所示,其中的z轴就是深度,也就是物体和我们的距离,z的正方向是指向屏幕内部,所以,离我们越远z值越大,反之亦然
•物体是3D的,屏幕显示设备是2D的,那么我们通过z值的大小来模拟现实中的3D物体,z值越大(离我们越远)在屏幕上显示的就越小,z值越小(离我们越近)在屏幕上显示的就越大,近大远小,复合眼睛看到的实际情况
•颜色缓冲区和深度缓冲区
颜色缓冲区,保存每一个像素点的颜色值,像素(key)= 颜色(value)
深度缓冲区,保存每一个像素点的颜色值,像素(key)= 深度(value)
采取keyValue的模式(个人理解)
•颜色缓冲区和深度缓冲区的关系
两者一一对应,同时更新颜色值RGBA和深度值z,因为一个像素点只能保留一个颜色和一个深度值z
•深度测试
深度缓冲区(DepthBuffer)和颜⾊色缓存区(ColorBuffer)是对应的.颜⾊色缓存区存储像素的颜⾊信息,而深度缓冲区存储像素的深度信息. 在决定是否绘制一个物体表⾯时, ⾸先要将表面对应的像素的深度值与当前深度缓冲区中的值进⾏比较. 如果⼤大于深度缓冲区中的值,则丢弃这部分.否则 利⽤用这个像素对应的深度值和颜色值.分别更新深度缓冲区和颜⾊缓存区. 这个过程称为”深度测试”
•深度测试的理解
屏幕上显示的2D物体,实际上就是每一个像素点上显示不同的颜色,如果两个都是不透明的物体,相互重叠地放在我们眼前,重叠地部分看到的一定是离我们近的物体,这是毋庸置疑的,那么对应到Open GL里面的深度z就是表示距离我们的远近,不透明的情况下,我们比较重叠部分的深度值z,哪个越小即离我们越近,重叠部分的像素点就应该渲染对应的颜色,而深度值z大的被遮挡住就不需要渲染了,这就是深度测试,深度,深度缓冲区,再次理解如下所述
•什么是深度?
深度其实就是该像素点在3D世界中距离摄像机的距离,Z值
•什么是深度缓冲区?
深度缓存区,就是⼀块内存区域,专⻔存储着每个像素点(绘制在屏幕上的)深度值,深度值(Z值)越⼤,则离摄像机就越远.
•为什么需要深度缓冲区?
在不使用深度测试的时候,如果我们先绘制一个距离比较近的物体,再绘制距离较远的物体,则距离远的位图因为后绘制,会把距离近的物体覆盖掉,有了深度缓冲区后, 绘制物体的顺序就不那么重 要的, 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中. 除⾮调⽤glDepthMask(GL_FALSE).来禁⽌写入.
•只开启深度测试,上面所述的两种异常,即未开启正背面剔除和开启正背面剔除,完全消失,只不过没有开启正背面剔除,看不到的背面也被渲染了,性能下降了而已,深度测试+正背面剔除就可以达到渲染没有问题且性能提高50%,3D图形渲染的时候两者全部开启总是没错的,渲染完毕之后要记得关闭
2.2 深度测试函数
// 1.开启深度测试
glDisable(GL_DEPTH_TEST);
// 2.关闭深度测试
glDisable(GL_DEPTH_TEST);
// 3.指定深度测试判断模式
glDepthFunc(GL_LESS); //GL_LESS 默认值
// 4.打开、阻断深度缓冲区写入,默认打开
glDepthMask(GL_TRUE); // 开启深度缓冲区写入
glDepthMask(GL_FALSE); // 开启深度缓冲区写入
2.3 ZFighting闪烁问题的原因
因为开启深度测试后,OpenGL 就不不会再去绘制模型被遮挡的部分. 这样实现的显示更更加真实.但是 由于深度缓冲区精度的限制对于深度相差⾮常小的情况下.(例如在同⼀平面上进⾏2次 制),OpenGL 就可能出现不能正确判断两者的深度值,会导致深度测试的结果不可预测.显示出来的 现象时交错闪烁.前面2个画⾯,交错出现.
开启深度测试,深度z之间的距离如果大于Open GL所能识别的最小精度,那么美问题,如果不能则会出现像素点颜色与预期不符合的情况,也就是ZFighting,入下图所示
•如何解决ZFighting
z之间的间距小了,扩大一些间距就好了
让深度值之间产⽣间隔.如果2个图形之间有间隔,是不不是意味着就不会产⽣干涉.可以理 解为在执⾏深度测试前将3D物体的深度值做⼀些细微的增加.于是就能将叠的2个图形深度值之 间有所区分.