【UnityShader】uGUIのImageを揺らす #79
前回の成果
ShaderLabのプロパティ属性について理解した。
今回やること
uGUIのImageを揺らそうと思います。
事前準備
Scene上にImageを配置し、今回制作するマテリアルをアタッチします。
ソースコード
Shader "Unlit/shakeTexture" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} _Color ("Tint", Color) = (1,1,1,1) [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1) [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1) [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {} [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0 _SinWave("SinWave", Range(0, 1)) = 0.2 _SinWidth("SinWidth", Range(0, 1)) = 0.5 _SinSpeed("SinSpeed", Range(0, 1)) = 0.2 _SinColorDistant("SinColorDistant", Range(0, 3)) = 0.2 [Toggle] _IsHorizontal ("Is Horizontal", int) = 0 } SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } Cull Off Lighting Off ZWrite Off Blend One OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #pragma multi_compile_instancing #pragma multi_compile _ PIXELSNAP_ON #pragma multi_compile _ ETC1_EXTERNAL_ALPHA #include "UnityCG.cginc" #ifdef UNITY_INSTANCING_ENABLED UNITY_INSTANCING_BUFFER_START(PerDrawSprite) fixed4 unity_SpriteRendererColorArray[UNITY_INSTANCED_ARRAY_SIZE]; float4 unity_SpriteFlipArray[UNITY_INSTANCED_ARRAY_SIZE]; UNITY_INSTANCING_BUFFER_END(PerDrawSprite) #define _RendererColor unity_SpriteRendererColorArray[unity_InstanceID] #define _Flip unity_SpriteFlipArray[unity_InstanceID] #endif CBUFFER_START(UnityPerDrawSprite) #ifndef UNITY_INSTANCING_ENABLED fixed4 _RendererColor; float4 _Flip; #endif float _EnableExternalAlpha; CBUFFER_END fixed4 _Color; struct appdata_t { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO }; v2f vert(appdata_t v) { v2f o; UNITY_SETUP_INSTANCE_ID (v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #ifdef UNITY_INSTANCING_ENABLED v.vertex.xy *= _Flip.xy; #endif o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.color = v.color * _Color * _RendererColor; #ifdef PIXELSNAP_ON o.vertex = UnityPixelSnap (o.vertex); #endif return o; } sampler2D _MainTex; sampler2D _AlphaTex; float _SinWave; float _SinWidth; float _SinSpeed; float _SinColorDistant; int _IsHorizontal; float _wave; float _speed; float _width; float _clrDis; float2 posColor(float2 inUV, float n) { float waveValueX = sin(inUV.y * _wave + _speed + _clrDis * n) * _width; float waveValueY = sin(inUV.x * _wave + _speed + _clrDis * n) * _width; return inUV + float2(waveValueX * step(1, _IsHorizontal), waveValueY * step(_IsHorizontal, 0)); } fixed4 frag(v2f i) : SV_Target { fixed4 color = fixed4(0, 0, 0, 0); float2 inUV = i.texcoord; _wave = _SinWave * 100; _speed = _Time.y * _SinSpeed * 20.0; _width = _SinWidth * 0.2; _clrDis = _SinColorDistant * _SinWidth * 5; color.r = tex2D(_MainTex, posColor(inUV, 2)).r; color.g = tex2D(_MainTex, posColor(inUV, 1)).g; color.b = tex2D(_MainTex, posColor(inUV, 0)).b; color.a = ( tex2D(_MainTex, posColor(inUV, 2)).a + tex2D(_MainTex, posColor(inUV, 1)).a + tex2D(_MainTex, posColor(inUV, 0)).a ) / 3; color *= i.color; color.rgb *= color.a; return color; } ENDCG } } }
ShaderはUnityのビルドインシェーダーのUI-Default.shaderを元に作成しています。
ビルドインシェーダーは、以下のUnity公式サイトから取得することができます。
対象のUnityバージョンを選択して、以下画像のようにダウンロードしてください。
Shaderの解説
今回のShaderは、ただ揺らすだけではなく色も揺らします。
色を揺らす
color.r = tex2D(_MainTex, posColor(inUV, 2)).r; color.g = tex2D(_MainTex, posColor(inUV, 1)).g; color.b = tex2D(_MainTex, posColor(inUV, 0)).b; color.a = ( tex2D(_MainTex, posColor(inUV, 2)).a + tex2D(_MainTex, posColor(inUV, 1)).a + tex2D(_MainTex, posColor(inUV, 0)).a ) / 3;
色を揺らすと言っても、やっていることは単純です。
各カラーチャンネルのUV値を少しずつズラすことによって、色を揺らす表現を行います。
アルファ値に対しても同様の計算を行っているのは、透過画像の対策となります。
この計算をやらないと以下のように、透過部分のアルファ値が意図しないものとなってしまいます。
アルファ値がおかしくなっている例
上記gifで使用した画像(Unityにデフォルトで入っているもの)
posColor関数
float2 posColor(float2 inUV, float n) { float waveValueX = sin(inUV.y * _wave + _speed + _clrDis * n) * _width; float waveValueY = sin(inUV.x * _wave + _speed + _clrDis * n) * _width; return inUV + float2(waveValueX * step(1, _IsHorizontal), waveValueY * step(_IsHorizontal, 0)); }
この関数でズラす値を決めています。
stepとToggleで、縦に揺らすか横に揺らすかを決めています。
sinの揺れの値は以下の画像のように、各色がバラバラになるように引数のnを利用しています。
以下の画像のRBGはグラフの色と同じになります。
step
これはy<=xなら1を、y>xなら0を返す関数です。
step(x, y); // 1が返ってくる step(1.0, 1.2); // 0が返ってくる step(1.0, 0.8);
posColor関数でズラしたRBGAの値に元の色を乗算してあげることで色の揺れを実現させています。
結果
Imageと色が揺れれば成功です。
色は、A1やA2といった文字の部分がわかりやすいかと思います。
inspector
また、Is Horizontalにチェックを入れることで横揺れにすることも可能です。
念の為、透過画像のgifも添付します。
ソースコードにコメントを付与
Shader "Unlit/shakeTexture" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} _Color ("Tint", Color) = (1,1,1,1) [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1) [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1) [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {} [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0 _SinWave("SinWave", Range(0, 1)) = 0.2 _SinWidth("SinWidth", Range(0, 1)) = 0.5 _SinSpeed("SinSpeed", Range(0, 1)) = 0.2 _SinColorDistant("SinColorDistant", Range(0, 3)) = 0.2 [Toggle] _IsHorizontal ("Is Horizontal", int) = 0 } SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } Cull Off Lighting Off ZWrite Off Blend One OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #pragma multi_compile_instancing #pragma multi_compile _ PIXELSNAP_ON #pragma multi_compile _ ETC1_EXTERNAL_ALPHA #include "UnityCG.cginc" #ifdef UNITY_INSTANCING_ENABLED UNITY_INSTANCING_BUFFER_START(PerDrawSprite) fixed4 unity_SpriteRendererColorArray[UNITY_INSTANCED_ARRAY_SIZE]; float4 unity_SpriteFlipArray[UNITY_INSTANCED_ARRAY_SIZE]; UNITY_INSTANCING_BUFFER_END(PerDrawSprite) #define _RendererColor unity_SpriteRendererColorArray[unity_InstanceID] #define _Flip unity_SpriteFlipArray[unity_InstanceID] #endif CBUFFER_START(UnityPerDrawSprite) #ifndef UNITY_INSTANCING_ENABLED fixed4 _RendererColor; float4 _Flip; #endif float _EnableExternalAlpha; CBUFFER_END fixed4 _Color; struct appdata_t { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO }; v2f vert(appdata_t v) { v2f o; UNITY_SETUP_INSTANCE_ID (v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #ifdef UNITY_INSTANCING_ENABLED v.vertex.xy *= _Flip.xy; #endif o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.color = v.color * _Color * _RendererColor; #ifdef PIXELSNAP_ON o.vertex = UnityPixelSnap (o.vertex); #endif return o; } sampler2D _MainTex; sampler2D _AlphaTex; float _SinWave; float _SinWidth; float _SinSpeed; float _SinColorDistant; int _IsHorizontal; float _wave; float _speed; float _width; float _clrDis; /// <summary> /// UV値を係数に応じてズラす /// </summary> /// <param name="inUV">UV値</param> /// <param name="n">指数</param> float2 posColor(float2 inUV, float n) { float waveValueX = sin(inUV.y * _wave + _speed + _clrDis * n) * _width; float waveValueY = sin(inUV.x * _wave + _speed + _clrDis * n) * _width; // stepで縦に揺れるか横に揺れるかを決める return inUV + float2(waveValueX * step(1, _IsHorizontal), waveValueY * step(_IsHorizontal, 0)); } fixed4 frag(v2f i) : SV_Target { fixed4 color = fixed4(0, 0, 0, 0); float2 inUV = i.texcoord; _wave = _SinWave * 100; _speed = _Time.y * _SinSpeed * 20.0; _width = _SinWidth * 0.2; _clrDis = _SinColorDistant * _SinWidth * 5; // 各色をズラす color.r = tex2D(_MainTex, posColor(inUV, 2)).r; color.g = tex2D(_MainTex, posColor(inUV, 1)).g; color.b = tex2D(_MainTex, posColor(inUV, 0)).b; // 透過画像対策でアルファ値もズレに合わせる color.a = ( tex2D(_MainTex, posColor(inUV, 2)).a + tex2D(_MainTex, posColor(inUV, 1)).a + tex2D(_MainTex, posColor(inUV, 0)).a ) / 3; color *= i.color; color.rgb *= color.a; return color; } ENDCG } } }
今回は以上となります。
ここまでご視聴ありがとうございました。