OpenGL 正背面剔除、深度测试、颜色混合

370 阅读5分钟

正背面剔除

话不多说,先看图:

在旋转的过程中,图形会出现黑色。这是因为,在本案例中,开启了默认光源着色器,在旋转过程中,OpenGL 不能确定应该显示那一面,有些在阴影面的图元没有被丢弃。造成阴影免得图元也被显示出来了,就会出现黑色。

使用平面着色器我们不到这种现象,并不是因为这种现象没有出现,而是应为正面和背面都是红色,观察不出来这种现象。如下图

解决方案

告诉 OpenGL 正背面。OpenGL 对正面和背面的有自己的定义:

正面:面对图元,按照逆时针顶点连接顺序的三角形面

背面:面对图元,按照顺时针顶点连接顺序的三角形面

下面介绍下 OpenGL 中关于正背面相关的函数:

glEnable (GL_CULL_FACE);  //开启正背面剔除,默认剔除背面
glDisable(GL_CULL_FACE);  //关闭正背面剔除
void glCullFace(GLenum mode);  //选择剔除正面还是背面,mode:GL_FRONT(正面), GL_BACK(背面),  GL_FRONT_AND_BACK(正背面)
void glFrontFace(GLenum mode); //设置正面,mode:GL_CW(顺时针),GL_CCW(逆时针)

深度测试

设置完正背面剔除之后,再来旋转这个图形,发现了新的 bug,图形在旋转的过程中少了一块。如下图

这是因为,在旋转的过程中,OpenGL 不知道图元离观察者的远近。在这个例子中,一部分正对着观察者的外环和内环都是正面,OpenGL在旋转到一些位置的时候不知道有些图元已经被离观察者更加的图元遮挡了,就会造成这种效果。

开始深度测试之前我们先介绍下深度缓冲

深度缓冲、z缓冲(z-buffer)就像颜色缓冲(Color Buffer)(储存所有的片段颜色:视觉输出)一样,在每个片段中储存了信息,并且(通常)和颜色缓冲有着一样的宽度和高度。 深度缓冲是由窗口系统自动创建的,它会以16、24或32位float的形式储存它的深度值([0,1]之间的浮点数值)。在大部分的系统中,深度缓冲的精度都是24位的。

深度测试(Depth Testing)被启用的时候,OpenGL会将一个片段的的深度值与深度缓冲的内容进行对比。OpenGL会执行一个深度测试(检查谁离观察者更近),如果这个测试通过了的话,深度缓冲将会更新为新的深度值(对应的颜色缓冲区的值也需要更新)。如果深度测试失败了,片段将会被丢弃。

开启关闭深度测试的函数

glEnable (GL_DEPTH_TEST);  //开启深度测试
glDisable(GL_DEPTH_TEST);  //关闭深度测试

改变深度测的测试规则:

glDepthFunc(GLenum mode)    // mode参数说明见下表
参数 说明
GL_ALWAYS 总是通过深度测试
GL_NEVER 总是不通过深度测试
GL_LESS 测试深度值 < 存储的深度值时通过
GL_EQUAL 测试深度值 = 存储的深度值时通过
GL_LEQUAL 测试深度值 <= 存储的深度值时通过
GL_GREATER 测试深度值 > 存储的深度值时通过
GL_NOTEQUAL 测试深度值 != 存储的深度值时通过
GL_GEQUAL 测试深度值 >= 存储的深度值时通过

tips: 深度测试也可以解决影藏面消除的问题。

z-fighting

深度测试的风险:z-fighting(z冲突),当两个片段的 Z 值相当接近,超出了深度的精度范围。这时候深度测试无法确定显示那个片段。

解决 z-fighting 的方法

开启多边形偏移

多边形偏移是通过在扩大深度值之间的间隔来解决这个问题。

开启关闭多边形偏移:

glEnable (GLenum cap);  //开启多边形偏移 cap参数说明见下表
glDisable(GLenum cap);  //关闭多边形偏移 cap参数说明见下表
参数 对于填充模式
GL_POLYGON_OFFSET_POINT GL_POINT
GL_POLYGON_OFFSET_LINE GL_LINE
GL_POLYGON_OFFSET_FILL GL_FILL

指定偏移量

void glPolygonOffset(   GLfloat factor, //指定用于为每个多边形创建可变深度偏移的比例因子。初始值为0
                        GLfloat units); //用来乘以特定的值来创建固定的深度偏移。初始值为0

预防 z-fighting 的方法

1、不要让物体离的太近

2、让近剪裁面离观察者远一些

3、提高设备精度

颜色混合

根据我们学到的深度测试的知识,可以判断,在上面的图形当中,两个正方形叠加的部分显示的是黄色。那么当黄色的正方形有一定的透明度的时候,重叠的部分显示的是什么颜色?这就和下面要讨论的问题颜色混合有关了。当我们在渲染的时候开启了颜色混合,就会显示类似下图的效果:

颜色混合就是用 将要和已经存储在颜色缓冲区当中的颜色混合的颜色 在和已经存储在颜色缓冲区当中的颜色 按照混合公式计算的结果替换掉已经存储在颜色缓冲区当中的颜色。就像我们的赋值运算:

a就是已经存储在颜色缓冲区当中的颜色,

b就是要和已经存储在颜色缓冲区当中的颜色混合的颜色

int a = 5;
int b = 6;
a = a * b;

在固定着色器中,我们只需要打开颜色混合的开关就能进行颜色混合。使用glEnable (GLBLEND)glDisable (GLBLEND)开启和关闭颜色混合,

在可编程着色器中,颜色混合是在片元着色器上进行的。我们需要根据公式进行混合计算。(滤镜效果)

混合公式:

Cf = ( Cf * S ) + ( Cd * D ) 
Cf: 最终颜色
Cf: 源颜色,也就是将要和已经存储在颜色缓冲区当中的颜色混合的颜色
S : 源混合因子
Cd: 目标颜色,也就是已经存储在颜色缓冲区当中的颜色
D : 目标混合因子

对应的函数是:

void glBlendFunc(	GLenum sfactor,  //源混合因子  参数见
                    GLenum dfactor); //目标混合因子

sfactor参数 dfactor参数

以上是我对OpenGL 正背面剔除、深度测试、颜色混合学习的整理总结。如有疏漏请留言。