前提背景
首先我们用OpenGL绘制这样一个立方体

我们试想一下,当我们从任意一个角度看它的时候,最多能看到几个面?
答案很明显,只有三个面。
那么继续思考,对于那些我们看不到面,我们有必要去绘制它们吗?如果我们能以某种方式去丢弃这部分数据,那么OpenGL的渲染性能将会超过50%
背面剔除
既然我们要丢弃看不到的面,那么问题来了:
如何知道哪个面看得到,哪个面看不到呢?
实际上OpenGL在处理的时候,它只允许检查所有正面朝向观察者的面,并渲染它们,而丢弃所有背向观察者的面,并以此来节省片段着色器的运行
所以接下来要做的就是,我们要告诉OpenGL哪个是正面,哪个是背面
如何判断正反面
我们在通过三角形来绘制形状时,会定义顶点连接的顺序,它可能是顺时针或逆时针

如上图,左侧三角形是顺时针方向,右侧是逆时针方向
OpenGL就是利用这个三角形的顺时针或逆时针方向来决定三角形是正面还是反面
默认情况下,逆时针的顶点连接顺序被定义为三角形的正
注:逆时针或顺时针都是相对于观察者方向的
-
当定义顶点顺序时,应该想象对应的三角形是面向你的,所以定义的三角形顶点方向应该逆时针的。
-
这样定义的好处在于三角形顶点的实际连接顺序是在光栅化阶段进行的,也就是顶点着色器运行之后,这些顶点就是以观察者视角所见的了。
对于上图,左侧三角形 1 -> 2 ->3 的连接顺序是顺时针的,这是在观察者位于屏幕前看到的,如果观察者位于屏幕后,连接顺序依旧是 1 -> 2 -> 3 ,那么就是逆时针了。
这也是为什么说,定义三角形顶点顺序时要假设三角形是面向你的,保证逆时针定义,并且可以根据观察者方向的改变,顺时针和逆时针方向会发生改变。

上图中,三角形的顶点顺序都 1 -> 2 -> 3,当我们定义这个顺序时,都是假设观察者正面向这个三角形呢,所以都是逆时针定义的。
但是从右侧眼镜处来观察,右侧三角形方向是逆时针的,左侧三角形方向是顺时针的,这就是因为对于右侧三角形来说,观察者方向和当初定义顺序时的假设方向一致,而对于左侧三角形,观察者方向就和定义顺序时的假设方向相反了,所以从反方向来看就成了顺时针了。
这样一来,在面剔除的优化下,右侧面可见,左侧面不可见了,也就是面向观察者的正面可见,反面不可见了。
为什么要逆时针定义三角形方向,但是观察时却成了顺时针了,就是因为当初定义的逆时针方向其实是和观察者方向挂钩的。
总结一下:处理背面剔除有两个需要参考的依据,一个是正背面判断
,一个是观察者位置
明白了这一点,就对于背面剔除就会更加清晰了
如何使用
OpenGL 中可以通过如下方法开启面剔除
glEnable(GL_CULL_FACE)
默认情况下,背面面剔除是关闭的
开启面剔除后,所有的背向观察者的面都会被丢弃,节省渲染性能。
另外,OpenGL 还提供了其他功能来选择要剔除的面。
public static native void glCullFace(
int mode
);
有三个模式可选:
- GL_BACK:只剔除背向面
- GL_FRONT:只剔除正向面
- GL_FRONT_AND_BACK:剔除正向面和背向面
glCullFace 的初始值是 GL_BACK
,只剔除背向面。
除了需要剔除的面之外,还可以通过调用glFrontFace
方法告诉 OpenGL 将顺时针的面(而不是逆时针的面)定义为正向面。
public static native void glFrontFace(
int mode
);
它有两个选项:
- GL_CCW:代表逆时针方向为正向面
- GL_CW:代表顺时针方向为正向面
glFrontFace 默认值是 GL_CCW
,逆时针方向为正向面。
整理如下:
// 开启表面剔除(默认背面剔除)
glEnable(GL CULL FACE);
// 关闭表面剔除(默认背面剔除)
glDisable(GL CULL FACE);
// 用户选择剔除那个面(正面/背面)
// mode参数为: GL FRONT,GL BACK,GL FRONT AND BACK ,默认GL BACK
glCullFace(GLenum mode);
// 用户指定绕序那个为正面
// mode参数为: GL CW,GL CCW,默认值:GL CCW
glFrontFace(GL enum mode);
// 例如,剔除正面实现(1)
glCullFace(GL_ BACK);
glFrontFace(GL CW);
//例如,剔除正面实现(2)
glCullFace(GL FRONT):
有几点再强调一下
-
使用面剔除可以优化渲染过程,省下超过 50 % 的片段着色器执行数。
-
使用面剔除时定义顶点要以逆时针方向定义。
-
逆时针或顺时针都是相对于观察者方向的。
最后,我觉背面剔除真的很重要,想想看所有的游戏中都会用到,尤其是大型游戏,😝~!
网友评论