【安卓OpenGLES】 开发入门(八):渲染优化之面剔除

3,843 阅读4分钟

本文正在参加「金石计划」

什么是面剔除?

想象下我们在绘制3D立方体时,在同一个角度你可以看到几个面呢?如下:

image-20230411162803160

上面图中,我们最多可以看到3个面,另外3个面是在背面的,也就是无法看到,那既然是看不到的,为什么我们还要去绘制呢?要知道绘制过程中,着色器的使用是很消耗资源的,于是OpenGL给我们提供了一个机制用来屏蔽那些看不到的面,简称:面剔除。

剔除不需要绘制的面可以大大提高我们的绘制性能。

虽然我们可以直观的看到哪个面是可以观察到的,但是对于计算机来说,它是不知道的,那怎么样让计算机知道哪个是背面哪个是可视面呢

这正是面剔除要做的事:OpenGL允许检查所有正面朝向观察者的面,并渲染它们,并丢弃所有背面朝向的面,OpenGL使用了一个很巧的方法来识别当前面是否是背面:分析顶点数据的连接顺序

顶点连接顺序

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

img

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

一般在选择顶点数据的时候,把顶点数据的连接顺序定义为逆时针是一个很好的习惯,因为只要开启面剔除在默认情况下就可以剔除那些不需要绘制的面。

特别注意:从观察者的角度看正面和背面的顶点数据顺序是相反的,即正面如果是逆时针,那么从观察者的角度,背面就要设计为顺时针

img

下面我们来看下面剔除具体是如何实现的:

面剔除实践

我们前面说过我们需要将我们所有的三角形顶点数据都要以逆时针摆放,为了实现面剔除效果,我们先来纠正我们的前面讲解的立方体的顶点顺序。

逆时针顶点数组顺序

float vertices[] = {
        // positions          // normals           // texture coords
        //back face
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,//bottom-left
        -0.5f, 0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 0.0f,//top-left
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,//top-right
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,//top-right
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 1.0f,//bottom-right
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,//bottom-left
        //front face
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,//bottom-left
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 0.0f,//bottom-right
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,//top-right
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,//top-right
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 1.0f,//top-left
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,//bottom-left
        //left face
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,//top-right
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 1.0f,//top-left
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,//bottom-left
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,//bottom-left
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 0.0f,//bottom-right
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,//top-right
        //right face
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,//top-left
        0.5f,  -0.5f, 0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f,//bottom-left
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,//bottom-right
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,//bottom-right
        0.5f, 0.5f,  -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f,//top-right
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,//top-left
        //bottom face
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
        //top face
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f,//top-left
        -0.5f,  0.5f, 0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 1.0f,//bottom-left
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,//bottom-right
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,//bottom-right
        0.5f,  0.5f,  -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f,//top-right
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f//top-left
};
  • 1.下一步我们就是要开启面剔除:开启OpenGL的GL_CULL_FACE选项就能开启面剔除功能

    glEnable(GL_CULL_FACE);
    

从现在开始,所有观察者看不到的背面都会被剔除。

  • 2.那如果我们需要剔除正面而非背面,怎么办?OpenGL给我们提供了下面api来解决这个问题:

    glCullFace(GL_BACK);
    

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

    Type意义
    GL_BACK只剔除背面
    GL_FRONT只剔除正面
    GL_FRONT_AND_BACK剔除背面和正面

    默认值为GL_BACK,剔除背面

  • 3.我们还可以通过glFrontFace来告诉OpenGL是顺时针为正面还是逆时针为正面

    glFrontFace(GL_CCW);
    

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

下面我们尝试把顺时针定义为正面,剔除背面:

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

看下效果

image-20230411172830100

是的,你看到的是背面本应该被隐藏的面,由于我们的顶点数组是以逆时针定义的,由于将OpenGL的默认规则改为了顺时针为正面,导致我们只看到背面

下面我们将逆时针改为正面试试看

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

效果

video-to-gif output image

可以看到此时正常显示了正面,且将背面进行了隐藏,达到了优化渲染的效果

完整代码已经贴到github上了,大家可以自行下载查看。我是小余,我们下期见》》》

参考

learnopengl.com/Advanced-Op…