实践项目:https://github.com/JackieLong/OpenGL/tree/main/project_culling_test
需求场景
从立方体的外部任意角度观察,只能看到最多3个面,完全可以避免绘制其他的面,提升渲染效率。
一个闭合的形状,它的每一个面都有两侧,每一侧要么面向摄像机(眼睛、观察者),要么背对摄像机。OpenGL通过面剔除(Face Culling)来实现只绘制面向观察者的面。
OpenGL能够检查所有面向(Front Facing)观察者的面,并渲染它们,而丢弃那些背向(Back Facing)的面,节省我们很多的片段着色器调用(它们的开销很大!)。但我们仍要告诉OpenGL哪些面是正向面(Front Face),哪些面是背向面(Back Face)。OpenGL使用了一个很聪明的技巧,分析顶点数据的环绕顺序(Winding Order)。
环绕顺序
在程序代码中,我们会按一定顺序定义如下图两个三角形:
// 顶点的定义顺序就是顶点的绘制顺序
const GLfloat vertices[] = {
// 三角形(左),顶点绘制顺序:1->2->3,顺时针(clock-wise,CW)
vertices[0], // 顶点1
vertices[1], // 顶点2
vertices[2], // 顶点3
// 三角形(右),顶点绘制顺序:1->3->2,逆时针(counter clock-wise,CCW)
vertices[0], // 顶点1
vertices[2], // 顶点3
vertices[1] // 顶点2
};
相关API
glEnable(GL_CULL_FACE); // 开启面剔除,默认不开启
glFrontFace(mode); // 定义“Front Face”
// mode的可能取值如下:
// GL_CW: 顶点绘制顺序是顺指针的面是“Front Face”
// GL_CCW: 顶点绘制顺序是逆指针的面是“Front Face”,默认值
glCullFace(mode); // 剔除朝向摄像机的面。
// GL_BACK: 剔除朝向摄像机的背面,默认值
// GL_FRONT: 剔除朝向摄像的正面
// GL_FRONT_AND_BACK: 都剔除。
具体如何调用,由三角形顶点数据的定义顺序决定:
// 按顺时针定义立方体的六个面的正面(朝外的面)
float cubeVertices[] = {
// Back face
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // Bottom-left
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // bottom-right
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // bottom-left
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
// Front face
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // top-left
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
// Left face
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-left
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right
// Right face
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
// Bottom face
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, // top-left
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right
// Top face
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f // bottom-left
};
// 此时,朝外的面都是顺时针绘制,我们可以如下调用:
glEnable(GL_CULLING);
glFrontFace(GL_CW);
glCullFace(GL_BACK);
// 或者
glEnable(GL_CULLING);
glFrontFace(GL_CCW);
glCullFace(GL_FRONT);