摘要
实现一个圆环形的进度条,通过一个取值范围是0到1的值来确定进度。
索引
正文
简介
实现这种环形的进度条有两个主要问题,第一是将图片显示成一个透明的圆环,第二是控制要显示的扇形的圆心角。
这里每一个问题都可以有两类实现方案:使用图片采样或者实时的数学计算。这两种方案可以看做是同一个操作的两种不同优化方向。
事实上在shader编写和特效的实现过程中,有很多操作都可以在这两个优化方向上选择,以达到平衡负载的目的。使用图片、常数或顶点色来保存一些数据更加直观,对计算性能的要求很低,不随计算复杂度的增加而增加,缺点是占用空间更大、会有精度限制而且结果固定,无法实现高解析度、平滑过渡、动态改变等功能;使用数学方法实时计算得到数据,可以解决上述问题,不会占用存储空间,但是会增加CPU/GPU的运算负载,如果是十分复杂的运算则会消耗大量时间,在占用面积很大的片段着色器中执行可能会导致帧率明显下降等问题,而且寻找和修正这些复杂算法本身就会增大开发难度。所以具体在项目中选择哪种方案还是要根据项目的性能要求和实际情况灵活选择。
下面将具体介绍这两种不同的实现方案。
使用透明贴图实现
最简单的方法
使用透明贴图就是使用一张同时包含环形和角度Alpha值的纹理,角度渐变的方向和进度条的走向一致,然后使用Value值进行AlphaTest,剪裁掉透明区域。随着Value值的变化,进度条被剪裁掉的部分也越来越多,由此达到进度变化的目的。
使用到的纹理如下
这是最简单的方法,使用任意一个支持AlphaTest的shader都能完成。
这种方法存在两个弊端:
第一是AlphaTest的阈值是对全图生效的,也就是说表现环形用的镂空也会被一并剪裁,而这些地方在作图的时候往往都存在一个羽化效果,边界并不是严格的01二值化,这就导致调整进度变化的同时环形的边缘也会有细微变化。
第二是由于是对纹理进行采样,在纹理尺寸较小的时候剪裁边缘会出现比较明显的锯齿。使用效果图如下
改进:计算宽度
下面通过计算uv值剪裁来动态调整圆环的宽度,同时也避免了边缘的抖动。
本文所使用的网格是默认的Quad,大部分计算都放在了片段着色器中。
首先通过模型uv得到片段在uv空间的坐标,这个坐标是从0~1的,我们要把它映射到-1~1(线性映射就是求直线解析式y=kx+b然后解方程组)。这样uv坐标的原点就到了中心位置,通过点乘即可得到每个片段到中心的距离的平方了。
float2 uv11 = i.uv * 2 - 1;
float uvLength2 = dot(uv11,uv11);
然后声明两个属性,外半径和内半径,使用1-得到正确的方向,它和距离相加后向下取整,得到一个01值,距离不足指定半径的=0,超过的=1。1-这个值给它取反,表示距离超出半径的=0,不足的=1,这样内外相乘即可得到环形区域(相乘=1的部分)。
_MaxRadius("MaxRadius", Range(0,1)) = 1
_MinRadius("MinRadius", Range(0,1)) = 0.5
float uv_final = (1-floor(uvLength2 + (1-_MaxRadius))) * floor(uvLength2 + (1-_MinRadius));
把这个值与固定值0.5做剪裁,再加入常规的AlphaTest再次剪裁得到最终效果。再次剪裁的目的的避免Value值影响环形。
fixed4 col = tex2D(_MainTex, i.uv);
clip(uv_final - 0.5);
clip(_Value - col.a);
return col;