在渲染过程中可能产⽣的问题
甜甜圈案例.png我们在绘制一个红色甜甜圈进行移动的时候,会出现上图的情况,那么出现这个问题的原因是什么呢?有什么方案可以解决这个问题呢?
解决方案
在绘制3D场景时,我们需要决定哪些部分是对观察者可⻅的,或者哪些部分是不可见的。对于不可⻅的部分,应该及早丢弃。这种情况叫做”隐藏⾯消除”(Hidden surfaceelimination).
解决方案:油画算法
先绘制场景中的离观察者较远的物体,再绘制较近的物体。
例如下面的图例:先绘制红色部分,再绘制⻩色部分,最后再绘制灰⾊部分,即可解决隐藏面消除的问题。即将场景按照物理距离和观察者的距离远近排序,由远及近的绘制即可。
但是在三个三角形是叠加的情况下,油画算法将⽆法处理.
油画算法的弊端.png
解决方案:正背⾯剔除(Face Culling)
我们可以思考这个问题:尝试观察一个3D图形,你从任何一个⽅向去观察,最多可以看到几个面? 我们为何要多余的去绘制那根本看不到的面?
任何平⾯都有2个⾯:正⾯和背面,意味着我们一次只能看到一面。通过分析顶点数据的顺序,OpenGL可以做到检查所有正面朝向观察者的面,并渲染它们;从⽽丢弃背面朝向的面。
分析顶点数据
- 正/背面的区分:
- 正⾯:按照逆时针顶点连接顺序的三⻆形⾯
- 背⾯:按照顺时针顶点连接顺序的三角形⾯
分析立方体中的正背面
- 分析
- 左侧三角形顶点顺序为: 1—> 2—> 3 ; 右侧三角形的顶点顺序为: 1—> 2—> 3.
- 当观察者在右侧时,则右边的三角形方向为逆时针方向则为正面,⽽左侧的三角形为顺时针则为背面
- 当观察者在左侧时,则左边的三角形为逆时针方向判定为正面,而右侧的三角形为顺时针判定为背面
- 总结
- 正面和背面是有三角形的顶点定义顺序和观察者方向共同决定的.随着观察者的角度方向的改变,正面背面也会跟着改变
//开启表面剔除(默认背面剔除)
glEnable(GL_CULL_FACE);
//关闭表面剔除(默认背面剔除)
glDisable(GL_CULL_FACE);
//选择剔除那个面(正面/背面)
// mode参数为: GL_FRONT, GL_BACK, GL_FRONT_AND_BACK,默认GL_BACK
glCullFace(GLenum mode);
//用户指定绕序那个为正面
//mode参数为: GL_CW, GL_CCW,默认值:GL_CCW
glFrontFace(GL enum mode);
//剔除正面实现①
glCullFace(GL_BACK);
glFrontFace(GL_CW);
//剔除正面实现②
glCullFace(GL_FRONT);
glFrontFace(GL_CCW);
弊端
如果前后两个点都是正面或是背面,这时OpenGL无法区分哪个面在前,哪个面在后,就可能出现下图所示的问题。
解决方案:深度缓冲区
-
深度
- 深度,就是像素点在3D世界中距离摄像机的距离,即Z值。
-
深度缓冲区
- 深度缓存区,就是⼀块内存区域,专门存储每个像素点的深度值。深度值(Z值)越⼤,则离摄像机就越远。
-
为什么需要深度缓冲区?
- 在不使⽤深度测试的时候,如果先绘制⼀个⽐较近的物体,再绘制较远的物体。较远的图像就会像油画一样覆盖掉之前的图像。有了深度缓冲区后,绘制物体的顺序就不那么重要了。只要通过开启了深度缓冲区,并允许深度值的写入,OpenGL都会把像素的深度值写入到缓冲区中。
-
深度测试
- 深度缓冲区和颜⾊缓存区是对应的。颜⾊缓存区存储像素的颜⾊信息,而深度缓冲区存储像素的深度信息。在决定是否绘制⼀个物体表⾯时,首先要将表面对应的像素的深度值与当前深度缓冲区中的值进⾏⽐较。如果大于深度缓冲区中的值,则丢弃这部分;否则利⽤这个像素对应的深度值和颜⾊值,分别更新深度缓冲区和颜色缓存区。这个过程称为深度测试。
-
深度值的计算
- 深度值一般由16位,24位或者32位值表示,通常是24位。位数越高的话,深度的精确度越好。深度值的范围在[0,1]之间,值越小表示越靠近观察者,值越大表示远离观察者。
- 深度缓冲主要是通过计算深度值来比较⼤小,在深度缓冲区中包含深度值介于0.0和1.0之间, 从观察者看到其内容与场景中的所有对象的 z 值进行了⽐较。这些视图空间中的 z 值可以在投影平头截体的近平面和远平面之间的任何值。我们因此需要一些方法来转换这些视图空间 z 值 到 [0,1] 的范围内。
深度值的使用
开启深度测试
glEnable(GL_DEPTH_TEST);
在绘制场景前,清除颜⾊缓存区,深度缓冲。清除深度缓冲区默认值为1.0。
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
指定深度测试判断模式
void glDepthFunc(GLEnum mode);
深度缓冲区写入开关
//value: GL_TURE,开启深度缓冲区写入; GL_FALSE,关闭深度缓冲区写⼊
void glDepthMask(GLBool value);
总结
使⽤正⾯/背面剔除法和深度测试法解决了OpenGL的渲染效率问题。效果图如下:
网友评论