OpenGL核心技术之面剔除

笔者介绍:姜雪伟IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

面剔除技术在游戏开发中主要目的是提高效率,在现实生活中也会经常遇到这种情况,比如我们观看一幢大厦,我们在大厦的某个位置看,只能看到大厦的一到两个面,其他的面我们是看不到的,因为被挡住了,但是这不妨碍我们看这幢大厦,在虚拟城市建模时,也会使用面剔除,城市漫游如果只是沿着道路行走,背对道路那一侧的建筑面是永远看不到的,美术可以使用一张面片代替,不需要再建复杂的面片,但是在游戏中模型的面我们不知道那个面是背对玩家的,因为在场景中模型是可以随意移动和旋转的。这就要用到游戏中的面剔除技术。

下面开始讲游戏中的面剔除技术处理,游戏一般会采用DX或者OpenGL去做渲染,以OpenGL为例,它会检查所有正面朝向(Front facing)观察者的面,并渲染它们,而丢弃所有背面朝向(Back facing)的面,这样就节约了我们很多片段着色器的命令(它们很昂贵!)。我们必须告诉OpenGL我们使用的哪个面是正面,哪个面是反面。OpenGL使用一种聪明的手段解决这个问题——分析顶点数据的连接顺序(Winding order)。

当我们定义一系列的三角顶点时,我们会把它们定义为一个特定的连接顺序(Winding Order),它们可能是顺时针的或逆时针的。每个三角形由3个顶点组成,我们从三角形的中间去看,从而把这三个顶点指定一个连接顺序。

OpenGL核心技术之面剔除_第1张图片

正如你所看到的那样,我们先定义了顶点1,接着我们定义顶点23,这个不同的选择决定了这个三角形的连接顺序。下面的代码展示出这点:

GLfloat vertices[] = {
    //顺时针
    vertices[0], // vertex 1
    vertices[1], // vertex 2
    vertices[2], // vertex 3
    // 逆时针
    vertices[0], // vertex 1
    vertices[2], // vertex 3
    vertices[1] // vertex 2
};

每三个顶点都形成了一个包含着连接顺序的基本三角形。OpenGL使用这个信息在渲染你的基本图形的时候决定这个三角形是三角形的正面还是三角形的背面。默认情况下,逆时针的顶点连接顺序被定义为三角形的正面。

当定义你的顶点顺序时,你如果定义能够看到的一个三角形,那它一定是正面朝向的,所以你定义的三角形应该是逆时针的,就像你直接面向这个三角形。把所有的顶点指定成这样是件炫酷的事,实际的顶点连接顺序是在光栅化阶段(Rasterization stage)计算的,所以当顶点着色器已经运行后。顶点就能够在观察者的观察点被看到。

我们指定了它们以后,观察者面对的所有的三角形的顶点的连接顺序都是正确的,但是现在渲染的立方体另一面的三角形的顶点的连接顺序被反转。最终,我们所面对的三角形被视为正面朝向的三角形,后部的三角形被视为背面朝向的三角形。下图展示了这个效果:

OpenGL核心技术之面剔除_第2张图片

在顶点数据中,我们定义的是两个逆时针顺序的三角形。然而,从观察者的方面看,后面的三角形是顺时针的,如果我们仍以123的顺序以观察者当面的视野看的话。即使我们以逆时针顺序定义后面的三角形,它现在还是变为顺时针。它正是我们打算剔除(丢弃)的不可见的面!

下面进入OpenGL实际代码操作,开启OpenGLGL_CULL_FACE选项就能开启面剔除功能函数:

glEnable(GL_CULL_FACE);

从这儿以后,所有的不是正面朝向的面都会被丢弃。目前,在渲染片段上我们节约了超过50%的性能,但记住这只对像立方体或者是模型这样的封闭形状有效。当我们绘制上个教程中那个草的时候,我们必须关闭面剔除,这是因为它的前、后面都必须是可见的。

OpenGL允许我们改变剔除面的类型,要是我们剔除正面而不是背面会怎样?我们可以调用glCullFace来做这件事:

glCullFace(GL_BACK);

glCullFace函数有三个可用的选项:

  • GL_BACK:只剔除背面。
  • GL_FRONT:只剔除正面。
  • GL_FRONT_AND_BACK:剔除背面和正面。

glCullFace的初始值是GL_BACK。另外,我们还可以告诉OpenGL使用顺时针而不是逆时针来表示正面,这通过glFrontFace来设置:

glFrontFace(GL_CCW);

默认值是GL_CCW,它代表逆时针,GL_CW代表顺时针顺序。

我们可以做个小实验,告诉OpenGL现在顺时针代表正面:

glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);

最后的结果只有背面被渲染了:

OpenGL核心技术之面剔除_第3张图片

要注意,你可以使用默认逆时针顺序剔除正面,来创建相同的效果:

glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);

总结:

关于面剔除操作,现在都是在Shader中加入一行命令行即可,面剔除操作是OpenGL优化的一个方面,其实现在市面上各种引擎功能都很成熟了,引擎已经为我们做了面剔除操作的事情,但是作为开发者,自己本身还是要明白其原理,这样更有助于提升自己的技能。

你可能感兴趣的:(图形学编程,3D引擎,OpenGL核心技术)