在绘制3D场景时,为了尽可能的逼真需要有近大远小的效果,而且可能会出现互相遮盖的情况。例如上图前面甜点会遮住后面的一部分,这种情况下为了提高渲染性能我们就不应该渲染被遮住的那部分,那如何处理呢?
正背面剔除
以常见的正方体为例,在任何一个角度方向上可见的最多3个面。如果以我们看见的角度为正面,看不见的为背面,也就是说我们只要渲染看见的那部分就可以了。
OpenGL可以做到检查所有正面朝向观察者的面,并渲染它们,背后朝向的面则不再渲染,从而节约片元着色器的性能。
1. 首先,我们需要告诉OpenGL那个面为正面
OpenGL是通过分析顶点顺序来区分正背面的,默认情况下是按照逆时针顶点连接顺序的三⻆形⾯为正面,按照顺时针顶点连接顺序的三角形⾯为背面。
需要注意的是,正背面是由顶点定义顺序和观察者方向共同决定的,随着观察者角度方向的改变,正背面也可能改变。
2. 正背面剔除相关代码
开启表面剔除(默认背面剔除)
void glEnable(GL_CULL_FACE);
关闭表面剔除(默认背面剔除)
void glDisable(GL_CULL_FACE);
用户选择剔除哪个面(正面/背面)
void glCullFace(GLenum mode);
mode参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默 认GL_BACK
用户指定绕序哪个为正⾯
glFrontFace(GL_CW);
mode参数为: GL_CW,GL_CCW,默认值:GL_CCW
例如,剔除正面实现(1)
glCullFace(GL_BACK);
glFrontFace(GL_CW);
例如,剔除正⾯面实现(2)
glCullFace(GL_FRONT);
如果是封面图的情况下,正背面剔除还远远不够用,接下来还需要加入深度测试哦。
深度测试
所谓深度,就是该像素点在3D世界中距离摄像机的距离,z值。也可以说是三维坐标系中的Z轴距离。
深度测试,深度缓冲区(DepthBuffer)和颜色缓冲区(ColorBuffer)是对应的。颜色缓冲区存储像素的颜色信息,而深度缓冲区存储像素的深度信息。在决定是否绘制一个物体表面时,首先要将表面对应的像素的深度值与当前深度缓冲区中的值进行比较。如果大于深度缓冲区中的值,则丢弃这部分,否则利用这个像素对应的深度值和颜色值,分别更新深度缓冲区和颜色缓冲区,这个过程就是“深度测试”。
深度缓冲区,就是一块内存区域(一般由窗口管理系统,GLFW创建),专门存储绘制在屏幕上的每个像素点的深度值,深度值越大距离摄像机越远。只要存在深度缓冲区,OpenGL就会把像素的深度值写入缓冲区中,可以调用glDepthMask(GL_FALSE)来禁止写入。
补充信息
深度值计算,深度值一般由16位,24位或者32位值表示,通常是24位。位数越高,深度值越精确。深度值范围在[0,1]之间,值越小越小越靠近观察者,反之亦然。
视图空间中的z值可以在投影平头截体的近平面和远平面之间的任何值。因此我们需要一些方法把这些视图空间z值转换到[0,1]的范围内,下面的线性方程把z值转换为0.0和1.0之间的值:
注:far和near是提供到投影矩阵设置可见视图截锥的远近值
使用深度测试
开启深度测试
glEnable(GL_DEPTH_TEST);
在绘制场景前,最好清除颜色缓冲区和深度缓冲区
gl_Clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
还可以指定深度测试判断模式
void gl_Depth_Func(GLEnum mode);
ZFighting闪烁问题
开启深度测试之后确实可以帮助我们节约很多性能,但是也会有一些令人头疼的问题ZFighting闪烁。由于深度缓冲区精度的限制,对于深度相差非常小的情况下OpenGL 就无法正确判断出两者的深度值,会导致渲染的结果无法预测,显示出来的图像交错闪烁(时而渲染出绿色,时而渲染出红色,无法确定,交错闪烁)。
一. 启用Polygon Offset 方式解决
解决方法:让深度值之间产生间隔,避免干涉。就是在执行深度测试之前对物体的深度值做一些细微的增加。
开启Polygon Offset
glEnable(GL_POLYGON_OFFSET_FILL)
参数:
对应光栅化模式 GL_POINT:
GL_POLYGON_OFFSET_POINT
对应光栅化模式 GL_LINE:
GL_POINTGL_POLYGON_OFFSET_LINE
对应光栅化模式 GL_FILL:
GL_POLYGON_OFFSET_FILL
二. 指定偏移量
void glPolygonOffset(Glfloat factor,Glfloat units);
一般将factor和units设置为-1.0
每个Fragment 的深度值都会增加如下所示的偏移量:
Offset = ( m * factor ) + ( r * units);
m : 多边形的深度的斜率的最大值,理解⼀个多边形越是与近裁剪⾯平行,m 就越接近于0.
r : 能产生于窗⼝坐标系的深度值中可分辨的差异最小值.r 是由具体OpenGL平台指定的一个常量.
一个⼤于0的Offset 会把模型推到离你(摄像机)更更远的位置,相应的一个⼩小于0的Offset 会把模型拉近
三. 关闭Polygon Offset
glDisable(GL_POLYGON_OFFSET_FILL);
只要glEnable开启过,就需要glDisable关闭
最后,不要将两个物体靠的太近,避免渲染时三角形叠在⼀起。这种⽅式要求对场景中物体插入一个少量的偏移,那么就可能避免ZFighting现象;
尽可能将近裁剪面设置得离观察者远⼀些,在近裁剪平面附近,深度的精确度是很高的,因此尽可能让近裁剪面远一些的话,会使整个裁剪范围内的精确度变高一些。但是这种⽅式会使离观察者较近的物体被裁减掉,因此需要调试好裁剪⾯面参数;
使⽤更高位数的深度缓冲区,通常使⽤的深度缓冲区是24位的,现在有一些硬件使⽤32位的缓冲区,使精确度得到提⾼。