うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【Unity】Splatoon で利用されているような波ゲージ を Shader を用いて作る

f:id:hacchi_man:20200116004324p:plain
静止画ですがゲージの部分が波のように揺れている表現を作ってみます

波のような結果を出すものとして sin波とcos波 があるのでこちらを利用する
Shader で利用する場合はこれに _Time を食わせることで -1 ~ 1 の間を行き来させることができる

左右に動かしてみる

_Time.z を sin に食わせて -1 ~ 1 の範囲だと大きすぎるので 適当に20で割ることで
0.45 ~ 0.55 の値を行き来するようになった

f:id:hacchi_man:20200116005356g:plain

            fixed4 frag (v2f i) : SV_Target
            {
                // -0.05 ~ 0.05 の範囲に絞る
                float t = 0.5 + sin(_Time.z) / 20;
                
                if (i.uv.x < t)
                {
                    return fixed4(.5, .5, .5, 1);
                }
                
                return tex2D(_MainTex, i.uv);
            }

差分をつけて左右にずらしてみる

sin波の値を座標によってずらしたいため今回は uv の値を利用する
i.uv.yに掛ける値を大きくすることで波の周期は短くなっていきます

f:id:hacchi_man:20200116010150g:plain

            fixed4 frag (v2f i) : SV_Target
            {
                // -0.05 ~ 0.05 の範囲に絞る
                float t = 0.5 + sin(_Time.z + i.uv.y * 4) / 20;
                
                if (i.uv.x < t)
                {
                    return fixed4(.5, .5, .5, 1);
                }
                
                return tex2D(_MainTex, i.uv);
            }

複数の波を出してみる

せっかくなので波を複数表示させてみる上記ではsin波を利用していたので
もう一つの波にはcon波を利用する

f:id:hacchi_man:20200116010619g:plain

            fixed4 frag (v2f i) : SV_Target
            {
                float t1 = 0.5 + sin(_Time.w + i.uv.y * 3) / 20;                
                float t2 = 0.55 + cos(_Time.z + i.uv.y) / 40;
                 
                if (i.uv.x < t1)
                {
                    return fixed4(.5, .5, .5, 1);
                }
                if (i.uv.x < t2)
                {
                    return fixed4(.0, .0, .0, 1);
                }
                 
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }

コード整理

if文を使っていたので削除して、ゲージの位置や色を外部から渡せるように対応
※対応してないため、ゲージが 0 や 最大のときにも波が出ます

f:id:hacchi_man:20200116012147g:plain

Shader "Custom/Unlit/WaveGauge"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Ratio ("ratio", Range(0, 1)) = 0.5
        _Color1 ("Color1", Color) = (0.5, 0.5, 0.5, 1)
        _Color2 ("Color2", Color) = (0, 0, 0, 1)
    }
    
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
 
            #include "UnityCG.cginc"
 
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
 
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
 
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Ratio;
            fixed4 _Color1;
            fixed4 _Color2;
 
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
 
            fixed4 frag (v2f i) : SV_Target
            {
                float t1 = _Ratio + sin(_Time.w + i.uv.y * 3) / 20;                
                float t2 = _Ratio + 0.05 + cos(_Time.z + i.uv.y) / 40;
                
                fixed4 gaugeColor = lerp(tex2D(_MainTex, i.uv), _Color2, saturate(i.uv.x < t2));
                return lerp(gaugeColor, _Color1, saturate(i.uv.x < t1));
            }
            ENDCG
        }
    }
}