Version: 2023.2
言語: 日本語
サーフェスシェーダーでのカスタムライティングモデル
DX11/OpenGL コアテッセレーションを伴うサーフェスシェーダー

サーフェスシェーダーライティングの例

このページでは、サーフェスシェーダーのカスタムサーフェスシェーダーのライティングモデルの例を紹介します。もっと一般的なサーフェスシェーダーについては、サーフェスシェーダーの例を参照してください。

Deferred Shading はカスタムのマテリアル単位のライティングモデルではうまく機能しない場合もあるため、以下の例のほとんどは、シェーダーを Forward Rendering のみにコンパイルしています。

レンダーパイプラインの互換性

機能名 ビルトインレンダーパイプライン ユニバーサルレンダーパイプライン (URP) HD レンダーパイプライン (HDRP) カスタム SRP
サーフェスシェーダー 不可

URP で Shader オブジェクトを効率的に作成する方法については、Shader Graph を参照してください。
不可

HDRP で Shader オブジェクトを効率的に作成する方法については、Shader Graph を参照してください。
なし

ディフューズ

以下は、ビルトインの Lambert ライティングモデルを使用するシェーダーです。

  Shader "Example/Diffuse Texture" {
      Properties {
        _MainTex ("Texture", 2D) = "white" {}
      }
      SubShader {
        Tags { "RenderType" = "Opaque" }
        CGPROGRAM
        #pragma surface surf Lambert
      
        struct Input {
            float2 uv_MainTex;
        };
      
        sampler2D _MainTex;
      
        void surf (Input IN, inout SurfaceOutput o) {
            o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        }
        ENDCG
      }
      Fallback "Diffuse"
    }

これは、シーンにディレクショナルライトが 1つあるとき、テクスチャがある場合とない場合の見え方の違いです。

以下の例は、ビルトインの Lambert モデルを使用する代わりに、カスタマイズしたライティングモデルを作成して同じ効果を得る方法を示しています。

これを行うには、多くのサーフェスシェーダーライティングモデル関数の使用が必用です。ここに簡易な Lambert ライティングモデルがあります。CGPROGRAM セクションだけが変わっていることに注目してください。周囲のシェーダーコードはまったく変更されていません。

    Shader "Example/Diffuse Texture" {
            Properties {
                _MainTex ("Texture", 2D) = "white" {}
            }
            SubShader {
            Tags { "RenderType" = "Opaque" }
            CGPROGRAM
              #pragma surface surf SimpleLambert
  
              half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {
                  half NdotL = dot (s.Normal, lightDir);
                  half4 c;
                  c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
                  c.a = s.Alpha;
                  return c;
              }
  
            struct Input {
                float2 uv_MainTex;
            };
        
            sampler2D _MainTex;
        
            void surf (Input IN, inout SurfaceOutput o) {
                o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
            }
            ENDCG
            }
            Fallback "Diffuse"
        }

この簡易なディフューズライティングモデルでは、LightingSimpleLambert 関数が使われています。LightingSimpleLambert 関数は表面法線と光方向間のドット積を行うことでライティングを計算し、光の軽減や色を適用します。

ディフューズラップ

以下は、 ラップされたディフューズ です。照明がオブジェクトの端を「包み込む」ような ディフューズ ライティングの変形です。表面下の散乱効果をシミュレーションするのに便利です。CGPROGRAM セクションだけが変更されています。ここでも、周囲のシェーダーコードは同じなので省きます。

    ...ShaderLab code...
        CGPROGRAM
        #pragma surface surf WrapLambert

        half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) {
            half NdotL = dot (s.Normal, lightDir);
            half diff = NdotL * 0.5 + 0.5;
            half4 c;
            c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten);
            c.a = s.Alpha;
            return c;
        }

        struct Input {
            float2 uv_MainTex;
        };
    
        sampler2D _MainTex;
            void surf (Input IN, inout SurfaceOutput o) {
            o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        }
        ENDCG
        ...ShaderLab code...

これは、シーンにディレクショナルライトが 1つあるとき、テクスチャがある場合とない場合の見え方の違いです。

トゥーンランプ

以下の例は、テクスチャのランプを使用して表面が光と法線間の角度にどのように反応するかを決定する “Ramp” ライティングモデルを表示しています。これは、さまざまなエフェクトに使用され、特に トゥーン ライティングとともに使用すると効果的です。

    ...ShaderLab code...
        CGPROGRAM
        #pragma surface surf Ramp

        sampler2D _Ramp;

        half4 LightingRamp (SurfaceOutput s, half3 lightDir, half atten) {
            half NdotL = dot (s.Normal, lightDir);
            half diff = NdotL * 0.5 + 0.5;
            half3 ramp = tex2D (_Ramp, float2(diff)).rgb;
            half4 c;
            c.rgb = s.Albedo * _LightColor0.rgb * ramp * atten;
            c.a = s.Alpha;
            return c;
        }

        struct Input {
            float2 uv_MainTex;
        };
    
        sampler2D _MainTex;
    
        void surf (Input IN, inout SurfaceOutput o) {
            o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        }
        ENDCG
        ...ShaderLab code...

