在上一篇文章 https://www.jianshu.com/p/6df57b02ab78 中我们最后遗留了一个问题,甜甜圈旋转时有一个缺口,如下图:
旋转中的甜甜圈-缺口.png
上面这种情况出现的原因是什么呢?
- 在甜甜圈旋转的过程中,当前后两部分重叠时,对于我们而言,需要展示的是前面的部分,后面部分是隐藏面,但是OpenGL并不能清楚的区分哪个面在前哪个面在后,因此出现了缺口。
那么如何解决这种问题呢?
- 利用深度测试解决。
先来了解一下下面的这些概念:
深度:
深度其实就是该像素点在3D世界中距离摄像机的距离,Z值。
深度缓冲区:
深度缓冲区就是一块内存区域,专门存储着每个像素点(绘制在屏幕上的)深度值。
(如果观察者在Z轴正方向)深度值(z值)越大,越靠近观察者;
(如果观察者在Z轴反方向)深度值(z值)越小,越靠近观察者。
为什么需要深度缓冲区?
在不使⽤深度测试的时候,如果我们先绘制⼀个距离⽐较近的物体,再绘制距离较远的物体,则距离远的位图因为后绘制,会把距离近的物体覆盖掉。有了深度缓冲区后,绘制物体的顺序就不那么重要的。
实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中. 除非调⽤glDepthMask(GL_FALSE).来禁⽌写入.
深度缓存区的默认值为1.0,表示最大的深度值,深度值的范围是[0,1]。
深度测试:
在绘制一个物体表面时,首先要将表面对应的像素的深度值与当前深度缓冲区中的值进行比较,如果大于深度缓冲区中的值,就丢弃这部分,否则就利用这个像素对应的颜色值和像素值,分别更新颜色缓冲区和深度缓冲区。这个过程就叫“深度测试”。
那么针对上面甜甜圈缺口的问题,我们就需要使用深度测试来解决,在甜甜圈绘制之前,开启深度测试 glEnable(GL_DEPTH_TEST),用于新旧深度值的对比,决定像素点是绘制还是丢弃,所以在之前代码的基础上,主要需要修改RenderScene函数,流程如下:
RenderScene.png当然,开启即是整个程序开启,那么绘制完毕后,就要将其关闭glDisable(GL_DEPTH_TEST);
深度测试能解决什么问题呢?
1、当甜甜圈旋转时,2个部分重叠时,此时OpenGL不能清楚的分辨出哪个图层在前,哪个图层在后,则会出现甜甜圈缺口现象。
2、影藏面消除,除了使用正面剔除,还可以使用深度测试来解决。
隐藏面消除总结:
- 正背面消除:需要根据顶点数据顺序判断用户可见部分与隐藏面,隐藏面直接丢弃,不绘制,只绘制可见部分
- 深度测试:可以一次性解决隐藏面消除问题,原理是不管有多少图层,只显示可见图层,剩余不可见的都丢弃
多边形偏移
开启深度测试后,由于深度缓冲区精度有限制,导致深度值在误差极小时,OpenGL出现无法判断的情况,导致出现画面交错闪现的现象,例如下图:
Z-Fighting(Z冲突,闪烁)问题.png
上图所示的情况,就是Z-Fighting(Z冲突,闪烁)问题,其问题产生的主要原因是由于图形靠的太近,导致无法区分出图层先后次序,针对该问题,OpenGL提供了一种多边形偏移(Polygon Offset)方案。
使用多边形偏移,分为3个步骤:
1、在绘制前,开启多边形偏移
- 解决方法:让深度值之间产生间隔,如果两个图形之间有间隔,就不会产生干涉,可以理解为在执行深度测试前将立方体的深度值做一些细微的增加,于是能将重叠的两个图形深度值之间有所区分。
glEnable(GL_POLYGON_OFFSET_FILL)
2、指定偏移量
glPolygonOffset (GLfloat factor, GLfloat units);,参数一般填 -1 和 -1
每个Fragment的深度值都会增加如下所示的偏移量:
offset = (m * factor) + (r * units)
m:多边形的深度的斜率的最大值,一个多边型越是与近裁剪面平行,m就越接近于0。
r:能产生于窗口坐标系的深度值中可分辨的差异最小值。r是由具体的OpenGL平台指定的一个常量。
注:一个大于0的offset会把模型推到离你(摄像机)更远的位置,相应的一个小于0的offset会把模型拉近。
3、关闭多边形偏移
glDisable(GL_POLYGON_OFFSET_FILL)
如何预防ZFighting闪烁问题?
1、避免两个物体靠的太近:在绘制时,插入一个小偏移
2、将近裁剪面(设置透视投影时设置)设置的离观察者远一些:提高裁剪范围内的精确度
3、使用更高位数的深度缓冲区:提高深度缓冲区的精确度
混合
当开启深度测试后,两个重叠的图层中,如果有一个图层是半透明的,另一个是非半透明,此时就不能通过深度值比较,来进行颜色值的覆盖,而是需要将两个颜色进行混合,然后存入颜色缓冲区。
使用
针对不同需求,颜色混合的使用方式有两种
- 开关方式
用于单纯的将两个图层重叠时进行颜色混合,这种混合并不能解决颜色的混合。在固定着色器和可编程着色器都可以使用这种方式。
//开启,
glEnable(GL_BlEND);
//关闭
glDisable(GL_BlEND);
- 开关方式 + 混合方程式
用于处理类似滤镜效果的场景,简单描述就是将需要处理的图片颜色和图片上覆盖的半透明颜色进行混合 即 两股颜色混合,此时如果只是单纯的开关方式,已经不能满足我们的需求,需要借助混合方程式,来实现两股颜色的混合。一般是在可编程着色器中片元着色器中使用。
//开启,
glEnable(GL_BlEND);
//设置混合因子--默认值是 GL_SRC_ALPHA 和 GL_ONE_MINUS_SRC_ALPHA
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
//关闭
glDisable(GL_BlEND);
在glBlendFunc方法中,是通过混合方程式来得到颜色的组合,默认情况下混合方程式如下所示:
//Cf -- 最终组合的颜色值
//Cd:源颜色 -- 当前渲染命令传入的颜色值
//CS:目标颜色 -- 颜色缓冲区中已经存在的颜色值
//S:源混合因子
//D:目标混合因子
Cf = (Cs * S) + (Cd * D)
在混合方程中,新颜色的alpha值越高,添加的新颜色成分就越高,旧颜色值就保留的越少
总结:
- 在颜色缓冲区中,每个像素点只能存储一种颜色
- 颜色混合主要用于实现在不透明物体前绘制透明物体的效果
- 只有上面图层是透明时,才需要开启颜色混合,如果不是,则没有必要开启颜色混合
网友评论