OpenGL-Blending
1.混合
混合这个概念对于我们来讲比较好理解,常见的玻璃,常用的png图片中的A通道代表的就是alpha值。
我们与混合的第一次接触应该是在opengl入门里的纹理一章节,我们在一个立方体上采用了两个texture,并通过↑和↓键,改变两个texture的混合比例。在那节教程中,导入texture时我们也发现普通的jpg图片采用的是GL_RGB
三通道24位颜色数据,而png图片采用的是GL_RGBA
四通道32位颜色数据。其中alpha的取值在0.0~1.0之间,alpha值越高则代表透明度较低。在混合的时候,我们以各个片段的基本颜色,通过alpha值加权进行混合。
2.丢弃片段
首先我们向模板缓冲的场景中加入可爱的草。(草生)
这里虽然我们在片段着色器中加入了texture的alpha值,但是opengl默认并不会处理alpha值,我们需要手动来完成。
void main()
{
vec4 texColor = texture(texture1, TexCoords);
if(texColor.a < 0.1)
discard;
FragColor = texColor;
}
这里GLSL给了我们discard
指令,这个指令可以让我们把这个片段丢弃,因此这里的颜色就不会被绘制。
由于这里我比较懒,绘制贴图的四边形用的顶点,还是之前教程中给出的立方体的顶点。不过之前的立方体顶点中贴图是倒立的,因此我在顶点着色器中对顶点位置的y轴做了翻转。
这里我甚至可以将原来立方体的六个面都绘制出来,出现大草原。
3.真 · 混合
当然上面的丢弃操作只能算是一种假混合,真要混合还是要通过加权的方式重新计算每个片段的颜色。
同深度测试模板测试类似的,混合也是需要我们手动开启的:
glEnable(GL_BLEND);
接下来按照国际惯例我们还需要告诉opengl应该怎样混合
glBlendFunc(GLenum sfactor, GLenum dfactor)
函数共有两个参数,用来设置源和目标因子
选项 | 因子值 |
---|---|
GL_ZERO | 0 |
GL_ONE | 1 |
GL_SRC_COLOR | 源颜色向量 C ⃗ s o u r c e \vec{C}_{source} Csource |
GL_ONE_MINUS_SRC_COLOR | 1 - C ⃗ s o u r c e \vec{C}_{source} Csource |
GL_DST_COLOR | 目标颜色向量 C ⃗ d e s t i n a t i o n \vec{C}_{destination} Cdestination |
GL_ONE_MINUS_DST_COLOR | 1 - C ⃗ d e s t i n a t i o n \vec{C}_{destination} Cdestination |
GL_SRC_ALPHA | C ⃗ s o u r c e \vec{C}_{source} Csource.a |
GL_ONE_MINUS_SRC_ALPHA | 1 - C ⃗ s o u r c e \vec{C}_{source} Csource.a |
GL_DST_ALPHA | C ⃗ d e s t i n a t i o n \vec{C}_{destination} Cdestination.a |
GL_ONE_MINUS_DST_ALPHA | 1 - C ⃗ d e s t i n a t i o n \vec{C}_{destination} Cdestination.a |
GL_CONSTANT_COLOR | 常数颜色向量 C ⃗ c o n s t a n t \vec{C}_{constant} Cconstant |
GL_ONE_MINUS_CONSTANT_COLOR | 1 - C ⃗ c o n s t a n t \vec{C}_{constant} Cconstant |
GL_CONSTANT_ALPHA | C ⃗ c o n s t a n t \vec{C}_{constant} Cconstant.a |
GL_ONE_MINUS_CONSTANT_ALPHA | 1 - C ⃗ c o n s t a n t \vec{C}_{constant} Cconstant.a |
当然这个选项的命名规则十分简单,我们甚至不需要看上面的表格。
- 开头GL
- ONE_MINUS表示(1 - C)
- SRC表示source也就是源颜色,DST表示destination目标颜色,CONSTANT表示constant常数颜色
- 最后再加上ALPHA的话就表示取alpha分量,如果没有后缀的话就用COLOR结尾。
- 特殊情况ZERO/ONE分别表示0和1
举个栗子,如果我们需要使用源颜色向量的alpha分量作为源因子,使用1 - alpha作为目标因子:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUX_SRC_ALPHA);
同时我们也可以采用glBlendFuncSeparate
为RGB和A通道分别设置不同的选项:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
甚至我们还可以通过glBlendEquation(GLenum mode)
修改源和目标之间的运算符
选项 | 运算关系 |
---|---|
GL_FUNC_ADD | Src + Dst |
GL_FUNC_SUBTRACT | Src - Dst |
GL_FUNC_REVERSE_SUBTRACT | Dst - Src |
这个选项默认是GL_FUNC_ADD
不过不常用,因为在大部分情况下加法都是我们希望的操作。
4.渲染半透明纹理
这次我们尝试使用新的方法,在场景中渲染玻璃
结果发现随意放置玻璃的话,会发现有很大的遮挡问题。这个问题在于采用这种混合方式的时候我们不能够随意地绘制物体,我们需要按照一定次序去绘制,这样才可以获得更加合理的视觉效果。
而且在模板测试的后面再去绘制片段的话,
5.按序绘制物体
绘制的原则如下:
- 先绘制所有不透明的物体
- 对所有透明的物体排序
- 按从远到近的顺序绘制透明的物体
(由于我的玻璃的顶点数据用的还是之前立方体的一套,不太容易调整绘制顺序,因此只调节了非透明物体和透明物体之间的绘制顺序。如果要调整透明物体之间的绘制顺序,我们需要建立一个map来保存透明物体和相机之间的距离,在绘制的时候按顺序拿出来绘制即可)
或许我们还可以采用更加高级的次序无关透明度来获得更加优异的结果,但是这就超出本教程的范围了。