![](https://img.haomeiwen.com/i18597282/9eb053368c156dd7.gif)
甜甜圈旋转两部分重叠时,此时OpenGL不能区分哪个图层在前,哪个图层在后就会出现缺少一部分的现象。解决此问题需了解新概念--深度测试。
1、深度测试
- 深度:就是该像素点在3D世界中距离摄像机的距离,(像素点z坐标距离观察者的距离)z值的绝对值。
-
深度缓冲区:一块内存区域(显存中),专门存储着每个像素点(绘制在屏幕上的)深度值。深度值越大则离观察者越远。
观察者在z轴的正方向,z值越大越大则靠近观察者
观察者在z轴的负方向,z值越小越大则靠近观察者 - 深度缓存区原理:就是把距离观察者平面的深度值与窗口中每个像素点1对1进行关联及存储。(120 * 120=120 * 120个深度存储值。120 屏幕像素)
-
深度缓冲区的必要性
在不使用深度测试的时候,如果先绘制⼀个距离比较近的物体,再绘制距离较远的物体,则距离远的位图因为后绘制,会把距离近的物体覆盖掉。 有了深度缓冲区后,绘制物体的顺序就不那么重要了。 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写入到缓冲区中。 除非调用 glDepthMask(GL_FALSE)来禁⽌写入。 -
深度测试
深度缓冲区(DepthBuffer)和颜色缓存区(colorBuffer)是对应的。颜色缓存区存储像素的颜色信息,而深度缓冲区存储像素的深度信息。在决定是否绘制⼀个物体表面时,首先要将表⾯面对应的 像素的深度值 与当前深度缓冲区中的值进行比较。 如果大于深度缓冲区中的值,则丢弃这部分。否则 利用这个像素对应的深度值和颜⾊值。分别更新深度缓冲区和颜色缓存区.。这个过程称为“深度测试”。 -
深度值计算
深度值⼀般由16位,24位或者32位值表示,通常是24位。位数越⾼的话,深度的精确度越好。深度值的范围在[0,1]之间,值越小表示越靠近观察者,值越大表示远离观察者。
深度缓冲主要是通过计算深度值来比较⼤小,在深度缓冲区中包含深度值介于0.0和1.0之间, 从观察者看到其内容与场景中的所有对象的 z 值进⾏了比较。这些视图空间中的 z 值可以在投影平头截体的近平面和远平面之间的任何值。我们因此需要一些⽅法来转换这些视图空间 z 值到 [0,1] 的范围内,下⾯的 (线性) 方程把 z 值转换为 0.0 和 1.0 之间的值 :
深度值计算.png
far和near是提供到投影矩阵设置可见视图截锥的远近值。
深度测试的使用
开启深度测试
glEnable(GL_DEPTH_TEST);
关闭深度测试
glDisable(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);
函数(mode) | 说明 |
---|---|
GL_ALWAYS | 总是通过测试 |
GL_NEVER | 总是不通过测试 |
GL_LESS | 在当前深度值 < 存储的深度值时通过 |
GL_GREATER | 在当前深度值 > 存储的深度值时通过 |
GL_EQUAL | 在当前深度值 == 存储的深度值时通过 |
GL_LEQUAL | 在当前深度值 <= 存储的深度值时通过 |
GL_GEQUAL | 在当前深度值 >= 存储的深度值时通过 |
GL_NOTEQUAL | 在当前深度值 != 存储的深度值时通过 |
打开/阻断 深度缓存区写入
//value: GL_TURE,开启深度缓冲区写入; GL_FALSE,关闭深度缓冲区写⼊
void glDepthMask(GLBool value);
开启深度测试甜甜圈。
![](https://img.haomeiwen.com/i18597282/988680e6a678a5d8.gif)
2、ZFighting闪烁
ZFighting闪烁原因:开启深度测试后,OpenGL 就不会再去绘制模型被遮挡的部分。这样实现的显示更加真实。但是由于深度缓冲区精度的限制对于深度相差非常⼩的情况下。(例:在同一平⾯上进行2次绘制),OpenGL就可能出现不能正确判断两者的深度值,会导致深度测试的结果出现随机。前⾯2个画⾯,交错出现。就会出现闪烁问题。
解决方式
- 第一步:启用Polygon Offset:增大重叠或深度值接近的2个图形的深度值差距,使得深度值偏移而不产生重叠。
glEnable(GL_POLYGON_OFFSET_FILL);
Polygon Offset模式 | 对应的光栅化模式 |
---|---|
GL_POLYGON_OFFSET_FILL | GL_LINE |
GL_POLYGON_OFFSET_LINE | GL_LINE |
GL_POLYGON_OFFSET_POINT | GL_POINT |
- 第二步:指定偏移量
通过glPolygonOffset来指定2个参数 factor和units
void glPolygonOffset(Glfloat factor,Glfloat units);
应⽤到⽚段上总偏移计算⽅方程式:
Depth Offset = (DZ * factor) + (r * units); DZ:深度值(Z值)
r:使得深度缓冲区产⽣变化的最⼩值
负值,将使得z值距离我们更近,⽽正值,将使得z值距离我们更远。
一般而言,设置factor和units设置为-1.0和-1.0。
- 第三步:关闭Polygon Offset
//参数模式同开启
glDisable(GL_POLYGON_OFFSET_FILL);
ZFighting闪烁问题预防
- 不要将两个物体靠的太近,避免渲染时三⻆形叠在一起。
- 尽可能将近裁剪面设置得离观察者远一些。
如果观察者离近裁剪平⾯很近,那么深度测试对精确度要求很⾼。因此,可以适当推远近裁剪平⾯的位置来避免这个问题,但是可能导致离观察者较近的物体被裁减掉,因此需要调试好裁剪⾯参数。 - 使用更高位数的深度缓冲区
默认的深度缓冲区精度是24位的,如果硬件支持32位的缓冲区,就可以提供更高的精度。
3、裁剪
在OpenGL 中提高渲染的⼀种⽅式,只刷新屏幕上发生变化的部分。OpenGL 允许将要进行渲染的窗⼝只去指定⼀个裁剪框。
基本原理
用于渲染时限制绘制区域,通过此技术可以在屏幕(帧缓冲)指定一个矩形区域。启用剪裁测试之后,不在此矩形区域内的片元被丢弃,只有在此矩形区域内的⽚元才有可能进入帧缓冲。因此,实际达到的效果就是在屏幕上开辟了一个⼩窗口,可以在其中进⾏指定内容的绘制。
//1 开启裁剪测试
glEnable(GL_SCISSOR_TEST);
//2.关闭裁剪测试
glDisable(GL_SCISSOR_TEST);
//3.指定裁剪窗⼝
//x,y:指定裁剪框左下⻆位置; width,height:指定裁剪尺⼨
void glScissor(Glint x, Glint y, GLSize width, GLSize height);
理解窗口、视口、裁剪区域
- 窗口:显示界面(等价于iOS的window)。
- 视口:窗口中用来显示图形的⼀块矩形区域,它可以和窗⼝等大,也可以⽐窗⼝⼤或者小。只有绘制在视口区域中的图形才能被显示,如果图形有一部分超出了了视口区域,那么那⼀部分是看不到的。通过glViewport()函数设置。
-
裁剪区域(平⾏投影):视⼝矩形区域的最小最⼤x坐标(left,right)和最⼩最⼤y坐标 (bottom,top),而不是窗口的最小最大x坐标和y坐标。通过glOrtho()函数设置,这个函数还需指定最近 最远z坐标,形成⼀个⽴体的裁剪区域。
窗口、视口
4、混合
OpenGL渲染时会把颜色值存在颜⾊缓存区中,每个⽚段的深度值也是放在深度缓冲区。
- 当深度缓冲区被关闭时,新的颜色将简单地覆盖原来颜色缓存区存在的颜色值。
- 当深度缓冲区再次打开时,新的颜⾊片段只是当它们比原来的值更接近邻近的裁剪平⾯才会替换原来的颜⾊片段。
//开启混合
gl_Enable(GL_BIEND);
颜色混合
- 目标颜色:已经存储在颜色缓存区的颜色值(已经存在的颜色,旧颜色)。
-
源颜色:作为当前渲染命令结果进⼊颜⾊缓存区的颜色值(新进入的颜色,新颜色)。
当混合功能被开启时,源颜色和⽬标颜色的组合方式是混合方程式控制的。在默认情况下,混合方程式如下所示:
Cf = (Cs * S) + (Cd * D);
//Cf: 最终计算参数的颜⾊
//Cs: 源颜⾊
//Cd: 目标颜⾊
//S: 源混合因⼦
//D: ⽬标混合因⼦
设置混合因子
glBlendFunc(GLenum S, GLenum D);
//S:源混合因⼦
//D:⽬标混合因子
函数 | RGB混合因子 | Alpha混合因子 |
---|---|---|
GL_ZERO | (0, 0, 0) | 0 |
GL_ONE | (1, 1, 1) | 1 |
GL_SRC_COLOR | (Rs, Gs, Bs) | As |
GL_ONE_MINUS_SCR_COLOR | (1, 1, 1) - (Rs, Gs, Bs) | 1-As |
GL_DST_COLOR | (Rd, Gd, Bd) | Ad |
GL_ONE_MINUS_DST_COLOR | (1, 1, 1) - (Rd, Gd, Bd) | 1-Ad |
GL_SRC_ALPHA | (As, As, As) | As |
GL_ONE_MINUS_SCR_ALPHA | (1, 1, 1) - (As, As, As) | 1-As |
GL_DST_ALPHA | (Ad, Ad, Ad) | Ad |
GL_ONE_MINUS_DST_ALPHA | (1, 1, 1) - (Ad, Ad, Ad) | 1-Ad |
GL_CONSTANT_COLOR | (Rc, Gc, Bc) | Ac |
GL_ONE_MINUS_CONSTANT_ALPHA | (1, 1, 1) - (Ac, Ac, Ac) | 1-Ac |
GL_CONSTANT_ALPHA | (Ac, Ac, Ac) | Ac |
GL_ONE_MINUS_CONSTANT_ALPHA | (1, 1, 1) - (Ac, Ac, Ac) | 1-Ac |
GL_ONE_MINUS_CONSTANT_ALPHA | (f, f, f)* f = min(As, 1 - Ad) | 1 |
- 表中R、G、B、A 分别代表 红、绿、蓝、Alpha
- 表中下标S、D,分别代表源、⽬标
- 表中C 代表常量颜⾊(默认⿊色)
混合方程式
实际上不止一种颜色混合方程式,OpenGL有5个不同的方程式进行选择。
glbBlendEquation(GLenum mode);
模式 | 函数 |
---|---|
GL_FUNC_ADD | GL_LICf = (Cs * S) + (Cd * D)E |
GL_FUNC_SUBTRACT | Cf = (Cs * S) - (Cd * D) |
GL_FUNC_REVERSE_SUBTRACT | Cf = (Cd * D) - (Cs * S) |
GL_MIN | Cf = min(Cs, Cd) |
GL_MAX | Cf = max(Cs, Cd) |
常量混合颜色
常量混合颜⾊,默认初始化为黑色(0.0f,0.0f,0.0f,0.0f),但是还是可以修改这个常量混合颜色。
void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclampf alpha );
glBlendFuncSeparate函数
除了能使⽤OpenGL内置的混合因⼦,还可以有更灵活的选择。
void glBlendFuncSeparate(GLenum strRGB, GLenum dstRGB , GLenum strAlpha, GLenum dstAlpha);
//strRGB: 源颜色的混合因⼦
//dstRGB: 目标颜⾊的混合因⼦
//strAlpha: 源颜⾊的Alpha因⼦
//dstAlpha: 目标颜⾊的Alpha因⼦
举例
例:使用常见的混合函数组合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
如果颜色缓存区已经有一种颜⾊红色(1, 0, 0, 0)
在这上面混合一种alpha为0.6的蓝色(0, 0, 1, 0.6)
Cd (目标颜色) = (1, 0, 0, 0)
Cs (源颜色) = (0, 0, 1, 0.6)
S = 源alpha值 = 0.6
D = 1 - 源alpha值= 1-0.6 = 0.4
⽅程式Cf = (Cs * S) + (Cd * D)
等价于 = (Blue * 0.6) + (Red * 0.4)
最终颜色是以原先的红色(⽬标颜色)与后来的蓝色(源颜色)进⾏组合。源颜色的alpha值越高,添加的蓝色颜色成分越高,⽬标颜⾊所保留的成分就会越少(事例结果如图3)。
![](https://img.haomeiwen.com/i18597282/799f87510540ccf3.png)
网友评论