上一篇文章我们讲到了使用深度测试可以解决隐藏面消除的问题。并且效果看起来很完美,但是深度测试真的就没有一点问题吗?当然不是,接下里我们就来看下深度测试的问题:
一、深度测试的潜在风险: Z-fighting
(Z冲突、闪烁)
1.为什么会出现Z-fighting
:
因为开启深度测试后,OpenGL
就不会再去绘制模型被遮挡的部分。这样实现的显示更加真实,但是由于深度缓冲区精度的限制对于深度相差非常小的情况下(例如在同⼀一平⾯上进行2次绘制),OpenGL
就可能出现不能正确判断两者的深度值,会导致深度测试的结果不可预测。显示出来的现象是交错闪烁,即前面2个画面交错出现。
同一个位置上出现的图层切深度出现精确度很低时,就会容易引起
Z-fighting
现象。表示两个物体考的非常近,无法确定谁在钱,谁在后,从而出现显示歧义。
2.如何解决Z-fighting
为了解决Z-fighting,OpenGL提供一种方法--多边形偏移(Polygon Offset
):
让深度值之间产⽣间隔。如果2个图形之间有间隔,是不是意味着就不会产⽣⼲涉.可以理解为在执行深度测试前将⽴方体的深度值做⼀些细微的增加。于是就能将叠加的2个图形深度值在绘制前有所区分。
- 1.启用
Polygon Offset
:
/启⽤用Polygon Offset ⽅方式
glEnable(GL_POLYGON_OFFSET_FILL)
//参数列表:
GL_POLYGON_OFFSET_POINT 对应光栅化模式: GL_POINT
GL_POLYGON_OFFSET_LINE 对应光栅化模式: GL_LINE
GL_POLYGON_OFFSET_FILL 对应光栅化模式: GL_FILL
- 指定偏移量:
void glPolygonOffset(Glfloat factor,Glfloat units);
应⽤用到⽚片段上总偏移计算⽅方程式:
Depth Offset = (DZ * factor) + (r * units); DZ:深度值(Z值)
r:使得深度缓冲区产⽣变化的最小值。
负值,将使得z值距离我们更近,⽽正值,将使得z值距离我们更远。
factor
和units
一般设置为-1,-1即可
- 关闭
Polygon Offset
glDisable(GL_POLYGON_OFFSET_FILL)
至于为什么要关闭的原因,大家应该还有印象吧,OpenGL
执行的时候是一个巨大的状态机,所以开启多边形偏移后,设置完偏移量后,必须关闭。否者会对后面的绘制产生影响。
3.如何防止Z-fighting
产生呢?
- 避免两个物体靠的太近:在绘制时,插入一个小偏移;
- 将近裁剪面(设置透视投影时设置)设置的离观察者远一些:提高裁剪范围内的精确度;
- 使用更高位数的深度缓冲区:提高深度缓冲区的精确度。
二、颜色混合
颜色混合.png我们把OpenGL
渲染时会把颜⾊值存在颜⾊缓存区中,每个⽚段的深度值也是放在深度缓冲区。当深度缓冲区被关闭时,新的颜⾊将简单的覆盖原来颜⾊缓存区存在的颜⾊值,当深度缓冲区再次打开时,新 的颜⾊⽚段只是当它们⽐原来的值更接近邻近的裁剪平⾯才会替换原来的颜⾊⽚段.
那么如果开启深度测试后.但是2个重叠的图层中, 有⼀个图层是半透明的. 有⼀个图层是⾮半透明的. 那么此时就不能进⾏单纯的 ⽐较深度值,然后进⾏覆盖. ⽽是需要将2个图层的颜⾊进⾏混合。
1.普通混合:
用于单纯的将两个图层重叠时进行颜色混合,这种混合并不能解决颜色的混合。在固定着色器和可编程着色器都可以使用这种方式。
固定着⾊器/可编程着⾊器-> 使⽤开关⽅式 ->颜⾊混合(单纯的2个图层重叠进⾏混合)
- 开始颜色混合:
glEnable(GL_BlEND);
- 关闭颜色混合:
glDisable(GL_BlEND);
2.使用混合因子:
有些场景系统默认的混合模式可能并不符合我们的需求,这个时候就需要我们手动设置混合因子,来实现我们所想要呈现的效果。这种混合只能在可编程着色器上使用。
可编程着⾊器->⽚元着⾊器 -> 处理图⽚原图颜⾊+薄薄的绿⾊(颜⾊值) -> 进⾏颜⾊混合⽅程式计算 -> 套⽤公式
两个概念:
-
⽬标颜⾊:已经存储在颜⾊缓存区的颜⾊值;
-
源颜⾊:作为当前渲染命令结果进⼊颜⾊缓存区的颜⾊值。
当混合功能被启动时,源颜⾊和⽬标颜⾊的组合⽅式是混合⽅程式控制的。在默认情况下,混合⽅程式如下所示:
//Cf:最终计算参数的颜⾊
//Cs:源颜⾊
//Cd:⽬标颜⾊
//S:源混合因⼦
//D:⽬标混合因⼦
Cf = (Cs * S) + (Cd * D)
//开启,
glEnable(GL_BlEND);
//设置混合因子--默认值是 GL_SRC_ALPHA 和 GL_ONE_MINUS_SRC_ALPHA
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
//关闭
glDisable(GL_BlEND);
设置混合因⼦,需要⽤到glBlendFun
函数
void glBlendFunc(GLenum S,GLenum D)
S:源混合因⼦
D:⽬标混合因⼦
修改颜⾊混合⽅程式:
默认混合⽅程式:
Cf = (Cs * S) + (Cd * D);
实际上远不⽌这⼀种混合⽅程式,我们可以从5个不同的⽅程式中进⾏选择:
混合方程式.png
选择混合⽅程式的函数glbBlendEquation
:
void glbBlendEquation(GLenum mode);
除了能使⽤glBlendFunc
来设置混合因子,还可以有更灵活的选择。
//strRGB: 源颜⾊的混合因⼦
//dstRGB: ⽬标颜色的混合因子
//strAlpha: 源颜⾊的Alpha因⼦
//dstAlpha: ⽬标颜色的Alpha因⼦
void glBlendFuncSeparate(GLenum strRGB,GLenum dstRGB ,GLenum strAlpha,GLenum dstAlpha);
颜色混合总结:
在颜色缓冲区中,每个像素点只能存储一种颜色;混合函数经常⽤于实现在其他⼀些不透明的物体前⾯绘制⼀个透明物体的效果。可简单理解为,只有当上层图像有透明度时才需要开启颜色混合,反之,上层图像不透明,下层图像被完全遮挡,则没有必要再开启颜色混合了。
颜色混合实际使用代码,在RenderSence
中开启和关闭颜色混合:
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//1.开启混合
glEnable(GL_BLEND);
//2.开启组合函数 计算混合颜色因子
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//定义颜色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//3.使用着色器管理器
//*使用 单位着色器
//参数1:简单的使用默认笛卡尔坐标系(-1,1),所有片段都应用一种颜色。GLT_SHADER_IDENTITY
//参数2:着色器颜色
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
//4.容器类开始绘制
squareBatch.Draw();
//5.关闭混合功能
glDisable(GL_BLEND);
//同步绘制命令
glutSwapBuffers();
}
觉得不错记得点赞哦!听说看完点赞的人逢考必过,逢奖必中。ღ( ´・ᴗ・` )比心
网友评论