左图是原图,没有使用九宫格sliced。也没有在图集中。这篇文章作为基础,只考虑如何画斜条纹。另外两篇文章讨论了如何处理在九宫格和在图集中使用的问题。
注意,所有的改动都在这里:
void main () {
vec4 o = vec4(1, 1, 1, 1);
#if USE_TEXTURE
CCTexture(texture, v_uv0, o);
#endif
o *= v_color;
// 在这里进行斜条纹的绘制
ALPHA_TEST(o);
#if USE_BGRA
gl_FragColor = o.bgra;
#else
gl_FragColor = o.rgba;
#endif
}
}%
亮度提升
其实把r,g,b都加个数就可以了,但是为了更加自然,我把亮度调节到当前值与vec3(1,1,1)的中间位置。这样无论是亮的部分或是暗的部分,都会呈现出一些变化,而不会把亮部“曝光过渡”。
vec3 one = vec3(1, 1, 1);
float fin = .5;
o.rgb = mix(o.rgb, one, fin * .5);
方式1,平移x,求奇偶
这种方式开销较大,切角度控制不均匀不方便,不推荐使用。学习shader的可以理解一下。
一条分界线
有了亮度的区别,那么我们就可以以不同的亮度显示出一条线来。实际上是两种不同的亮度拼凑出一条线。
vec3 one = vec3(1, 1, 1);
float fin = step(v_uv0.x, v_uv0.y);
o.rgb = mix(o.rgb, one, fin * .5);
从图像就能看出来,uv坐标轴是这样的:
x轴从左上角到右上角为[0,1]
y轴从左上角到左下角为[0,1],
注意跟我们习惯的右下角原点坐标系是y轴相反,且原点在左上角。
另外step的意义,例如 float fin = step(a,b):当a小于b时返回0,否则返回1。好像是一个开关。0乘在下面的mix函数中,就代表不做变换,完全用原值。而1则代表做变换。
平移分界线求每个使能结果的和的奇偶性
平移x,得到不同的边缘斜线部分的覆盖。数一数有多少个。它们一定是呈现1,2,3,4,5,6…这样的自然数的。那么我们在对2求模,根据得到的结果是0还是1,来作为开关。1就是变亮,0就是不变亮。
vec3 one = vec3(1, 1, 1);
float rate = step(v_uv0.x, v_uv0.y);
rate += step(v_uv0.x + .1, v_uv0.y);
rate += step(v_uv0.x + .2, v_uv0.y);
rate += step(v_uv0.x + .3, v_uv0.y);
rate += step(v_uv0.x + .4, v_uv0.y);
rate += step(v_uv0.x + .5, v_uv0.y);
rate += step(v_uv0.x + .6, v_uv0.y);
rate += step(v_uv0.x + .7, v_uv0.y);
rate += step(v_uv0.x + .8, v_uv0.y);
rate += step(v_uv0.x + .9, v_uv0.y);
rate += step(v_uv0.x - .1, v_uv0.y);
rate += step(v_uv0.x - .2, v_uv0.y);
rate += step(v_uv0.x - .3, v_uv0.y);
rate += step(v_uv0.x - .4, v_uv0.y);
rate += step(v_uv0.x - .5, v_uv0.y);
rate += step(v_uv0.x - .6, v_uv0.y);
rate += step(v_uv0.x - .7, v_uv0.y);
rate += step(v_uv0.x - .8, v_uv0.y);
rate += step(v_uv0.x - .9, v_uv0.y);
float fin = mod(rate, 2.0);
o.rgb = mix(o.rgb, one, fin * .5);
想一想如果修改其中2.0,和 .5这两个呈现什么效果?
比如对应改为:4.0和.2
不过这不是最终想要的哈。
方式2,只按x轴求模做step,再旋转
这个方式就比较好了,不需要求多个step,只需要一个step即可。可以360度均匀无死角旋转,对时间动画比较友好。
x求模
float fin = mod(v_uv0.x, .1) * 10.0;
o.rgb = mix(o.rgb, one, fin * .5);
其中由于模.1,取值[0,0.1]幅度比较小,不容易观察,所以我在后面放大了10倍。
在中间亮度处做一下step
float fin = mod(v_uv0.x, .1);
fin = step(.05, fin);
o.rgb = mix(o.rgb, one, fin * .5);
注意之前乘的10倍已经去掉了,因为我们从0.05处求step就可以变为0或1。
旋转
条纹已经出来了,我们把输入的uv先旋转一下。用旋转后的uv做条纹处理看看。
t就是旋转的弧度,比如下面的四分之π就是45度。具体它往哪边转,大家自己体会一下。不是高中数学的坐标轴方向我也很蒙,多调试,结果当成经验就好。
float t = 3.1416 / 4.0;
mat2 rot = mat2(cos(t), -sin(t), sin(t), cos(t));
vec2 uv2 = v_uv0 * rot;
float fin = mod(uv2.x, .1);
fin = step(.05, fin);
o.rgb = mix(o.rgb, one, fin * .5);
处理锯齿
上图其实已经基本完成了,但是明显的狗牙齿我们不能忍。先把step改smoothstep试试:
float fin = mod(uv2.x, .1);
// fin = step(.05, fin);
fin = smoothstep(.05-.01, .05+.01, fin);
o.rgb = mix(o.rgb, one, fin * .5);
这样一来我们干掉了一半的狗牙。想想另一半,突变在求模的函数上,不连续,直接用模值还真没办法。因为smoothstep函数似乎不能直接用在一个不连续的值上。(如果有小伙伴能直接用的话可以教教我)
那么我尝试着把不连续的模值构造成连续的。其实在.05附近连续的即可:反转一下每偶数个模周期的值就行了。
下面我把基础值提出,求了模周期奇偶even,并且按照奇偶性的区间翻转了smoothstep的结果:
float t = 3.1416 / 4.0;
mat2 rot = mat2(cos(t), -sin(t), sin(t), cos(t));
vec2 uv2 = v_uv0 * rot;
float x = uv2.x;
float d = .05;
float hd = d / 2.0;
float d10 = d / 10.0;
float even = mod(floor(x/d), 2.0);
float fin = smoothstep(hd-d10, hd+d10, mod(x, d));
fin = (1.0 - even) * fin + even * (1.0 - fin);
o.rgb = mix(o.rgb, one, fin * .5);
所谓“翻转”就是指用1减一下。就是0~1之间的函数图形上下反过来了。
另外里面的d变成了hd,d的1半。在实际操作中发现的。当对奇偶性进行了翻转,结果原来的两个断面接在了一起,造成了条纹宽度变大了一倍,那么在一开始就先缩小一倍。大家可以自行体验。
到此,线条造型部分就已经完成了。
处理透明部分
由于按钮图片有个阴影,那么阴影按常理是不要加条纹的。就根据其透明度来判断即可。注意里面的enable变量如何得来和使用。
一般来说我喜欢用smoothstep代替step,它可以使交接部分变得平滑,此处透明度的处理上也是一样的原理。毕竟从一个临界值硬生生的分开总是不好的。
float t = 3.1416 / 4.0;
mat2 rot = mat2(cos(t), -sin(t), sin(t), cos(t));
vec2 uv2 = v_uv0 * rot;
float x = uv2.x;
float d = .05;
float hd = d / 2.0;
float d10 = d / 10.0;
float even = mod(floor(x/d), 2.0);
float fin = smoothstep(hd-d10, hd+d10, mod(x, d));
fin = (1.0 - even) * fin + even * (1.0 - fin);
float min_gray = .5;
float enable = smoothstep(min_gray-.2, min_gray, o.a);
o.rgb = mix(o.rgb, one, fin * enable * .5);
此时就是开头的图片结果了。
总结
以上的方式和流程也是我做这个效果时的处理方式。我觉得记录的已经足够详细了。可能后面要补充一下旋转,因为这里的旋转轴是角上的点。如果围绕中心点选择呢?还需要做一些平移反平移的变换。
另外,下面有个文章在这个基础上做了针对九宫格的兼容性变换。就比如这个图,我是需要用在各种尺寸的长方形按钮上的。那么就要sliced(九宫格)模式了。如果直接用这篇文章的结论,画出来的线会出现明显的折射感。
还有,如果所用到的图,在图集里面该怎么办?另一篇文章讲解了如何对图集的图做一个兼容性变换。
各种变换都是提前做出变换,要让这篇文章用到的输入还能照常使用,不需要改变代码。
CocosCreator Effect (Shader) - 反九宫格补偿
CocosCreator Effect (Shader) - 反图集打包(Packable)补偿