知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】ノイズシェーダー拡張 【4】#48

前回の成果

ノイズシェーダーでテクスチャを歪ませました。

soramamenatan.hatenablog.com


今回やること

ノイズで背景を歪めます。

sasanon.hatenablog.jp


事前準備

Scene上にPlaneを配置し、今回制作したMaterialをアタッチします。

f:id:soramamenatan:20200329114152p:plain


ソースコード

Shader "Unlit/BackgroundNoise" {
    Properties {
        _NoiseTilingOffset ("NoiseTex Tiling(x,y)/Offset(z,w)", Vector) = (0.1, 0.1, 0,0)
        _NoiseSizeScroll ("NoiseTex Size(x,y)/Scroll(z,w)", Vector) = (16, 16, 0, 0)
        _DistortionPower ("Distortion Power", Float) = 0
    }
    SubShader {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"}
        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha

        GrabPass { "_BackgroundTexture" }

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "extensionNoiseUtil.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                float4 grabuv : TEXCOORD0;
                float2 noiseuv : TEXCOORD1;
            };

            sampler2D _BackgroundTexture;
            fixed4 _NoiseTilingOffset;
            fixed4 _NoiseSizeScroll;
            float _DistortionPower;

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.grabuv = ComputeGrabScreenPos(o.vertex);
                o.noiseuv = TRANSFORM_NOISE_TEX(v.uv, _NoiseTilingOffset, _NoiseSizeScroll);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                fixed3 dist = normalNoise(i.noiseuv, _NoiseSizeScroll.xy);
                dist = dist * 2 - 1;
                dist *= _DistortionPower;
                i.grabuv.xy += dist.xy;
                fixed4 col = tex2Dproj(_BackgroundTexture, UNITY_PROJ_COORD(i.grabuv));
                return col;
            }
            ENDCG
        }
    }
}

extensionNoiseUtil.cgincはこちらを参照してください。

soramamenatan.hatenablog.com


レンダリング結果を取得

GrabPass直前にレンダリングした結果を取得できるようになります。
GrabPassで取得した値は_GrabTextureで取得することができます。

// 直前のレンダリング結果を取得
GrabPass {}

// GrabPassで取得した結果を格納
sampler2D _GrabTexture;

この処理は重いので1度しか呼ばない場合は良いですが何回も呼ぶ場合は名前を指定してあげます。
そうすると1フレームに1回のみテクスチャを取得し、そのテクスチャを使い回すようになるので負荷が減ります。
名前を指定する際には変数名も揃えてください。

// 直前のレンダリング結果を取得
GrabPass { "_hogeTexture" }

// GrabPassで取得した結果を格納
sampler2D _hogeTexture;

light11.hatenadiary.com


座標系を揃える

o.grabuv = ComputeGrabScreenPos(o.vertex);

ComputeGrabScreenPos でGrabPassで取得したテクスチャのUV座標を求めています。
GrabPassで取得したテクスチャはプラットフォームによって座標系が異なってしまうため、この関数を使用します。


テクスチャ座標の計算

fixed4 col = tex2Dproj(_BackgroundTexture, UNITY_PROJ_COORD(i.grabuv));

tex2Dprojはwで除算をしてからtex2Dをしている関数です。

wで除算する理由は下記サイト様が参考になります。

light11.hatenadiary.com

UNITY_PROJ_COORDは投影されたテクスチャ座標を適切なテクスチャ座標に変換するものです。
ほとんどのプラットフォームでは、引数に入れた値がそのまま帰ってきます。


結果

背景が歪んだら成功です。

property

f:id:soramamenatan:20200413082218p:plain

描画結果

f:id:soramamenatan:20200413082159g:plain


ソースコードにコメントを付与

Shader "Unlit/BackgroundNoise" {
    Properties {
        _NoiseTilingOffset ("NoiseTex Tiling(x,y)/Offset(z,w)", Vector) = (0.1, 0.1, 0,0)
        _NoiseSizeScroll ("NoiseTex Size(x,y)/Scroll(z,w)", Vector) = (16, 16, 0, 0)
        _DistortionPower ("Distortion Power", Float) = 0
    }
    SubShader {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"}
        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha

        // 直前のレンダリング結果を取得
        GrabPass { "_BackgroundTexture" }

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "extensionNoiseUtil.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                float4 grabuv : TEXCOORD0;
                float2 noiseuv : TEXCOORD1;
            };

            // GrabPassで取得した結果を格納
            sampler2D _BackgroundTexture;
            fixed4 _NoiseTilingOffset;
            fixed4 _NoiseSizeScroll;
            float _DistortionPower;

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.grabuv = ComputeGrabScreenPos(o.vertex);
                o.noiseuv = TRANSFORM_NOISE_TEX(v.uv, _NoiseTilingOffset, _NoiseSizeScroll);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                fixed3 dist = normalNoise(i.noiseuv, _NoiseSizeScroll.xy);
                dist = dist * 2 - 1;
                dist *= _DistortionPower;
                i.grabuv.xy += dist.xy;
                // tex2Dproj : i.grabuvを除算
                // UNITY_PROJ_COORD : 投影されたテクスチャ座標を正しい座標に変換
                fixed4 col = tex2Dproj(_BackgroundTexture, UNITY_PROJ_COORD(i.grabuv));
                return col;
            }
            ENDCG
        }
    }
}

今回は以上となります。
ここまでご視聴ありがとうございました。