【Unity】2D SpriteでバリアのようなShaderを作ったので紹介する【歪み】

どーも、ぐるたか@guru_takaです。

今回はGIFのようなShaderを作ったので、ソースを公開。解説、コメントも載せています!

もともと、GIFのようにパーティクルが減衰しちゃうような演出が目的でした。なので、ドロっと下に水滴が落ちるようなバリアっぽい表現にしてます。

参考になれば幸いです!

MEMO
パーティクルの減衰は、コンポーネントAera Effector 2Dをアタッチして実現してます!

ソース

Shader "Barrier"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _DistortionTex ("Disortion Texture(RG)", 2D)            = "grey" {}
        _DistortionPower ("Distortion Power", Range(0, 0.1))     = 0
        _width ("Width", Range(0, 0.2))     = 0.05

    }

    SubShader
    {
        Tags {"Queue"="Transparent" "RenderType"="Transparent" }

        GrabPass { "_GrabPassTexture" }

        Pass {

            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag

           #include "UnityCG.cginc"
           //矩形パルス関数
           #define pulse(a, b, x) (step((a),(x)) - step((b),(x)))

            struct appdata {
                half4 vertex                : POSITION;
                half4 texcoord              : TEXCOORD0;
            };

            struct v2f {
                half4 vertex                : SV_POSITION;
                half2 uv                    : TEXCOORD0;
                half4 grabPos               : TEXCOORD1;
            };

            sampler2D _DistortionTex;
            half4 _DistortionTex_ST;
            sampler2D _GrabPassTexture;
            half _DistortionPower;
            fixed4 _Color;
            float _width;

            v2f vert (appdata v)
            {
                v2f o                   = (v2f)0;

                o.vertex                = UnityObjectToClipPos(v.vertex);
                o.uv                    = TRANSFORM_TEX(v.texcoord, _DistortionTex);
                o.grabPos               = ComputeGrabScreenPos(o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                half2 uv = i.grabPos.xy / i.grabPos.w;

                //歪み用テクスチャをuvスクロール
                float2 uvTmp = i.uv ;
                uvTmp.y += _Time.x * 2;

                //rgは0~1の範囲にあるので、-0.5~0.5にずらす
                half2 distortion = tex2D(_DistortionTex, uvTmp).rg - 0.5;

                //歪みの強さ
                distortion *= _DistortionPower;

                //円外(uv座標内の半径0.5の外)のピクセルは破棄
                float dst = distance(float2(0.5, 0.5), i.uv);
                if (dst > 0.5)
                    discard;

                //円の内側の半径
                float insideRad = 0.5 - _width;

                //背面の歪み
                uv += distortion;
                fixed4 distortColor = tex2D(_GrabPassTexture, uv)* step(dst,insideRad);
                //円縁
                fixed4 circleOutline = _Color * pulse(insideRad,0.5,dst);

                return distortColor + circleOutline;
            }
            ENDCG
        }
    }
}

主な処理の流れ

STEP.1
grabPassで背面を取得
STEP.2
ノーマルマップで背面を歪ませる
STEP.3
円縁を追加して完成!

STEP1:grabPassで背面を取得

まずはgrabPassで背面画像を取得します。
関連ソースは以下の通りです。

        Tags {"Queue"="Transparent" "RenderType"="Transparent" }

        GrabPass { "_GrabPassTexture" }

      Pass {

            sampler2D _GrabPassTexture;

              }

GrabPass {} と明記すると、背面画像が取れます!ただ、注意すべきこともあり、Tags {"Queue"="Transparent"}にする必要があります。

不透明なすべてのオブジェクトを描画した後に背面を取得しないといけないので、"Queue"="Transparent"にしています!

参考 ShaderLab: GrabPassUnity公式

また以下のソースでは、背面画像のUV座標を取得しています。射影空間からテクスチャ座標に変換するものです。”そういうもの”と思えば良いでしょう笑

            v2f vert (appdata v)
            {
                o.grabPos               = ComputeGrabScreenPos(o.vertex);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                half2 uv = i.grabPos.xy / i.grabPos.w;
            }

ここでの解説は割愛しますが、ご興味ある方はこちらの関連リンクをチェックしてみてください!
参考 Standard Assetsの「GlassStainedBumpDistort」シェーダを覗いてみたe.blog 参考 射影テクスチャリングProject ASURA

STEP2:ノーマルマップで背面を歪ませる

次はノーマルマップのRG要素で背面を歪ませています。非常にシンプルです!テクスチャで歪ませるイメージや仕組みは、私が記載したQiitaの記事をご覧ください!
参考 【Unity】テクスチャをシンプルに歪ませるshaderの作成方法Qiita

超ザックリ解説すると、ノーマルマップの凹凸(RG要素)に紐づいて、UV座標が変化します。その結果、背面が歪むように映るイメージです!

あとはUVにスクロールして、ノーマルマップが下向きに動くようにしました。distortion *= _DistortionPower;は歪みの強さを表してます。こんな感じで歪む強度が変化します。

今回使用したノーマルマップはこちらです。

STEP3:円縁を追加して完成!

最後に、円縁を追加します。流れはこんな感じ!

  1. UV座標系で半径0.5以外のピクセルを全て破棄
  2. 円縁を短径パルス関数で作成
  3. 歪み背面と円縁を足して完成!
                //1. 円外(uv座標内の半径0.5の外)のピクセルは破棄
                float dst = distance(float2(0.5, 0.5), i.uv);
                if (dst > 0.5)
                    discard;

                //円の内側の半径
                float insideRad = 0.5 - _width;

                //背面の歪み
                uv += distortion;
                fixed4 distortColor = tex2D(_GrabPassTexture, uv)* step(dst,insideRad);
                //2. 円縁
                fixed4 circleOutline = _Color * pulse(insideRad,0.5,dst);

                //3. 背面+円縁
                return distortColor + circleOutline;

短径パルス関数は#define pulse(a, b, x) (step((a),(x)) - step((b),(x)))で定義しています。

引用:Shader(HLSL), 手続き的にテクスチャ生成など行うとき使用頻度の高い関数

最後に

あとは、2D Spriteを作成し、適当な正方形のSprite画像をアタッチ。そして、上記shaderに基づくマテリアルをアタッチすれば完成です!


参考リンク

参考 【Unity】【シェーダ】GrabPassを使って歪みシェーダを作るLIGHT11 参考 Unity シェーダーチュートリアル 屈折表現Tsumiki Tech Times

コメントを残す