一、深度测试
1. 深度
就是在坐标系中,像素点的 z 坐标距离观察者的距离。 观察者可以放在坐标系的任意位置,所以,不能简单的说 z 数值越大或越小,观察者就越靠近物体。
如果观察者在 z轴的正方向,z值越大,越靠近观察者。
如果观察者在 z轴的负方向,z值越小,越靠近观察者。
2. 深度缓冲区
存储在显存中。取值范围[0, 1] 浮点型。
原理:把距离观察者平面(近截面积)的深度值 与 窗口中每个像素点1对1进行关联以及存储。
3. 未开启深度测试时, 渲染的时候用的是油画算法,按照图层顺序渲染。
在讨论表面裁剪的文章中,我们论述过图层顺序。
图册添加顺序: 1区 -> 2区 -> 3区 -> 四区
-
绕Y轴顺时针旋转:
区 内环部分覆盖 1区;
1区 无法覆盖 2区; -
绕Y轴逆时针旋转:
4区 无法覆盖 3区;
2区 内环部分覆盖 1区; -
绕X轴顺时针旋转:
1区和2区 无法覆盖 3区和4区; -
绕X轴逆时针旋转:
3区和4区 内环部分覆盖 1区和2区;
4. 深度测试
深度缓冲区和颜色缓冲区是对应的。
颜色缓冲区存储像素的颜色印象,而深度缓冲区存储像素的深度信息。
在决定是否绘制一个物体表面时,首先要将表面对应的像素的深度值与当前深度缓冲区的值进行比较。如果大于深度缓冲区中的值,则丢弃这部分。否则利用这个像素对应的深度值和颜色值。分别更新深度缓冲区和颜色缓冲区。这个过程称为“深度测试”。
4.1 开关深度测试
// 开启
glEnable(GL_DEPTH_TEST);
// 关闭
glDisable(GL_DEPTH_TEST);
4.2 修改深度测试规则
glDepthFunc(<#GLenum func#>);
| 参数 | 说明 |
|---|---|
| GL_ALWAYS | 总是通过测试 |
| GL_NEVER | 总是不通过测试 |
| GL_LESS | 当前深度值 < 存储的深度值时 通过 |
| GL_LEQUAL | 当前深度值 <= 存储的深度值时 通过 |
| GL_EQUAL | 当前深度值 = 存储的深度值时 通过 |
| GL_GREATER | 当前深度值 > 存储的深度值时 通过 |
| GL_GEQUAL | 当前深度值 >= 存储的深度值时 通过 |
| GL_NOTEQUAL | 当前深度值 != 存储的深度值时 通过 |
5. 深度冲突(Z-fighting)
当两个平面或者三角形,非常紧密地平行排列在一起时,深度缓冲精度不足,无法决定两个形状哪个在前面。
结果就是这两个形状不断地在切换前后顺序,这会导致很奇怪的花纹。这个现象叫做深度冲突(Z-fighting),因为它看起来像是这两个形状在争夺(Fight)谁该处于顶端。
解决方法:
让深度值之间产生间隔。
如果两个图形之间有间隔,那么这两个图形就不会产生干涉。
这种复杂的事情当然是交给 OpenGL 去做。
5.1 开启多边形偏移:
void glEnable (GLenum cap);
| 参数 | 对应多边形填充方式 |
|---|---|
| GL_POLYGON_OFFSET_POINT | GL_POINT |
| GL_POLYGON_OFFSET_LINE | GL_LINE |
| GL_POLYGON_OFFSET_FILL | GL_FILL |
5.2 指定偏移量
glPolygonOffset(<#GLfloat factor#>, <#GLfloat units#>)
参数建议都设置为 -1。
一个大于 0 的 offset 会把模型推到离观察者更远的位置,相应的一个小于 0 的 offset 会把模型拉近。
5.3 预防深度冲突
- 不要让两个物体靠的太近。
- 尽可能将近裁剪面设置的离观察者远一些。因为近裁剪面附近精确度较高。
- 使用更高位数的深度缓冲区。
二、颜色混合
1.1 颜色混合
当开启深度测试后,遇到两个图层重叠的片元,只会简单的取更接近邻近的裁剪平面的那个值。
但是,如果这两个图层,一个是半透明的,一个是非半透明的,就无法表现出叠加的效果。因此,还需要对两个图层的颜色进行混合。
开启代码
glEnable(GL_BLEND);
1.2 组合颜色
固定着色器/可编程着色器:都可以使用开关方式开启颜色混合,这个开关控制的仅仅是两个图层进行混合的计算。
如果我们是单纯的拿一个颜色,来跟一个图层进行混合,则需要使用片元着色器,进行颜色混合方程式的计算。
1.2.1 颜色混合方程式:**
Cf = (Cs * S) + (Cd * D)
Cf: 最终计算参数的颜色
Cs: 源颜色
Cd: 目标颜色
S: 源混合因子
D: 目标混合因子
目标颜色:已经存储在颜色缓冲区的颜色值。
源颜色: 作为当前渲染命令结果进入颜色缓冲区的颜色值。
1.2.2 混合参数设置代码
glBlendFunc(<#GLenum sfactor#>, <#GLenum dfactor#>)
sfactor: 源混合因子
dfactor: 目标混合因子
表中R、G、B、A 分别代码 红、绿、栏、alpha
表中下标 S、D,分别代表源颜色、目标颜色
表中C代表常量颜色(默认黑色)
1.2.3 修改混合方程常量颜色
void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
// 在 glew.h 中重命名了
typedef void (GLAPIENTRY * PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
1.2.4 扩展内容
- 修改颜色混合方程
void glBlendEquation(GLenum mode);
// 在 glew.h 中重命名了
typedef void (GLAPIENTRY * PFNGLBLENDEQUATIONPROC) (GLenum mode);
- 另外一种设置混合因子的函数
void glBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
// 在 glew.h 中重命名了
typedef void (GLAPIENTRY * PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
sfactorRGB: 源颜色的混合因子
dfactorRGB: 目标颜色的混合因子
sfactorAlpha: 源颜色的 Alpha 因子
dfactorAlpha: 目标颜色的 Alpha 因子
1.3 总结
最终颜色是以原先的红色(目标颜色)与后来的蓝色(源颜色)进行混合。源颜色的 alpha 值越高,添加的蓝色颜色成分越高,目标颜色所保留的成分就越少。
如果源颜色 alpha 为0,则源颜色完全覆盖目标颜色。因此,当一个不透明的图层盖在另外一个图层上的时候,不会因为混合,而产生颜色混合错误的情况。
混合函数经常用于实现在其他一些不透明的物体前面绘制一个透明物体的效果。