本章主要解决一个问题:
如何使用背面剔除来减少需要绘制的三角形数量?
引言
背面剔除,顾名思义,就是在渲染的时候,将背对着观察者的面片丢弃,只将正面朝向观察者(观察者能看到的)面片进行计算。举个栗子:之前我们显示的立方体盒子,不管从哪个角度看,我们最多只能看到3个面,有时甚至都只能看到一个面,那么,为什么我们还要对其他看不见的面进行渲染计算呢?将看不见的面剔除,我们最少可以节省50%的CPU和GPU资源。
背面剔除的目的就是节省CPU和GPU的资源,千万别觉得现在的CPU和GPU资源已经过剩了,对游戏来说,还差的远了。
如何判断一个面片是正面还是背面?
这是我们面临的最直接的一个问题。我们采用的方法是:定义一个顶点绕序来标识成正面,这样,OpenGL在渲染的时候如果看到这个面的顶点绕序不是正面绕序,那就判定其实背面,把它丢弃。
顶点绕序有两种:顺时针和逆时针。如下图,左边的就是顺时针绕序,右边的就是逆时针绕序:
原理十分简单!当我们在定义顶点的时候,我们可以在顶点1之后先定义顶点3来改变三角形的顶点绕序。用代码模拟就是这个样子:
float vertices[] = {
// 顺时针
vertices[0], // 顶点1
vertices[1], // 顶点2
vertices[2], // 顶点3
// 逆时针
vertices[0], // 顶点1
vertices[2], // 顶点2
vertices[1] // 顶点3
};
每个三角形图元的三个顶点都构成了一个顶点绕序。在渲染的时候,OpenGL会用这些信息来判断这是正面还是背面。默认情况下,顶点是逆时针顺序的会被认为是正面。
在我们上一章的代码中使用glEnable(GL_CULL_FACE)开启背面剔除,你会发现正面看窗户是可以看到的,背面看窗户就看不到了。分析transparentVertices顶点数组之后你就可以明白这顶点绕序是怎么回事了。
从这里就可以看出,当我们定义顶点的时候,还需要考虑其顺序问题。当所有的顶点都被正确定义后(立方体背面的顶点需要顺时针定义),我们观察立方体的时候就会是这样子:
我们可以看到的三角形是逆时针绕序,会被渲染。后面的三角形是顺时针绕序,会被丢弃。而当我们转过去的时候,后面的三角形又会变成逆时针绕序,前面的会变成顺时针绕序,这正是我们想要的!
如何使用背面剔除
细心的你肯定已经发现,我们已经尝试过使用背面剔除功能了。没错,就是使用一脉相承的glEnable函数,传入的参数是GL_CULL_FACE。在启用背面剔除之前,我们先要确保立方体的顶点绕序是逆时针的。很遗憾,之前我们一直用的立方体顶点绕序不全都是逆时针绕序的。你可以在这里获得一份逆时针绕序的顶点数据。
快使用这份顶点数据,然后开启背面剔除看看效果。笔者看到的效果是这样的:
从背面看过去就很明显可以看出背面剔除已经启用,因为我们看不到那些窗户了。还有很明显的地方就是地板不见了,因为我们现在看到的也是地板的背面,如果将调整视角从下往上看就可以看到地板了。
这样就够了吗?当然不是。OpenGL还提供了一些控制背面剔除效果的函数,甚至连前面也可以剔除掉。调用glCullFace(GL_FRONT);就可以剔除正面。效果是这样:
可以作为glCullFace参数的选项有:GL_FRONT,GL_BACK和GL_FRONT_AND_BACK。各个参数使用效果都能从参数名字上看出来,这里不再多说了。
要注意,剔除功能只影响多边形,而对点和直线无影响。例如,使用glCullFace(GL_FRONT_AND_BACK)后,所有的多边形都将被剔除,所以看见的就只有点和直线。
还有一个函数:glFrontFace()。用来设置哪个面是正面。默认参数为GL_CCW表示逆时针为正面,你也可以设置成GL_CW表示顺时针为正面。一般我们不需要去修改它。
处于研究的目的,我们将顺时针设置成正面,剔除背面,看看会有什么效果:
和前面的效果一样,这就对了。由于本文代码比较简单,不单独提供代码下载了,大家在前面的代码上修改就好了。
总结
这么简单,有啥好总结的,就是一个顶点绕序,一个glEnable,一个glCullFace,一个glFrontFace而已。不过千万别小看背面剔除,所有的游戏都会使用到,尤其是大型游戏中。想想看,精细人物模型面片数在1万以上,电影级别的面片数10万以上,你说背面剔除不重要,呵呵?
参考资料
www.learnopengl.com(非常好的一个网站,建议学习)