夜風のMixedReality

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

ゼロから始めるUnityShader開発 第五章 ライトの実装 ライティングを実装する

本日はシェーダー学習枠です。

前回まででShaderとは何か?どのように実行されているのか?Unityでの扱い方、URPでのShaderに関して紹介しました。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

これまではライトの影響を受けないUnlitと呼ばれる表現のシェーダーを記述していました。

今回はUnityのライトを受け取るシェーダーを記述します。

〇LitShader

ライトの影響を受けるシェーダーのことをLitShaderと呼んでいます。

今回は前回までのこちらのシェーダーをもとに作成していきます。

Shader "Unlit/TutorialShader"
{
    Properties
    {
     //   _MainTex ("Texture", 2D) = "white" {}
        _MainColor("Color" ,color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
            float4 _MainColor;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = TransformObjectToHClip(v.vertex);
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                float4 col = _MainColor;
                return col;
            }
            ENDHLSL
        }
    }
}

〇Lighting.hlsl

Unityではよく使用する関数がいくつかのHLSLファイルで提供されています。

例えばURPの場合C#で例えるところのMonobehaviorのような基礎的なUnityの機能を使用するためにCore.hlslをインクルードしています。

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

ライトに関しても同様にLighting.hlslというファイルで定義されていますのでこれを定義します。

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include  "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

次にライトを使用するために必要な情報を定義します。

 ライトは当たっている面の法線(面の向き)に応じてライトの影響を受けます。

 具体的にはライトの向きに対して向いている面にはライトが強く影響し、逆にライトの向きの同じ向きに向いている面は影になります。

 このため法線を取得する必要があります。

まずはInputステージから法線のデータを定義します。

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

 法線は3次元ベクトルとNORMALのセマンティックスで定義されます。

 またライトの処理はフラグメントシェーダーでピクセル単位で行う必要があるためフラグメントシェーダーに渡すための処理を定義します。

            struct v2f
            {
            float4 vertex : SV_POSITION;
            float3 normalWS : TEXCOORD1;
            };

ここではTEXCOORD1のセマンティクスであらわされる座標としてデータを渡します。

データの定義ができたため次に頂点シェーダーの処理を行います。

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = TransformObjectToHClip(v.vertex);
                //面の法線を取得、ライトの当たる向きを計算
                VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
                o.normalWS = normal.normalWS;
                return o;
            }

 ここでは法線ベクトル座標変換を行いnormalWS(ワールド軸のノーマル)へ変換し格納をしています。

一言でいえば法線を変換して渡しています。

VertexNormalInputsは次の3つのデータ型が格納されます。

struct VertexNormalInputs
{
    real3 tangentWS;
    real3 bitangentWS;
    float3 normalWS;
};

最後にフラグメントシェーダーの処理ですが次のようになります。

           float4 frag (v2f i) : SV_Target
            {
                float4 col = _MainColor;
                //Light.hlslで提供されるUnityのライトを取得する関数
                Light lt = GetMainLight();

                //ライトの向きを計算
                float strength = dot(lt.direction, i.normalWS);
                float4 lightColor = float4(lt.color, 1);
                return col* lightColor*strength;
            }

ここではGetMainLight()を使用してUnityのライト情報を取得してLightに格納しています。

このあたりの定義はLighting.hlslにあります。

Lightには次のようなデータが格納されています。

struct Light
{
    half3   direction;//向き
    half3   color;//色
    float   distanceAttenuation; // full-float precision required on some platforms
    half    shadowAttenuation;
    uint    layerMask;
};

ここで必要な情報は向きと色です。

この色にはライトの強さも含まれています。

ライトの向きとメッシュの法線のドット積がライトの強度になります。

一般的にこの計算のことをLdotN(LdN)と表します。

     //ライトの向きを計算
                float strength = dot(lt.direction, i.normalWS);

この時点での出力は次のようになります。

この時点でしっかりとライトの向きが取れて影が現れているのがわかると思います。

 冒頭で述べた通りライトの向きに対している面は1に近く、ライトの向きと同じ向きを向いている面ほど0に近い値が返されています。

ここにlt.colorで格納されているライトの色を積算したものが最終的なライトの計算になります。

                //ライトの向きを計算
                float strength = dot(lt.direction, i.normalWS);
                float4 lightColor = float4(lt.color, 1);

以上がライティングの実装になります。

〇シェーダー全文

Shader "Unlit/TutorialShader"
{
    Properties
    {
     //   _MainTex ("Texture", 2D) = "white" {}
        _MainColor("Color" ,color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include  "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            
            struct appdata
            {
                float4 vertex : POSITION;
                half3 normal: NORMAL;
            };

            struct v2f
            {
            float4 vertex : SV_POSITION;
            float3 normalWS : TEXCOORD1;
            };


            float4 _MainColor;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = TransformObjectToHClip(v.vertex);
                //面の法線を取得、ライトの当たる向きを計算
                VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
                o.normalWS = normal.normalWS;
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                float4 col = _MainColor;
                //Light.hlslで提供されるUnityのライトを取得する関数
                Light lt = GetMainLight();

                //ライトの向きを計算
                float strength = dot(lt.direction, i.normalWS);
                float4 lightColor = float4(lt.color, 1);
                return col* lightColor*strength;
            }
            ENDHLSL
        }
    }
}