一、什么是面剔除?
1.面剔除
尝试在头脑中想象一下有一个3D立方体,你从任何一个方向去看它,最多可以同时看到多少个面。如果你的想象力不是过于丰富,你终极最多能数出来的面是3个。你可以从一个立方体的恣意位置和方向上去看它,但是你永久不能看到多于3个面。所以我们为何还要去绘制那三个不会显示出来的3个面呢。如果我们可以以某种方式丢弃它们,我们会进步片段着色器超过50%的性能!
正反面剔除方案,是OpenGL中针对图形绘制的一种本领,紧张用于处理立体图形绘制时,只绘制观察者能看到的部分,看不到的部分就丢弃不绘制,这种做法可以将渲染性能进步50%左右。我们所说的是超过50%而不是50%,因为从一个角度只有2个或1个面可以或许被看到。这种情况下我们就可以或许进步50%以上性能了。
这简直是个好主意,但是有个问题必要解决:我们怎样知道某个面在观察者的视野中不会出现呢?如果我们去想象任何封闭的多少平面,它们都有两面,一面面向用户,另一面背对用户。如果我们只渲染面向观察者的面会怎样?
这正是面剔除(Face culling)所要做的。OpenGL答应检查所有正面朝向(Front facing)观察者的面,并渲染它们,而丢弃所有反面朝向(Back facing)的面,如许就节约了我们很多片段着色器的命令(它们很昂贵!)。我们必须告诉OpenGL我们使用的哪个面是正面,哪个面是反面。OpenGL使用一种智慧的手段解决这个问题——分析顶点数据的连接顺序(Winding order)。
2.隐藏面消除
任何一个3D的物体,比如立方体,球,多边形多面立方体,等等,就像地球有昼夜一样,太阳光永久只能照射在地球的一个面上,在任何情况下我们都只能看到其中的一个面,那么对于看不到的反面在计算机里图形学内里如果不被渲染,那么性能会进步50%,即使渲染了也不看不到,而且性能降落,所以,完全没有必要。在绘制3D场景的时间,我们必要决定哪些部分是对观察者 可见的,或者哪些部分是对观察者不可⻅的.对于不可见的部分,应该及早丢弃.比方在⼀个不透明的墙壁后,就不应该渲染.这种对于看不到的面不去渲染的情况叫做“隐藏面消除”
二、立方体中的正反面
任何物体都有两面性,正面和反面,而观察者只能看到一个面.OpenGL可以通过分析顶点数据的顺序检测到面向观察者的面从而渲染他们,丢弃反面的渲染.如许可与节约片元着色器的性能.
1.正反面的定义
- 正面:按照逆时针顶点连接顺序的三角形面
- 反面:按照顺时针顶点连接顺序的三角形面
2.顶点连接顺序分析
- 左侧三角形顶点顺序为: 1—> 2—> 3 ; 右侧三角形的顶点顺序为: 1—> 2—> 3 - 当观察者在右侧时,则右侧的三角形方向为逆时针方向则为正面,左侧的三角形为顺时针为反面
- 当观察者在左侧时,则左侧的三角形方向为逆时针方向为正面,右侧的三角形为顺时针为反面
- 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
- };
复制代码
正面和反面是由三角形的顶点定义顺序和观察者的方向共同决定的,随着观察者的角度方向改变,正面反面也会跟着改变。观察者的角度很好理解,站在不同的角度方位看一个物体,能看到的一面是正面,看不到的是反面。现实的顶点连接顺序是在光栅化阶段(Rasterization stage)计算的,所以当顶点着色器已经运行后。顶点就可以或许在观察者的观察点被看到。把所有三角的顶点都定义为逆时针是一个很好的习惯。
三、面剔除常用API
- void glEnable(GL_CULL_FACE)
复制代码 从这儿以后,所有的不是正面朝向的面都会被丢弃(尝试飞入立方体看看,内里什么面都看不见了)。目前,在渲染片段上我们节约了超过50%的性能,但记住这只对像立方体如许的封闭外形有用。当我们绘制上个教程中那个草的时间,我们必须关闭面剔除,这是因为它的前、背面都必须是可见的。
- void glDisable(GL_CULL_FACE)
复制代码
OpenGL答应我们改变剔除面的类型。要是我们剔除正面而不是反面会怎样?我们可以调用glCullFace来做这件事:
- void glCullFace(<#GLenum mode#>)
- glCullFace函数有三个可用的选项:
- GL_BACK:只剔除背面(默认值)。
- GL_FRONT:只剔除正面。
- GL_FRONT_AND_BACK:剔除背面和正面。
复制代码
另外,我们还可以告诉OpenGL使用顺时针而不是逆时针来表示正面,这通过glFrontFace来设置:
- glFrontFace(GLenum mode)
- glFrontFace(GL_CCW); // 逆时针为正面(默认值)
- glFrontFace(GL_CW); // 顺时针为正面
复制代码
- 我们可以做个小实行,告诉OpenGL如今顺时针代表正面:
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- glFrontFace(GL_CW);
- 或者
- glEnable(GL_CULL_FACE);
- glCullFace(GL_FRONT);
- 上述两段代码功能相同
复制代码
具体实现代码如下
- //例如,剔除背⾯实现(1)
- glCullFace(GL_BACK);
- glFrontFace(GL_CW);
- //例如,剔除正⾯实现(2)
- glCullFace(GL_FRONT);
- //以下两行是默认的,可以不写
- glFrontFace(GL_CCW);
- glCullFace(GL_BACK);
复制代码 剔除正、反面举比方下:
- // 剔除背面方式1 - 正常情况下使用默认的情况即可
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- //剔除背面方式2
- glEnable(GL_CULL_FACE);
- glFrontFace(GL_CW);
- glCullFace(GL_FRONT);
- //剔除正面方式1
- glEnable(GL_CULL_FACE);
- glCullFace(GL_FRONT);
- //剔除正面方式2
- glEnable(GL_CULL_FACE);
- glFrontFace(GL_CW);
- glCullFace(GL_BACK);
复制代码 四、正反面剔除功能举例
绘制甜甜圈模子,旋转甜甜圈,会出现如下情况:
旋转甜甜圈又出现如下问题:
在甜甜圈旋转的过程中,当前后两部分重叠时,对于我们而言,必要展示的是前面的部分,背面部分是隐藏面,但是OpenGL并不能清楚的区分哪个面在前哪个面在后,因此出现了缺口。
利用深度测试解决。深度测试参考文章:android openGL ES详解——深度缓冲区_opengl 深度信息-CSDN博客
五、总结
隐藏面消除总结:
- 正反面消除:必要根据顶点数据顺序判断用户可见部分与隐藏面,隐藏面直接丢弃,不绘制,只绘制可见部分
- 深度测试:可以一次性解决隐藏面消除问题,原理是不管有多少图层,只显示可见图层,剩余不可见的都丢弃
正如你所看到的那样,面剔除是OpenGL进步服从的一个强大工具,它使应用节流运算。你必须跟踪下来哪个物体可以使用面剔除,哪些不能。
参考文章
OpenGL_正面剔除,深度测试和Z辩论 - 简书
OpenGL--- (四)OpenGL渲染本领:正反面剔除 - 简书
Open GL 渲染本领-正反面剔除、深度测试、颜色混合 - 简书
OpenGL--- (四)OpenGL渲染本领:正反面剔除 - 简书
OpenGL---(五)OpenGL渲染本领:深度测试、多边形偏移、混合 - 简书
面剔除 - LearnOpenGL-CN
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |