OpenGL

OpenGL渲染技巧之正背面剔除、深度测试、颜色混合

2020-07-13  本文已影响0人  iOSer_jia

当我们使用默认光源着色器渲染一个3D图形,如果旋转物体,会发现转到背面会发生图形渲染出错问题,如下图我们只是使用默认光源着色器进行渲染一个红色的甜甜圈,旋转之后出现了黑红相间的情况。

未开启正背面剔除

造成这种现象的原因是OpenGL在渲染时是将整个物体渲染,而背面即观察者看不到的面也被一同渲染,这样造成了在旋转一定角度时,背面(即光源没有找到的阴面)也被观察者看到,这就造成图中黑色图层出现的问题。

正背面剔除

为了避免出现阴面的图层被观察者看到,我们显然不需要去渲染看不到到那一面,这样也可以节省性能。所以我们可以采用正背面剔除,来将不必要渲染的背面剔除。

// 开启背面剔除
glEnable(GL_CULL_FACE);

而默认的,我们将顶点连接顺序按逆时针顺序的称为正面,逆时针顺序的称为背面。

正背面.png

正面背面的定义是根据顶点的定义顺序和观察者方向共同决定的,随着观察者的方向改变,正背面也会跟着改变。

当然,我们也可以修改为剔除正面,或者修改顺时针为背面,OpenGL也给相应的修改API。

//选择剔除那个面
// mode 参数为GL_FRONT,GL_BACK,GL_FONT_AND_BACK,默认GL_BACK
void glCullFace(GLenum mode);

// 指定那个绕序为正面
// mode 参数为GL_CW,GL_CCW, 默认GL_CCW
void glFontFace(GLenum mode);

// 关闭表面剔除
void glEnable(GL_CULL_FACE);

深度测试

正背面剔除虽然解决了颜色交错的问题,但是当甜甜圈转到某个角度时,会出现下面这种情况。


未开启深度测试

不然理解造成这种现象的原因,因为物体转到这个角度时,有被OpenGL判定为绕序为正面的三角形被遮挡了,显然观察者看不到的面不应该被渲染出来,但只开启了正背面剔除的OpenGL是无法判断该三角形是否被遮挡,他只能依据正背面来决定渲染与否。所以我们要开启深度测试,来让被遮挡的面不渲染显示。

深度其实就是就是像素点在3D世界中距离摄像机的距离,而在GPU的缓冲区中,有一块区域,专门存储着每个像素点的深度值。而开启了深度测试,在决定是否绘制一个物体面时,首先要将表面对应像素点的深度与深度缓冲区的进行比较,如果大与深度缓冲区的值,即离观察者更远(观察者在z轴反方向),就丢弃这部分,否则用这个像素对应的深度值和颜色值,分别更新深度缓冲区和颜色值缓冲区(颜色缓冲区与深度缓冲区是一一对应的,它记录着颜色信息)。

//开启深度测试
glEnable(GL_DEPTH_TEST);
// 在绘制场景前,要清空颜色缓冲区,深度缓冲区
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

清除深度缓冲区的默认值为1.0,表示最大的深度值,深度值范围在0~1之间,值越小表示越接近观察者。

另外也可以指定深度测试的判断模式,不过一般不用修改。

void glDepthFunc(GLEnum mode);
mode 说明
GL_ALWAYS 总是通过测试
GL_NEVER 总是不通过测试
GL_LESS 当前深度值小于存储深度值时通过
GL_EQUAL 当前深度值等于存储深度值时通过
GL_LEQUAL 当前深度值小于等于存储深度值时通过
GL_GREATER 当前深度值大于存储深度值时通过
GL_NOTEQUAL 当前深度值不等于存储深度值时通过
GL_GEQUAL 当前深度值大于等于存储深度值时通过

也可以打开阻断深度缓冲区的写入。

// value: GL_TURE 开启深度缓冲区写⼊入; GL_FALSE 关闭深度缓冲区写⼊入
void glDepthMask(GLBool value);

多边形偏移

虽然深度测试解决了上述问题,但如果当前像素点的深度值与存储的深度值十分接近,差值小于深度缓冲区的精度时,就会导致OpenGL不能正确判断,使得结果不可预测,出现ZFighting闪烁问题。

ZFighting

为了解决这个问题,我们可以开启多边形偏移。
多边形偏移其实就是让两个深度值之间的差值做一些细微的增加,让OpenGL可以准确判断出该保存那个点到缓冲区中。

// 开启多边形偏移
glEnable(GL_POLYGON_OFFSET_FILL)
// 指定偏移量,一般写-1.0和-1.0
glPolygonOffset(GLfloat factor, GLfloat units);
参数 说明
GL_POLYGON_OFFSET_POINT 对应光栅化模式:GL_POINT
GL_POLYGON_OFFSET_LINE 对应光栅化模式:GL_LINE
GL_POLYGON_OFFSET_FILL 对应光栅化模式:GL_FILL

每个Fragment的深度值都会增加如下所示的偏移量量:
Offset = ( m * factor ) + ( r * units);
m : 多边形的深度的斜率的最大值,理解⼀个多边形越是与近裁剪⾯平⾏,m就越接近于0.
r : 能产⽣生于窗口坐标系的深度值中可分辨的差异最小值.r是由具体OpenGL平台指定的一个常量.
一个⼤于0的Offset会把模型推到离你(摄像机)更远的位置,相应的一个小于0的Offset会把模型拉近
一般⽽言,只需要将-1.0和-1.0这样简单赋值给glPolygonOffset基本可以满足需求.

使用多边形偏移后,需要将其关闭

// 参数同开启
glDisable(GL_POLYGON_OFFSET_FILL)

颜色混合

两个三角形重叠不只有遮挡,如果覆盖在上层的三角形是半透明,显然还需要考虑到颜色混合的问题。我们把渲染时会把颜色值存在颜色缓冲区中,与深度缓冲区一一对应,当一个颜色值保存到颜色缓冲区时,它会与原先的颜色进行计算得到一个新的颜色值保存到颜色缓冲区。

// 开启颜色混合
glEnanble(GL_BIEND);

我们将已存储在颜色缓存区的颜色称为目标颜色,将当前渲染命令结果进入颜色缓冲区的称为源颜色。但混合功能开启时,默认情况下混合的方程式为:

Cf = (Cs * S) + (Cd * D)

Cf: 最近计算得到的颜色
Cs: 源颜色
S: 源混合因子
Cd: 目标颜色
D: 目标混合因子

我们可以通过glBlendFunc(GLenum S,GLenum D);函数设置混合因子。通常设置为GL_SRC_ALPHA和GL_ONE_MINUS_SRC_ALPHA即可。

也可以修改混合的方程式改变混合的方式。

glbBlendEquation(GLenum mode);
mode function
GL_FUNC_ADD Cf = (Cs * S) + (Cd * D)
GL_FUNS_SUBTRACT Cf = (Cs * S) - (Cd * D)
GL_FUNC_REVERSE_SUBTRACT Cf = (Cd * D) - (Cs * S)
GL_MIN Cf = min(Cs, Cd)
GLMAX Cf = max(Cs, Cd)
上一篇 下一篇

猜你喜欢

热点阅读