android openGL ES详解——面剔除

目录

一、什么是面剔除?

1.面剔除

2.隐藏面消除

二、立方体中的正背面

1.正背面的定义

2.顶点连接顺序分析

三、面剔除常用API

四、正背面剔除功能举例

五、总结

一、什么是面剔除?

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 深度信息-优快云博客

五、总结

隐藏面消除总结:

  • 正背面消除:需要根据顶点数据顺序判断用户可见部分与隐藏面,隐藏面直接丢弃,不绘制,只绘制可见部分
  • 深度测试:可以一次性解决隐藏面消除问题,原理是不管有多少图层,只显示可见图层,剩余不可见的都丢弃

正如你所看到的那样,面剔除是OpenGL提高效率的一个强大工具,它使应用节省运算。你必须跟踪下来哪个物体可以使用面剔除,哪些不能。


 

参考文章

OpenGL_正面剔除,深度测试和Z冲突 - 简书

OpenGL--- (四)OpenGL渲染技巧:正背面剔除 - 简书

Open GL 渲染技巧-正背面剔除、深度测试、颜色混合 - 简书

OpenGL--- (四)OpenGL渲染技巧:正背面剔除 - 简书

OpenGL---(五)OpenGL渲染技巧:深度测试、多边形偏移、混合 - 简书

面剔除 - LearnOpenGL-CN

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲暇部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值