はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】頂点移動を題材にShaderGraphからコーディングを考える

はじめに

SRP(Scriptable Render Pipeline)にて利用可能なShaderGraphですが、標準レンダリングパイプラインでは利用することができません。

私はShaderGraphからShaderを触り始めた身なのですが、どうしてもSRPが使えない環境(プロジェクト)もありボチボチコーディングの方の勉強もしています。

今回は簡単な頂点の移動を題材にShaderGraphと頂点シェーダーの両側面から見ていきたいと思います。

f:id:hanaaaaaachiru:20200520143830g:plain

ShaderGraph

まずは実際に作ったノード達はこちら。

f:id:hanaaaaaachiru:20200520144723p:plain

ShaderGraphを触ったことのある方ならすぐ理解できると思いますが、World座標のy座標にSinTime(-1~1)を足し合わせる処理になっています。

ShaderGraphのノードについて

ShaderGraphのノードの詳細は以下のドキュメントにそれぞれ書かれています。
Combine Node | Shader Graph | 6.9.2

このドキュメントには全てではないですが、ノードをコードに変換したらどのようなコードになるかのサンプルが書かれています。(環境によって言語が異なったりするのでニュアンスとして捉えると良さそう)

例えばCombineノードのサンプルコードはこんな感じ。

void Unity_Combine_float(float R, float G, float B, float A, out float4 RGBA, out float3 RGB, out float2 RG)
{
    RGBA = float4(R, G, B, A);
    RGB = float3(R, G, B);
    RG = float2(R, G);
}

このコードから今回の場合は3つのfloatを結合してfloat3に変換していることが分かります。

もし私みたいにShaderGraphから始めた人はこれを見る癖をつけると、コーディングの上達も早いと思います。

コーディングする

最初に作成したノード達はWorld座標のy座標にSinTimeを足し合わせるという処理です。

これをコードに変換するとこのようになります。

v.vertex += float4(0, _SinTime.w, 0, 0);

_SinTimeというのはShaderLabの定義済みの値で、TimeノードのSinTime
に該当します。
Unity - マニュアル: ShaderLab 定義済みの値

_SinTimefloat4で、float4(t/8, t/4, t/2, t)となり_SinTime.wなら等倍の時間軸で値が遷移します。(-1 ~ 1)

コードにするとかなり短くなりますよね。

コード

ただそのほかもろもろの設定が必要なので、先ほどのコードを組み込んでみましょう。

Shader "Unlit/Test"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                // ここが一番大切なところ
                v.vertex += float4(0, _SinTime.w, 0, 0);
                    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

さいごに

このその他諸々が難しいんじゃないかという話だと思いますが、少し前からYoutubeにてシェーダーの仕組みを分かりやすく解説してくれている方がいらっしゃいました。

www.youtube.com

というか私はこの動画に影響されてコーディングを始めちゃいました。

かなり分かりやすく解説されているのでオススメです。

今回は本当の触りしかしませんでしたが、今後はもっと実用的なものなんかも作れたらと思うので良かったらお付き合いください。

ではまた。