夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

ゼロから始めるUnityShader開発 ShaderGraphのNormal Vectorノードをコードで再現する

本日はUnityシェーダー枠です。

〇Normal Vectorノード

ShaderGraphのNormal Vectorノードはオブジェクトの持つ法線の値を使用することができます。

一般的に法線の向きに拡大縮小するアニメーションやNoramlMapなどに使用されています。

Normal Vectorノードの出力をカラーとして再現すると次のような見た目を得ることができます。

簡単に表すと面の向きに合わせて色がついているということになります。

〇コードであらわす

モデルの持つ情報からノーマル(法線)を使用するために構造体にfloat3 normal を定義します。

     struct appdata
        {
    ・・・
            float3 normal : NORMAL;
        };

頂点シェーダーではまずワールド法線を取得します。

これはUnityObjectToWorldNormalメソッドに引数としてモデルの法線(normal)を与えてあげることで可能です。

次にカメラに対してのモデルの法線を取得します。これは取得したworldNormalとUNITY_MATRIX_Vの行列によって取得できます。

UNITY_MATRIX_VはUnityのカメラ向きの取得を行うことができるシェーダーマクロです。 Vはビューを意味しておりビュー行列とも呼ばれます。

そしてさらにUNITY_MATRIX_I_VviewNormalの行列をとることで最終的なNormal Vectorを取得することができます。

UNITY_MATRIX_I_Vはビュー行列の逆行列になります。これはカメラ空間からワールド空間への座標変換を意味します。

  v2f vert(appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            
            float3 worldNormal = UnityObjectToWorldNormal(v.normal);

            float3 viewNormal = mul((float3x3)UNITY_MATRIX_V, worldNormal);
            
            o.objectNormal = mul((float3x3)UNITY_MATRIX_I_V, viewNormal);

            return o;
        }

この出力を格納するためにv2f構造体にobjectNormalを追加します。

        struct v2f
        {
            float3 objectNormal : TEXCOORD0;
    ・・・
        };

以上でフラグメントシェーダーでobjectNormalを使用することで全く同じ見た目を再現することができます。(画像左がコード、右がShaderGraphです。)

本日は以上です。

〇コード

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

        Pass
        {
            CGPROGRAM
       #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

        struct appdata
        {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
        };

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

        v2f vert(appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            
            float3 worldNormal = UnityObjectToWorldNormal(v.normal);

            float3 viewNormal = mul((float3x3)UNITY_MATRIX_V, worldNormal);
            
            o.objectNormal = mul((float3x3)UNITY_MATRIX_I_V, viewNormal);

            return o;
        }

        fixed4 frag(v2f i) : SV_Target
        {
            float3 albedo = i.objectNormal;

            // Output color
            return fixed4(albedo, 1.0);
        }
            ENDCG
        }
    }
}