ShaderTips

シェーダーTips

主にUnityシェーダーについての記事を書いています。

【Unity】幾何減衰

マイクロファセット(微細表面)

マイクロファセットとは物体の表面には目では見えない凹凸のことです。

f:id:Ny_Program:20210621090020p:plain 物理ベースレンダリング入門 その① - 物理ベースレンダリングとは? - LIGHT11引用

幾何減衰

マイクロファセットにより入射光が遮断される(シャドウイング)ことがあります。

https://cdn-ak.f.st-hatena.com/images/fotolife/h/halya_11/20200228/20200228145813.png 物理ベースレンダリング入門 その① - 物理ベースレンダリングとは? - LIGHT11引用

同様に反射光もマイクロファセットにより遮断されます(マスキング)。 幾何減衰はこのマイクロファセットによる光の遮断による反射光の成分の減衰を計算するものです。

https://cdn-ak.f.st-hatena.com/images/fotolife/h/halya_11/20200228/20200228150052.png 物理ベースレンダリング入門 その① - 物理ベースレンダリングとは? - LIGHT11引用

youtu.be

幾何減衰にはいくつか種類があるようですが、今回はHeight-Correlated Smithモデルを使用します。

 {\displaystyle
V(v,l,\alpha)= \frac{0.5}{\Lambda _{l}+\Lambda_{v}} 
}

 { \displaystyle
\Lambda _{l}=n \cdot v \sqrt{(n \cdot l)^{2}(1.0-\alpha ^{2})+\alpha ^{2}} \
}

実装

Shader "SmithGGXCorrelated"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Roughness("Roughness", Range(0.0, 1.0)) = 0.5
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                half3 worldNormal : TEXCOORD2;
                half3 viewDir : TEXCOORD3;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.viewDir = UnityWorldSpaceViewDir(worldPos);
                return o;
            }

            sampler2D _MainTex;
            float _Roughness;
            float3 _LightColor0;

            // 幾何減衰(V項) マイクロファセットの凹凸に遮れた反射光
            inline float V_SmithGGXCorrelated(float ndotl, float ndotv, float alpha)
            {
                float lambdaV = ndotl * (ndotv * (1 - alpha) + alpha);
                float lambdaL = ndotv * (ndotl * (1 - alpha) + alpha);

                return 0.5f / (lambdaV + lambdaL + 0.0001);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                half3 normal = normalize(i.worldNormal);
                half3 viewDir = normalize(i.viewDir);
                half ndotv = abs(dot(normal, viewDir));
                float ndotl = max(0, dot(normal, _WorldSpaceLightPos0.xyz));
                
                float alpha = _Roughness * _Roughness;
                half V = V_SmithGGXCorrelated(_Roughness,ndotv,alpha);
                return fixed4(V * _LightColor0.rgb,1);
            }
            ENDCG
        }
    }
}

白色のキューブに緑色のライトを当てています。 右に行くほど_Roughness(粗さ)をあげています。 マイクロファセットにより入射光と反射光が遮断される為、右に行くほどライトの色(緑色)が消えています。

f:id:Ny_Program:20210930180643p:plain

関連

ny-program.hatenablog.com

ny-program.hatenablog.com