α混合
α分量
代表 R G B A 中的 A
通过控制A分量来实现半透明效果的方式,
被称为 α混合(alpha blending)。
实现α混合
如何开启α混合
- 开启混合功能
1
gl.enable(gl.BLEND);
- 指定混合函数
1
gl.blendFunc(gl.SRC.ALPHA,gl.ONE_MINUS_SRC_ALPHA);
LookAtBlendedTriangles Code
1 | let VSHADER_SOURCE = ` |
1 | function main() { |
1 | function initVertexBuffers(gl) { |
1 | let g_EyeX = 0.2, g_EyeY = 0.25, g_EyeZ = 0.25; |
混合函数
gl.blendFunc()
在LookAtBlendedTriangles中有两行涉及α混合的关键代码:
1 | gl.enable(gl.BLEND); |
α混合涉及两个颜色:
- 源颜色(source color)
- 待混合进去的颜色,也可以说是要叠加的对象的颜色
- 像 加奶咖啡 里的 牛奶(加进去的原料)
- 目标颜色(destination color)
- 待被混合进去的颜色,也可以说是画板上已有的颜色
- 像 加奶咖啡 里的 咖啡(被加的基底)
加法混合(additive blending)
加法混合会使被混合的区域更加明亮,
所以常常用来实现 爆炸 的光照效果。
blendFunc的参数
- 源颜色分量:(Rs,Gs,Bs,As)
- 目标颜色分量:(Rd,Gd,Bd,Ad)
镂空效果:gl.blendFunc(gl.ZERO,gl.ZERO)
- src_factor = gl.ZERO 表示忽视源颜色
- dst_factor = gl.ZERO 表示忽视目标颜色
- 同时忽视源颜色和目标颜色,就表示镂空处理
只取源颜色gl.blendFunc(gl.ONE,gl.ZERO)
- src_factor = gl.ONE 表示完全保留源颜色
- dst_factor = gl.ZERO 表示忽视目标颜色
- 完全忽视底色,直接将源颜色绘制在无视canvas背景画布的dom容器之中
只取目标颜色gl.blendFunc(gl.ZERO,gl.ONE)
- src_factor = gl.ONE 表示忽视源颜色
- dst_factor = gl.ZERO 表示完全保留目标颜色
- 完全忽视源颜色,保留目标颜色就表示保存画布,什么都不绘制
好像在用透明墨水画画一样
半透明的三维物体
在ColoredCube基础之上
按照之前介绍的实现 α混合 的方法,我们为颜色缓冲区填入的数据添加上透明度:
1 | let colors = new Float32Array([ |
再加上开启α混合的配置代码:
1 | gl.enable(gl.BLEND); |
应该就可以实现了,但是事实上的运行效果却和期望得到的不同:
为什么没有实现透明呢?
答: 因为开启了 DEPTH_TEST(隐藏面消除) 。
1 | gl.enable(gl.DEPTH_TEST); |
开启了隐藏面消除会使得 被隐藏的片元根本不被绘制 , 自然更不会有混合过程。
所以只需要 关闭gl.DEPTH_TEST 即可。
开启DEPTH_TEST
关闭DEPTH_TEST
透明与不透明物体共存
如何同时实现隐藏面消除和半透明效果?
BlendedCube直接把DEPTH_TEST关闭了,这种方法太简单粗暴,一点都不优雅。
可以通过使用下面这种机制同时实现 隐藏面消除 和 半透明效果。
提前剧透:
1.开启gl.DEPTH_TEST
1 | gl.enable(gl.DEPTH_TEST); |
2.绘制出所有不透明的物体
1 | gl.drawElements(非透明物体) |
3.禁止深度缓冲区的写入操作
深度缓冲区是用于进行 隐藏面消除 的,
这一步需要禁止接下来深度缓冲区的 写入操作,
使之变得 只读。
1 | gl.depthMask(false); |
4.绘制所有半透明物体
注意它们应当按照 深度进行排序
1 | gl.drawElements(半透明物体) |
5.释放深度缓冲区
重新使深度缓冲区 可读写
1 | gl.depthMask(true); |
BlendedWithDepthMask
> 主要绘制逻辑
1 | function draw(gl, u_MvpMatrix, mvpMatrix, projMatrix){ |