用GLTools中的自带函数gltMakeTorus绘制一个红色的甜甜圈,然后用点光源着色器进行渲染渲染。当让甜甜圈进行旋转后就会出现有黑色块状的问题,如下图:
出现这种问题的原因是,甜甜圈是一个立体图形,有正面和背面。我们用点光源着色器,正常情况下,正面被渲染成红色,背面为黑色。在甜甜圈旋转的过程中,正面依次变成了背面,而背面依次变成了正面,正背面交织在一起,就出现了有红色有黑色的情况。
在绘制物体时,先绘制离观察者较远的部分,再绘制较近的部分,这种绘制方式叫做油画算法。通过油画算法可以有效解决正背面交织显示的问题。
但是油画算法存在弊端:
一个3D物体,我们从任何一个方向去观察,都是只能看到它的一部分表面。对于看不到的表面,其实是没有必要进行绘制的。这种只绘制看得到的部分而剔除掉看不到的部分的绘制方式叫做正背面剔除。
在OpenGL中,所绘制的图形是通过一个个的三角形组合成的。OpenGL通过分析顶点数据的顺序得知哪些面是正面、哪些面是背面。默认规则:
上图中,左侧三角形顶点顺序为1->2->3,右侧三角形顶点顺序为1->2->3。
//开启正背面剔除
glEnable(GL_CULL_FACE);
//关闭正背面剔除
glDisable(GL_CULL_FACE);
/*
选择剔除哪个面(默认背面剔除)
GL_FRONT:正面
GL_BACK:背面
GL_FRONT_AND_BACK:正面和背面
*/
glCullFace(GL_BACK);
/**
指定哪个顶点顺序为正面,默认GL_CCW
GL_CW:顺时针为正面
GL_CCW:逆时针为正面
*/
glFrontFace(GL_CCW);
正/背面的判断方式尽量以默认方式为准,不要自己去修改,防止混乱。
开启正背面剔除后,有效解决了红、黑两色交错显示的问题,但却仍然有问题。
当圈圈沿着Y轴旋转到左右两边的圈圈重合时,重合处会出现一个缺口。如图中显示,甜甜圈的左边向屏幕里面移动,右边向屏幕外面移动,当左右两边重合时,左边圈圈的内测和右边圈圈的外侧,都面向观察者,所以都被判定为正面而需要显示。绘制重叠就造成了图中的缺口景象。要解决这个问题,需要用到新的技术,深度测试。
glDepthMask(GL_FALSE)
来禁⽌写⼊。深度测试中,默认是:新深度值 < 老深度值,则进行替换。但也有其他规则供开发者调用glDepthFunc来修改:
参数 | 说明 |
---|---|
GL_ALWAYS | 总是通过测试 |
GL_NEVER | 总是不通过测试 |
GL_EQUAL | 在当前深度值 = 存储的深度值时通过 |
GL_NOTEQUAL | 在当前深度值 != 存储的深度值时通过 |
GL_LESS | 在当前深度值 < 存储的深度值时通过 |
GL_LEQUAL | 在当前深度值 <= 存储的深度值时通过 |
GL_GREATER | 在当前深度值 > 存储的深度值时通过 |
GL_GEQUAL | 在当前深度值 >= 存储的深度值时通过 |
//开启深度测试
glEnable(GL_DEPTH_TEST);
//关闭深度测试
glDisable(GL_DEPTH_TEST);
//开启/关闭深度缓冲区写入。默认开启,当关闭时,开启深度测试会无效,因为深度值数据无法写入缓冲区。
glDepthMask(GL_TRUE);
//修改深度测试规则,默认是GL_LESS
glDepthFunc(GL_LESS);
开启深度测试后,OpenGL就不会再去绘制模型被遮挡的部分。但是由于深度缓冲区精度有限,对于深度相差⾮常⼩的情况,OpenGL就可能出现不能正确判断两者的深度值,会导致深度测试的结果不可预测,显示出来的现象时交错闪烁。尤其在一些早期的机子中,发生的概率比较大。
ZFighting闪烁的根本原因是深度值之间的差距太小,所以OpenGL采用了多边形偏移的方案解决此问题。也就是说,在深度测试前,将物体的深度值做一些细微的调整,避免因差距过小产生ZFighting现象。这个调整不需要开发者手动调整,通过调用函数glEnable(GL_POLYGON_OFFSET_LINE)开启,OpenGL就会自动进行调整。
/*
启⽤多边形偏移
由于渲染物体时光栅化模式有三种,所以多边形偏移的方式也有三种。开启时,参数需要跟填充方式一致:
GL_POLYGON_OFFSET_POINT对应光栅化模式:GL_POINT
GL_POLYGON_OFFSET_LINE对应光栅化模式:GL_LINE
GL_POLYGON_OFFSET_FILL对应光栅化模式:GL_FILL
*/
glEnable(GL_POLYGON_OFFSET_FILL)
glPolygonOffset (GLfloat factor, GLfloat units)
每个深度缓冲区的深度值都会增加如下所示的偏移量:Offset = (m * factor) + (r * units)
应⽤到⽚段上总偏移计算⽅程式:DepthOffset=(DZ*factor)+(r*units)
glDisable(GL_POLYGON_OFFSET_FILL)
裁剪是在OpenGL中提⾼渲染的⼀种⽅式,只刷新屏幕上发⽣变化的部分,OpenGL允许将要进⾏渲染的窗⼝只去指定⼀个裁剪框。
基本原理:⽤于渲染时限制绘制区域,通过此技术可以在屏幕(帧缓冲)指定⼀个矩形区域。启⽤剪裁测试之后,不在此矩形区域内的⽚元被丢弃,只有在此矩形区域内的⽚元才有可能进⼊帧缓冲。因此实际达到的效果就是在屏幕上开辟了⼀个⼩窗⼝,可以在其中进⾏指定内容的绘制。
//开启裁剪测试
glEnable(GL_SCISSOR_TEST);
//关闭裁剪测试
glDisable(GL_SCISSOR_TEST);
/**
指定裁剪窗⼝
x,y:指定裁剪框左下⻆位置
width,height:指定裁剪区域尺寸
*/
glScissor(x, y, width, height);
理解窗⼝、视⼝、裁剪区域:
在OpenGL渲染时会把颜⾊值存在颜⾊缓存区中,每个⽚段的深度值也是放在深度缓冲区。当深度缓冲区被关闭时,新的颜⾊将简单的覆盖原来颜⾊缓存区存在的颜⾊值,当深度缓冲区再次打开时,新的颜⾊⽚段只是当它们⽐原来的值更接近邻近的裁剪平⾯才会替换原来的颜⾊⽚段。
//开启颜色混合
glEnable(GL_BlEND);
//关闭颜色混合
glDisable(GL_BLEND);
当混合功能被启动时,⽬标颜⾊和源颜⾊的组合⽅式是混合⽅程式控制的。在默认情况下,混合⽅程式如下所示:
Cf = (Cs * S) + (Cd * D)
设置混合因⼦,需要⽤到函数glBlendFunc (GLenum sfactor, GLenum dfactor);
表中R、G、B、A分别代表红、绿、蓝、alpha;表中下标S、D分别代表源、⽬标;表中C代表常量颜⾊(默认⿊⾊)。
如果颜⾊缓存区已经有一个目标颜色(Cd)红⾊(1.0f, 0.0f, 0.0f, 1.0f),这时候进来一个源颜色(Cs)alpha为0.6的蓝色(0.0f, 0.0f, 1.0f, 0.6f)。设置混合因子:glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
则
最终颜⾊是以原先的红⾊(⽬标颜⾊)与后来的蓝⾊(源颜⾊)进⾏组合。源颜⾊的alpha值越⾼,添加的蓝⾊颜⾊成分越⾼,⽬标颜⾊所保留的成分就会越少。
混合函数经常⽤于实现,在⼀些不透明的物体上面绘制⼀个透明物体的效果。比如两个图层叠加,上层的图层透明,则混合。上面的图层不透明,则不混合,直接覆盖下面的图层颜色。
上面的颜色混合方程式是OpenGL中的默认混合方程式,实际上一共存在5种方程式。可以通过调用宏定义glBlendEquation(GLenum mode)
来选择使用哪种混合方程式。
GLenum | 方程式 |
---|---|
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) |
GL_MAX | Cf = max(Cs, Cd) |