一 为什么要面剔除
尝试在脑子中想象一个3D立方体,数数你从任意方向最多能同时看到几个面。如果你的想象力不是过于丰富了,你应该能得出最大的面数是3。
你可以从任意位置和任意方向看向这个球体,但你永远不能看到3个以上的面。所以我们为什么要浪费时间绘制我们不能看见的那3个面呢?如果我们能够以某种方式丢弃这几个看不见的面,我们能省下超过50%的片段着色器执行数!
我说的是超过50%而不是50%,因为从特定角度来看的话只能看见2个甚至是1个面。在这种情况下,我们就能省下超过50%了。
二 实现思路
这是一个很好的主意,但我们仍有一个问题需要解决:我们如何知道一个物体的某一个面不能从观察者视角看到呢?
如果我们想象任何一个闭合形状,它的每一个面都有两侧,每一侧要么面向用户,要么背对用户。如果我们能够只绘制面向观察者的面呢?
这正是面剔除(Face Culling)所做的。OpenGL能够检查所有面向(Front Facing)观察者的面,并渲染它们,而丢弃那些背向(Back Facing)的面,节省我们很多的片段着色器调用(它们的开销很大!)。但我们仍要告诉OpenGL哪些面是正向面(Front Face),哪些面是背向面(Back Face)。OpenGL使用了一个很聪明的技巧,分析顶点数据的环绕顺序(Winding Order)。
三 图元顶点围绕顺序
当我们定义一组三角形顶点时,我们会以特定的环绕顺序来定义它们,可能是顺时针(Clockwise)的,也可能是逆时针(Counter-clockwise)的。每个三角形由3个顶点所组成,我们会从三角形中间来看,为这3个顶点设定一个环绕顺序。
OpenGL在渲染图元的时候将使用这个信息来决定一个三角形是一个正向三角形还是背向三角形。默认情况下,逆时针顶点所定义的三角形将会被处理为正向三角形。
facesIndexs <<v1<<v2<<v3;facesIndexs <<v3<<v2<<v1;
当你定义顶点顺序的时候&#xff0c;你应该想象对应的三角形是面向你的&#xff0c;所以你定义的三角形从正面看去应该是逆时针的。这样定义顶点很棒的一点是&#xff0c;实际的环绕顺序是在光栅化阶段进行的&#xff0c;也就是顶点着色器运行之后。这些顶点就是从观察者视角所见的了。
四 面剔除
在开头我们就说过&#xff0c;OpenGL能够丢弃那些渲染为背向三角形的三角形图元。既然已经知道如何设置顶点的环绕顺序了&#xff0c;我们就可以使用OpenGL的面剔除选项了&#xff0c;它默认是禁用状态的。
在之前教程中使用的立方体顶点数据并不是按照逆时针环绕顺序定义的&#xff0c;所以我更新了顶点数据&#xff0c;来反映逆时针的环绕顺序&#xff0c;你可以从这里复制它们。尝试想象这些顶点&#xff0c;确认在每个三角形中它们都是以逆时针定义的&#xff0c;这是一个很好的习惯。
要想启用面剔除&#xff0c;我们只需要启用OpenGL的GL_CULL_FACE选项&#xff1a;
glEnable(GL_CULL_FACE);
OpenGL允许我们改变需要剔除的面的类型。如果我们只想剔除正向面而不是背向面会怎么样&#xff1f;我们可以调用glCullFace来定义这一行为&#xff1a;
glCullFace(GL_FRONT);
glCullFace函数参数有三个可用的选项&#xff1a;
- GL_BACK&#xff1a;只剔除背向面。
- GL_FRONT&#xff1a;只剔除正向面。
- GL_FRONT_AND_BACK&#xff1a;剔除正向面和背向面。
默认值是GL_CCW&#xff0c;它代表的是逆时针的环绕顺序&#xff0c;另一个选项是GL_CW&#xff0c;它&#xff08;显然&#xff09;代表的是顺时针顺序。
五 实验测试
下面做一个实验&#xff0c;告诉OpenGL现在顺时针顺序代表的是正向面&#xff1a;
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);
这样的结果是只有背向面被渲染&#xff1a;
六 实际中的问题
下面就是之前我导入3D模型数据的时候&#xff0c;顶点数据顺序搞错而导致的问题。
因为有的数据是使用4个顶点表示&#xff0c;这个时候你需要拆分为两个三角形输入opengl&#xff0c;于是第二个三角形数据输入的时候顺序输反了&#xff0c;从而导致以下问题。
图一是正确的顺序&#xff0c;所有面都可见&#xff0c;图二是有一半的三角形顶点数据反了&#xff0c;从而导致有显示问题。
参考文章&#xff1a;https://www.pianshen.com/article/9137381011/