-100p

-10p

+10p

+100p

Unityドット欠けシェーダー

UnityでuGUI用ドット欠けシェーダー。ピクセルが虫食いで欠けていく演出。
(Dead Pixel Shader for Unity uGUI to progressively missing pixels)

関連ページ 参考URL
よくSFCのFFとかでは、敵を倒した際に敵の画像のピクセルが徐々に欠けていき最終的に消滅する演出があった。
あれを作りたいと思って結構楽に作れたので共有。

なお今回 こちらの記事 を大いに参考にしている。
このシェーダーの応用で、 Dissolve Shader も作れたのでそちらも併せてどうぞ。

ドット欠けシェーダー、コード全文

下がドット欠けシェーダーのサンプル動画。

以下コード全文。uGUI Imageへの反映方法は こちらの記事 参照。
Shader "UI/DeadPixel"
{
    Properties 
    {
        _VanishPow ("VanishPow", Range(0.0, 1.0)) = 0 
//消滅する力、1で全てのドットが消える

        _Roughness ("Roughness", Range(1, 100.0)) = 15 
//欠けていくドットの粗さ

    }

    SubShader
    {
        Tags { "Queue" = "Transparent" }
        Cull Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct appdata
            {
                fixed2 uv : TEXCOORD0;
                fixed4 vertex : POSITION;
                fixed4 color : COLOR;
            };

            struct v2f
            {
                fixed2 uv : TEXCOORD0;
                fixed4 vertex : POSITION;
                fixed4 color : COLOR;
            };

            sampler2D _MainTex;
            
//アタッチされたSpriteのサイズを取得(x=1.0/width, y=1.0/height, z=width, w=height)

            float4 _MainTex_TexelSize;
   
            float _VanishPow;
            float _Roughness;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.color = v.color;
                return o;
            }

            
//1次元 疑似ランダム(入力は2次元です)

            float random(fixed2 st)
            {
                return frac(sin(dot(st.xy, fixed2(12.9898, 78.233))) * 43758.5453);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);

                
//アタッチされたSpriteの縦横比をratioに保存

                fixed ratio = _MainTex_TexelSize.z / _MainTex_TexelSize.w;
                
//_Roughnessの入力値を実際に使う値まで細かくする

                fixed fixRough = _Roughness * 0.001f;

                
//対象ピクセルのx座標とy座標を保存、ただしfixRoughの値に応じて座標の取得が大雑把になる

                
//またy座標にはratioを追加で掛けることで、座標を大雑把にした時の値に補正を乗せて欠けるドットを正方形に修正する

                fixed roughX = floor(i.uv.x / fixRough) * fixRough;
                fixed roughY = floor(i.uv.y / (fixRough * ratio)) * (fixRough * ratio);
                fixed2 roughUv = fixed2(roughX, roughY);

                
//ランダムノイズにより、指定座標に対し0~1のランダムな値が返ってくる

                fixed rdm = random(roughUv);
                
//ランダムノイズから_VanishPowを引き、その値が0未満になった時対象ドットを消滅させる

                fixed doVanish = step(0, rdm - _VanishPow);

                col *= i.color;
                col.a = col.a * doVanish;

                return col;
            }
            ENDCG
        }
    }
}

プロパティを解説

用意してあるプロパティは2つのみ。
VanishPowの値を上げていくと次第に対象の画像が消滅していく。
ドット欠けシェーダーのプロパティ
 VanishPow 
: 消滅する力。0から1まで設定可能で、0で全て表示、1で全てのピクセルが消える。
 Roughness 
: VanishPowを上げていく際の、欠けていくドットの粗さ。1から100まで設定可能。

Roughnessの設定を1以上にする場合に、注意する点がある。
Imageのscale.xとscale.yの値が違ったり、元のSpriteのサイズからwidthやheightを変えると、欠けていくドットが正方形ではなくなる。
下の画像は1600x1200のドラゴンの画像に対し、ImageのHeightを2倍の2400の設定にした場合の画像。
ドット欠けシェーダーの注意点
見ての通り欠けていくドットが長方形になっている。

コードを少し解説

一番重要な点は、フラグメントシェーダーに渡されたuv座標を、下の関数を使って0~1のfloat値に変換して返している所。
            
//1次元 疑似ランダム(入力は2次元です)

            float random(fixed2 st)
            {
                return frac(sin(dot(st.xy, fixed2(12.9898, 78.233))) * 43758.5453);
            }
このランダムな値を使って、対象uv座標のピクセルを消滅させるかどうか判断している。
                
//ランダムノイズにより、指定座標に対し0~1のランダムな値が返ってくる

                fixed rdm = random(roughUv);
                
//ランダムノイズから_VanishPowを引き、その値が0未満になった時対象ドットを消滅させる

                fixed doVanish = step(0, rdm - _VanishPow);

                col *= i.color;
                col.a = col.a * doVanish;

たとえばuv座標(0.5, 0.5)をrandom関数に渡し、その結果返ってきた値が0.1だったとする。
その上で、もし_VanishPowが0な場合次のような計算式になる。
 fixed doVanish = step(0, rdm - _VanishPow); 

 fixed doVanish = step(0, 0.1 - 0); 

 fixed doVanish = step(0, 0.1); 
0は0.1以下なので、step関数の結果doVanishには1が保存される。
そして
 col.a = col.a * doVanish; 
でピクセルのalpha値に1が掛け算される。つまり値に全く変化がない。

もしこれで、_VanishPowが0.2であった場合次のような計算式になる。
 fixed doVanish = step(0, rdm - _VanishPow); 

 fixed doVanish = step(0, 0.1 - 0.2); 

 fixed doVanish = step(0, -0.1); 
0は-0.1より大きいので、step関数の結果doVanishには0が保存される。
そして
 col.a = col.a * doVanish; 
でピクセルのalpha値に0が掛け算される。つまり対象座標のピクセルは消滅する。
1
1

-100p

-10p

+10p

+100p