💈

Unityで床屋のサインポールシェーダーを作ってみた(ShaderLab編)

2022/01/23に公開

こういうシェーダーを作ってみたので紹介です。

https://twitter.com/shiomusubi290/status/1482718771748179969?s=20

ShaderLabでの実装編とShaderGraphでの実装編で紹介しようと思います。

ShaderGraph編はこちら↓
https://zenn.dev/shiomusubi290/articles/ac5833706b5f9c

ShaderLabでの実装

ソースコードは以下の通りです。

Shader "Unlit/tokoya"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 localPos: TEXCOORD0;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.localPos = v.vertex.xyz;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float f = frac((i.localPos.y-i.localPos.z*0.5) * 1.5 + _Time.y );
                
                half4 col;
                if (f <= 0.25) {
                    col = half4(1,0,0,1);
                }
                else if (0.5 < f && f <= 0.75) {
                    col = half4(0,0,1,1);
                }
                else {
                    col = half4(1,1,1,1);
                }
                return col;
            }
            ENDCG
        }
    }
}

メインの処理は以下の部分です。

float f = frac((i.localPos.y-i.localPos.z*0.5) * 1.5 + _Time.y );

ここで何をやっているかを解説していきます。(自分の備忘録も含め、少し丁寧めに解説します)

i.localPos.y-i.localPos.z*0.5 について

まず、 i.localPos.y-i.localPos.z の部分は、ローカル座標のy成分とz成分を足し合わせています(-zを足しています)。

「ローカル座標」の部分は「ワールド座標」でも動作します。しかしその場合は、以下の記事にあるような問題(オブジェクトを動かすと描画されているものも一緒に動いてしまう)が発生するので、ローカル座標の方がおすすめです。
https://zenn.dev/kento_o/articles/9209e0289fa7cbc2dd16

y成分とz成分を足しているのは、斜めの線を作るためです。また、 -z としているのは、傾きを逆にしたかったからです。
左から、y成分のみ、z成分のみ、y+zの状態での描画です。

z成分に対して0.5をかけていますが、これにより斜めの傾きが緩やかになります。
足されるz成分の値が小さくなるので、もともとのy成分の横線の状態に近づくわけですね。

f = frac((i.localPos.y-i.localPos.z*0.5) * 1.5 + _Time.y) について

ここで先ほどの式を1.5倍していますが、ここはなんとなくで、斜めの線の密度を増やしています。
左が普通の状態で、右が1.5倍後です。

ここに _Time.y の値を足すことで、斜めの線が下に動いていきます。
_Time.y については以下の公式ドキュメントを参照してください。
https://docs.unity3d.com/ja/2021.2/Manual/SL-UnityShaderVariables.html

斜めの線が右下に動く理由は、 frac を使っていることが関係しています。
frac を使うことで小数部分が取得できます。例えばいま f=0.2 となるピクセルにいるとして、次の瞬間には_Time.y の値が加算されて f=0.3 になっているとしましょう。
この場合、さっきまで f=0.3 のピクセルで表示していた色と同じものを表示することになります。
いまy軸は上が正で、z軸は(-zなので)左が正なので、fがすべてのピクセルで計算された結果、今自分がいるピクセルよりも左上(座標が大きくなるのでfの値も大きくなる)であるピクセルの色が いまの自分の場所(f=0.2の場所)へ、自分の場所の色はf=0.1(座標の小さい右下)のピクセルへ...となり右下の方へ動くということです。

あとはfの値をif文で条件分岐して色を塗り分ければ完成です。

Discussion