点光源のライティング計算

前回までのシェーダーは環境光と平行光源だけのライティングたったため、今回は点光源を加えたライティングを行います。

これまでのリアルタイムレンダリング環境下における平行光源では、光源の向きだけを反映させた計算をしていました。
これは光源に位置という概念を持たせず、現実世界で生じる光の減衰を考慮しないということです。

一方、点光源には位置の概念を持たせ、光の減衰を考慮した計算を行います。
今回は光源と対象物の線形距離を減衰率に反映させるため、減衰係数については、

$$ Atten = \frac{ 1.0 }{ |\vec{L} - \vec{V}| } $$

を適用することとします。

また、ここでの点光源の光は全方向へ放出させるため、対象物ごとに光源ベクトルを都度算出します。
その後はこれまでと同様に、

$$ \vec{L} \cdot \vec{N} = |\vec{L}||\vec{N}|\cos \theta $$

を適用することとします。

点光源の実装

まずは点光源のみのライティング計算を実装し、光源と対象物の距離による光の減衰を確認します。

Shader "Custom/Shader" {
    SubShader {
        Pass {
            Tags { "LightMode" = "ForwardAdd" }
            Blend One One

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            struct v2f {
                float4 pos      : SV_POSITION;
                float3 normal   : NORMAL;
                float4 posWorld : TEXCOORD0;
            };

            fixed4 _LightColor0;

            v2f vert(appdata v) {
                v2f o;
                o.pos      = mul(UNITY_MATRIX_MVP, v.vertex);
                o.normal   = normalize(mul(float4(v.normal, 0), unity_WorldToObject).xyz);
                o.posWorld = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            half4 frag(v2f i) : SV_Target {
                float3 light;
                float  atten;
                if (_WorldSpaceLightPos0.w == 0) {
                    light = normalize(_WorldSpaceLightPos0.xyz);
                    atten = 1.0;
                } else {
                    float3 toLight = _WorldSpaceLightPos0.xyz - i.posWorld.xyz;
                    light = normalize(toLight);
                    atten = 1.0 / length(toLight);
                }
                float3 view = normalize(_WorldSpaceCameraPos.xyz - i.posWorld);
                float3 r    = max(0, dot(reflect(-light, i.normal), view));

                fixed3 diffuse  = _LightColor0.rgb * max(0, dot(light, i.normal)) * atten;
                fixed3 specular = _LightColor0.rgb * pow(r, 10.0);

                return half4(diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
}

環境光・平行光源・点光源の合成

前項のライティング処理にパスを加えて、2 パスの Blend で加算合成します。

Shader "Custom/Shader" {
    SubShader {
        Pass {
            Tags { "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            struct v2f {
                float4 pos      : SV_POSITION;
                float3 normal   : NORMAL;
                float4 posWorld : TEXCOORD0;
            };

            fixed4 _LightColor0;

            v2f vert(appdata v) {
                v2f o;
                o.pos      = mul(UNITY_MATRIX_MVP, v.vertex);
                o.normal   = normalize(mul(float4(v.normal, 0), unity_WorldToObject).xyz);
                o.posWorld = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            half4 frag(v2f i) : SV_Target {
                float3 light = normalize(_WorldSpaceLightPos0.xyz);
                float3 view  = normalize(_WorldSpaceCameraPos.xyz - i.posWorld);
                float3 r     = max(0, dot(reflect(-light, i.normal), view));

                fixed3 diffuse  = _LightColor0.rgb * max(0, dot(light, i.normal));
                fixed3 specular = _LightColor0.rgb * pow(r, 10.0);

                return half4(UNITY_LIGHTMODEL_AMBIENT.rgb + diffuse + specular, 1.0);
            }
            ENDCG
        }

        Pass {
            Tags { "LightMode" = "ForwardAdd" }
            Blend One One

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            struct v2f {
                float4 pos      : SV_POSITION;
                float3 normal   : NORMAL;
                float4 posWorld : TEXCOORD0;
            };

            fixed4 _LightColor0;

            v2f vert(appdata v) {
                v2f o;
                o.pos      = mul(UNITY_MATRIX_MVP, v.vertex);
                o.normal   = normalize(mul(float4(v.normal, 0), unity_WorldToObject).xyz);
                o.posWorld = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            half4 frag(v2f i) : SV_Target {
                float3 light;
                float  atten;
                if (_WorldSpaceLightPos0.w == 0) {
                    light = normalize(_WorldSpaceLightPos0.xyz);
                    atten = 1.0;
                } else {
                    float3 toLight = _WorldSpaceLightPos0.xyz - i.posWorld.xyz;
                    light = normalize(toLight);
                    atten = 1.0 / length(toLight);
                }
                float3 view = normalize(_WorldSpaceCameraPos.xyz - i.posWorld);
                float3 r    = max(0, dot(reflect(-light, i.normal), view));

                fixed3 diffuse  = _LightColor0.rgb * max(0, dot(light, i.normal)) * atten;
                fixed3 specular = _LightColor0.rgb * pow(r, 10.0);

                return half4(diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
}

ライセンス表記

ユニティちゃんライセンス

この作品はユニティちゃんライセンス条項の元に提供されています