これは、シーンにディレクショナルライトが 1つあるとき、テクスチャがある場合とない場合の見え方の違いです。

簡単なスペキュラー

以下の例は、ビルトインの BlinnPhong ライティングモデルに似た簡単なスペキュラーモデルを示しています。

    ...ShaderLab code...
        CGPROGRAM
        #pragma surface surf SimpleSpecular

        half4 LightingSimpleSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
            half3 h = normalize (lightDir + viewDir);

            half diff = max (0, dot (s.Normal, lightDir));

            float nh = max (0, dot (s.Normal, h));
            float spec = pow (nh, 48.0);

            half4 c;
            c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * atten;
            c.a = s.Alpha;
            return c;
        }

        struct Input {
            float2 uv_MainTex;
        };
    
        sampler2D _MainTex;
    
        void surf (Input IN, inout SurfaceOutput o) {
            o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        }
        ENDCG
        ...ShaderLab code...

これは、シーンにディレクショナルライトが 1つあるとき、テクスチャがある場合とない場合の見え方の違いです。

カスタム GI

Unity のビルトイン GI を模倣したシェーダーから紹介します。

    Shader "Example/CustomGI_ToneMapped" {
            Properties {
                _MainTex ("Albedo (RGB)", 2D) = "white" {}
            }
            SubShader {
                Tags { "RenderType"="Opaque" }
            
                CGPROGRAM
                #pragma surface surf StandardDefaultGI
    
                #include "UnityPBSLighting.cginc"
    
                sampler2D _MainTex;
    
                inline half4 LightingStandardDefaultGI(SurfaceOutputStandard s, half3 viewDir, UnityGI gi)
                {
                    return LightingStandard(s, viewDir, gi);
                }
    
                inline void LightingStandardDefaultGI_GI(
                    SurfaceOutputStandard s,
                    UnityGIInput data,
                    inout UnityGI gi)
                {
                    LightingStandard_GI(s, data, gi);
                }
    
                struct Input {
                    float2 uv_MainTex;
                };
    
                void surf (Input IN, inout SurfaceOutputStandard o) {
                    o.Albedo = tex2D(_MainTex, IN.uv_MainTex);
                }
                ENDCG
            }
            FallBack "Diffuse"
        }

次に、GI 上にトーンマッピングを追加します。

    Shader "Example/CustomGI_ToneMapped" {
            Properties {
                _MainTex ("Albedo (RGB)", 2D) = "white" {}
                _Gain("Lightmap tone-mapping Gain", Float) = 1
                _Knee("Lightmap tone-mapping Knee", Float) = 0.5
                _Compress("Lightmap tone-mapping Compress", Float) = 0.33
            }
            SubShader {
                Tags { "RenderType"="Opaque" }
            
                CGPROGRAM
                #pragma surface surf StandardToneMappedGI
    
                #include "UnityPBSLighting.cginc"
    
                half _Gain;
                half _Knee;
                half _Compress;
                sampler2D _MainTex;
    
                inline half3 TonemapLight(half3 i) {
                    i *= _Gain;
                    return (i > _Knee) ? (((i - _Knee)*_Compress) + _Knee) : i;
                }
    
                inline half4 LightingStandardToneMappedGI(SurfaceOutputStandard s, half3 viewDir, UnityGI gi)
                {
                    return LightingStandard(s, viewDir, gi);
                }
    
                inline void LightingStandardToneMappedGI_GI(
                    SurfaceOutputStandard s,
                    UnityGIInput data,
                    inout UnityGI gi)
                {
                    LightingStandard_GI(s, data, gi);
    
                    gi.light.color = TonemapLight(gi.light.color);
                    #ifdef DIRLIGHTMAP_SEPARATE
                        #ifdef LIGHTMAP_ON
                            gi.light2.color = TonemapLight(gi.light2.color);
                        #endif
                        #ifdef DYNAMICLIGHTMAP_ON
                            gi.light3.color = TonemapLight(gi.light3.color);
                        #endif
                    #endif
                    gi.indirect.diffuse = TonemapLight(gi.indirect.diffuse);
                    gi.indirect.specular = TonemapLight(gi.indirect.specular);
                }
    
                struct Input {
                    float2 uv_MainTex;
                };
    
                void surf (Input IN, inout SurfaceOutputStandard o) {
                    o.Albedo = tex2D(_MainTex, IN.uv_MainTex);
                }
                ENDCG
            }
            FallBack "Diffuse"
        }
サーフェスシェーダーでのカスタムライティングモデル
DX11/OpenGL コアテッセレーションを伴うサーフェスシェーダー