LearnOpenGL Note - Advanced OpenGL - Face culling

高级OpenGL-面剔除

Posted by Tao on Tuesday, April 12, 2022

通过本节您可以了解到包括但不限于以下概念:

  • 什么是背面剔除?
  • 如何定义面的正反
  • OpenGL中如何使用背面剔除?

1 什么是背面剔除

尝试在脑子中想象一个3D立方体,数数我们从任意方向最多能同时看到几个面。我们可以从任意位置和任意方向看向这个球体,但永远不能看到3个以上的面。所以我们为什么要浪费时间绘制我们不能看见的那3个面呢?如果我们能够以某种方式丢弃这几个看不见的面,我们能省下超过50%的片段着色器执行数!

我说的是超过50%而不是50%,因为从特定角度来看的话只能看见2个甚至是1个面。在这种情况下,我们就能省下超过50%了。

对于立方体这个面较少的几何对象,性能开销不明显,但是对于复杂的模型,开启背面剔除则能明显改善渲染性能。背面剔除,就是早点丢弃对观察者来说是背面的片元的一种方法。

2 面正反的定义

上节提到我们应乘早丢弃背面片元,那么如何确定哪个面是背面呢?OpenGL中使用顶点绕序(Winding order)来确定。

给定三角形三个顶点的顺序,三角形可以看起来具有顺时针(Clockwise)绕序或逆时针(Counter-clockwise)绕序。顺时针表示三个顶点依次绕三角形中心顺时针旋转。逆时针是指三个顶点依次绕三角形的中心逆时针旋转。

Winding_order.png

可以看到,我们首先定义了顶点1,之后我们可以选择定义顶点2或者顶点3,这将决定两个三角形不同的环绕顺序。下面的代码展示了这点:

float vertices[] = {
    // 顺时针
    vertices[0], // 顶点1
    vertices[1], // 顶点2
    vertices[2], // 顶点3
    // 逆时针
    vertices[0], // 顶点1
    vertices[2], // 顶点3
    vertices[1]  // 顶点2  
};

每组组成三角形图元的三个顶点就包含了一个环绕顺序。OpenGL在渲染图元的时候将使用这个信息来决定一个三角形是一个正向三角形还是背向三角形。默认情况下,逆时针顶点所定义的三角形将会被处理为正向三角形。

观察者所面向的所有三角形顶点就是我们所指定的正确环绕顺序了,而立方体另一面的三角形顶点则是以相反的环绕顺序所渲染的。这样的结果就是,我们所面向的三角形将会是正向三角形,而背面的三角形则是背向三角形。下面这张图显示了这个效果:

faceculling_frontback.png

左侧的三角形顶点顺序为1->2->3,右侧的三角形顶点顺序为1->2->3。当观察者在右侧时,则右边的三角形方向为逆时针方向为正面,而左侧的三角形为顺时针则为背面;当观察者转到左侧时,左侧的三角形为逆时针绕序判定为正面,而右侧的三角形为顺时针绕序判定为背面。可以看出正面和背面是由三角形的顶点定义顺序和观察者的观察方向共同决定的,而且随着观察方向的改变,正面和背面将会跟着改变。

3 OpenGL中的背面剔除

在OpenGL中背面剔除默认是禁用状态的,开启背面剔除需要使用:

glEnable(GL_CULL_FACE);

这一句代码之后,所有背面都将被丢弃。目前我们在渲染片段的时候能够节省50%以上的性能,但注意这只对像立方体这样的封闭形状有效。当我们想要绘制上一节中的草时,我们必须要再次禁用面剔除,因为它们的正面和背面都应该是可见的。

同时OpenGL提供了函数glCullFace来供用户选择剔除哪个面(比如只要求剔除正面或背面):

API void glCullFace(GLenum mode);

mode参数为 GL_FRONT, GL_BACK, GL_FRONT_AND_BACK 3个枚举类型。默认为GL_BACK.

以及glFrontFace函数用来根据绕序指定哪个为正面:

API void glFrontFace(GLenum mode);

mode参数为GL_CW ,GL_CCW。默认值是GL_CCW。

如果要剔除正面,代码:

glCullFace(GL_BACK);
glFrontFace(GL_CW);

和下面的代码等价:

glCullFace(GL_FRONT);

当开启了背面剔除时,穿过表面我们看不到立方体的背面了:

result.png

4 Reference

「如果这篇文章对你有用,请随意打赏」

Heisenberg Blog

如果这篇文章对你有用,请随意打赏

使用微信扫描二维码完成支付


comments powered by Disqus