Unity Custom Shader 記述におけるライティングの基礎 (URP 環境)

Unity シェーダーでライティングを記述する際の要点について覚え書き.
今回はテクスチャを使わず頂点ごとに色が設定されている場合に対するライティングの設定を行う

なお, レンダーパイプラインについては URP 環境で開発を行う.

検証環境

最低限のテンプレート

Shader "LightingURP/LambertLighting"
{
    Properties
    { }

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            HLSLPROGRAM
            
            #pragma vertex vert
            
            #pragma fragment frag

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

            struct Attributes
            {
                // vertices position of object space
                float4 positionOS   : POSITION;
            };

            struct Varyings
            {
                // vertices position of screen space
                float4 positionHCS  : SV_POSITION;
            };

            // vertex shader
            Varyings vert(Attributes IN)
            {
                // output object
                Varyings OUT;

                // position change with noise
                float3 outPos = IN.positionOS.xyz;

                // to clip space
                OUT.positionHCS = TransformObjectToHClip(outPos);

                return OUT;
            }

            // fragment shader
            half4 frag() : SV_Target
            {
                // return color
                half4 customColor = half4(0.5, 0, 0, 1);
                return customColor;
            }
            ENDHLSL
        }
    }
}
        

ライティングに必要なファイルのインポート

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

必要な変数の定義

struct Varyings
{
    // vertices position of screen space
    float4 positionHCS  : SV_POSITION;
    half3 lightAmount : TEXCOORD2;
    float4 color : COLOR;
};
        

頂点シェーダーに各頂点の色の編集とライティングの計算を追加

float xNormalized = saturate((IN.positionOS.x + 1.0) * 0.5);
OUT.color = float4(xNormalized, 0.0, 1.0 - xNormalized, 1.0);
        
// lighting
VertexNormalInputs positions = GetVertexNormalInputs(IN.positionOS);

Light light = GetMainLight();

OUT.lightAmount = LightingLambert(light.color, light.direction, positions.normalWS.xyz);
        

フラグメントシェーダーで光量に応じて色を変化させる

// fragment shader
half4 frag(Varyings IN) : SV_Target
{
    // lighting
    float3 litColor = IN.color.rgb * IN.lightAmount;
    return float4(litColor, 1.0);
}
        

球に反映させる

shader image

サンプルコード

Shader "LightingURP/LambertLighting"
{
    Properties
    { }

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        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 Attributes
            {
                // vertices position of object space
                float4 positionOS : POSITION;
                // float4 color : COLOR; // vertex color
            };

            struct Varyings
            {
                // vertices position of screen space
                float4 positionHCS  : SV_POSITION;
                half3 lightAmount : TEXCOORD2;
                float4 color : COLOR;
            };

            // vertex shader
            Varyings vert(Attributes IN)
            {
                // output object
                Varyings OUT;

                // position change with noise
                float3 outPos = IN.positionOS.xyz;

                // to clip space
                OUT.positionHCS = TransformObjectToHClip(outPos);

                // vertex processing
                float xNormalized = saturate((IN.positionOS.x + 1.0) * 0.5);
                OUT.color = float4(xNormalized, 0.0, 1.0 - xNormalized, 1.0);


                // lighting
                VertexNormalInputs positions = GetVertexNormalInputs(IN.positionOS);

                Light light = GetMainLight();

                OUT.lightAmount = LightingLambert(light.color, light.direction, positions.normalWS.xyz);

                return OUT;
            }

            // fragment shader
            half4 frag(Varyings IN) : SV_Target
            {
                // lighting
                float3 litColor = IN.color.rgb * IN.lightAmount;
                return float4(litColor, 1.0);
            }
            ENDHLSL
        }
    }
}