原理
根据传入的进度[0,1]剔除对应颜色值 RGB 之一的像素,上图为进度条所需的从颜色值 0 到颜色值 1 的贴图(透明底图,进度条的圈为从白到黑的不透明圆环)。
此代码负责剔除一定颜色值的像素,利用 step 函数的原理(step(a,b),当 b>=a 时返回 1,否则返回 0),其中 1.0-col.r 决定进度条动画的方向,代表从白向黑,也可以直接使用 col.r 从黑向白。
1 | float colorMinus = step(1.0-col.r,u_progress); |
这段代码包括两个部分,前面的部分决定进度条的颜色,后面的部分决定背景的颜色,我们可以从加号的地方拆开来看。前面的部分构造了一个三维向量,假如 colorMinus 为 1,则前面的向量就是全 1,此时和传入的进度条颜色值相乘可得进度条的颜色。后面构造了一个和前面相反的三维向量,此时后面的向量为全 0,和传入的背景颜色相乘依然是 0。
由此可得,当此处的像素筛选为进度条的时候,进度条的颜色值发挥作用,而背景颜色值不发挥作用;相反,此处像素筛选为背景时,进度条颜色值不发挥作用而背景颜色值发挥作用。为什么要这么做呢,因为 GPU 运算的时候是大量并行的,条件判断语句对于 GPU 来说每个分支都要执行,性能会有损失。
1 | vec4 final = vec4(colorMinus); |
只有上述代码的话,运行时的效果并不能符合我们的需要,进度条之外的像素也会有填充。因此我们需要对透明度进行剔除。
1 | final.a = (step(1.0-col.a,0.0) * colorMinus * u_edgeColor.a) + step(colorMinus,0.0) * u_bgColor.a; |
由于我们支持自定义进度条和进度条背景的透明度,因此这两个部分也需要进行处理。
关于进度条的透明度,起判断的部分只有 colorMinus * u_edgeColor.a。为什么要乘上 step(1.0-col.a,0.0)呢,主要是为了筛去透明的部分,col.a 是原材质贴图的透明度,当其为 1 的时候(不透明)所得值为 0,step(1.0-col.a,0.0)的值才为 1,进度条的颜色才生效。当然这里可以设置为你所需要的任意值。
关于背景的透明度,和进度条同理可得 step(colorMinus,0.0) * u_bgColor.a。
进度条 shader 代码
片元着色器
1 |
|
顶点着色器
1 |
|