最近经常要给2D游戏写一些新的shader来做特效。比起粒子特效,着色器特效可能更适合UI和2D元素上的表现。
先看一下效果:
关于在shaderlab种实现流光的文章很多,但很少有给UI实现的,并且常常只是Add一层颜色,并没有去表现“光”的效果。
以下是shader全文,后面会介绍一些细节:
1 Shader "Sprites/Default_Flowlight" 2 { 3 Properties 4 { 5 [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} 6 _Color ("Tint", Color) = (1, 1, 1, 1) 7 [MaterialToggle] PixelSnap ("Pixel snap", float) = 0 8 9 _FlowlightTex ("Flowlight Texture", 2D) = "white" {} 10 _Power ("Power", float) = 1 11 _SpeedX ("SpeedX", float) = 1 12 _SpeedY ("SpeedY", float) = 0 13 } 14 15 SubShader 16 { 17 Tags 18 { 19 "Queue"="Transparent" 20 "IgnoreProjector"="True" 21 "RenderType"="Transparent" 22 "PreviewType"="Plane" 23 "CanUseSpriteAtlas"="True" 24 } 25 26 Cull Off 27 Lighting Off 28 ZWrite Off 29 Blend One OneMinusSrcAlpha 30 31 Pass 32 { 33 CGPROGRAM 34 #pragma vertex vert 35 #pragma fragment frag 36 #pragma multi_compile _ PIXELSNAP_ON 37 #include "UnityCG.cginc" 38 39 struct appdata_t 40 { 41 float4 vertex : POSITION; 42 float4 color : COLOR; 43 float2 texcoord : TEXCOORD0; 44 }; 45 46 struct v2f 47 { 48 float4 vertex : SV_POSITION; 49 fixed4 color : COLOR; 50 half2 texcoord : TEXCOORD0; 51 half2 texflow : TEXCOORD1; 52 }; 53 54 fixed4 _Color; 55 sampler2D _FlowlightTex; 56 fixed4 _FlowlightTex_ST; 57 fixed _SpeedX; 58 fixed _SpeedY; 59 60 v2f vert(appdata_t IN) 61 { 62 v2f OUT; 63 OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex); 64 OUT.texcoord = IN.texcoord; 65 OUT.texflow = TRANSFORM_TEX(IN.texcoord, _FlowlightTex); 66 OUT.texflow.x += _Time * _SpeedX; 67 OUT.texflow.y += _Time * _SpeedY; 68 OUT.color = IN.color * _Color; 69 #ifdef PIXELSNAP_ON 70 OUT.vertex = UnityPixelSnap (OUT.vertex); 71 #endif 72 73 return OUT; 74 } 75 76 sampler2D _MainTex; 77 float _Power; 78 79 fixed4 frag(v2f IN) : SV_Target 80 { 81 fixed4 c = tex2D(_MainTex, IN.texcoord); 82 fixed4 cadd = tex2D(_FlowlightTex, IN.texflow) * _Power; 83 cadd.rgb *= c.rgb; 84 c.rgb += cadd.rgb; 85 c.rgb *= c.a; 86 return c; 87 } 88 ENDCG 89 } 90 } 91 }
由于是从Unity自带shader“Sprites-Default”修改而来,故名字只是在其后加上“-Flowlight”。
其实流程和正常的UV滚动叠加没有什么区别,注意要用UI传入的UV计算纹理坐标。
66 OUT.texflow = TRANSFORM_TEX(IN.texcoord, _FlowlightTex);
之后两张纹理在frag中叠加输出,需要注意的只有一行:
83 cadd.rgb *= c.rgb;
很多shader作者在写流光的时候只是把叠加纹理的颜色+=在了主纹理上,但如果先将叠加纹理乘以主纹理的颜色,就会让暗色变得更暗,更符合光吸收的感觉,再配合Power控制光强度可以更好地表现光。
当然这样一来的副作用就是流光纹理本身的颜色就没有用了,因为变成了只用灰度,毕竟本身目的只是在打光。需要颜色的话可以在这行的下一行乘以自定义颜色,这里就不写了。
以上是需要留意的部分。
具体流程上,在vert中先获得流光纹理的纹理坐标OUT.texflow,而后使用_Time时间参数让纹理坐标随时间偏移,注意偏移操作在顶点着色vert部分进行而不是在片段着色frag,这样不会浪费计算量。
之后在frag中进行叠加将流光纹理与主纹理叠加,注意是c.rgb += cadd.rgb,否则c += cadd会导致透明通道也被叠加上去,对下一步c的预乘透明通道产生影响。
在纹理的制作上,因为是循环滚动,所以为了不会让光出现过度频繁,需要在流光纹理的旁边加上合适宽度的黑色区域,下面是本文开头动图使用的流光纹理。
它在光线纹理(正方形纹理)的左侧加上了等宽的黑色区域,让纹理变为了2x1的宽度,这样再将材质的Tiling改为X:0.5 Y:1就可以让光出现的频率减半了,当然还可以把光线区域在PS再缩小为66%,这样就可以让光线的出现频率变为最初的1/3了(别忘了改Tilling),其他频率以此类推。
下一次会分享一下uGui用的溶解。说是uGui用,其实使用了Sprite Shader的东西都可以用。