鏡面反射光の計算

前回のシェーダーで拡散反射光を実装したので、今回は鏡面反射光を実装します。

ある面に対して、拡散反射光は光源ベクトルに依存していましたが、鏡面反射光は光源ベクトルとカメラベクトルに依存します。
つまり、拡散反射光はどの視点でも一定、鏡面反射光は視点によって変化するということです。

まずは光源ベクトル(\(\vec{L}\))と面の法線ベクトル(\(\vec{N}\))から鏡面反射ベクトルを求めます。

$$ \vec{R} = 2\vec{N}(\vec{L} \cdot \vec{N}) - \vec{L} $$

鏡面反射光の輝度は、鏡面反射ベクトル(\(\vec{R}\))とカメラベクトル(\(\vec{V}\))の内積の自乗なので、

$$ (\vec{R} \cdot \vec{V})^n $$

になります。

ここでの自乗は Direct3D マテリアルの Power に該当するもので、式が示すとおり内積の減衰に影響します。

鏡面反射光の実装

鏡面反射光の計算式が定義できたので実装します。

Shader "Custom/Shader" {
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            struct v2f {
                float4 pos   : SV_POSITION;
                half4  color : COLOR0;
            };

            fixed4 _LightColor0;

            v2f vert(appdata v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                float3 normal = normalize(mul(float4(v.normal, 0), unity_WorldToObject).xyz);
                float3 light  = normalize(_WorldSpaceLightPos0.xyz);

                float3 view = normalize(_WorldSpaceCameraPos - mul(unity_ObjectToWorld, v.vertex).xyz);
                float3 r    = max(0, dot(reflect(-light, normal), view));

                half3 specular = _LightColor0.rgb * pow(r, 10.0);
                o.color = fixed4(specular, 1.0);

                return o;
            }

            half4 frag(v2f i) : SV_Target {
                return i.color;
            }
            ENDCG
        }
    }
}

環境光・拡散反射光・鏡面反射光の合成

前回と同様に環境光・拡散反射光を計算し、鏡面反射光とあわせると下記のような結果になります。

Shader "Custom/Shader" {
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            struct v2f {
                float4 pos   : SV_POSITION;
                half4  color : COLOR0;
            };

            fixed4 _LightColor0;

            v2f vert(appdata v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                float3 normal = normalize(mul(float4(v.normal, 0), unity_WorldToObject).xyz);
                float3 light  = normalize(_WorldSpaceLightPos0.xyz);

                float3 view = normalize(_WorldSpaceCameraPos - mul(unity_ObjectToWorld, v.vertex).xyz);
                float3 r    = max(0, dot(reflect(-light, normal), view));

                half3 diffuse  = _LightColor0.rgb * max(0, dot(light, normal));
                half3 specular = _LightColor0.rgb * pow(r, 10.0);
                o.color = fixed4(UNITY_LIGHTMODEL_AMBIENT.rgb + diffuse + specular, 1.0);

                return o;
            }

            half4 frag(v2f i) : SV_Target {
                return i.color;
            }
            ENDCG
        }
    }
}

ライセンス表記

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

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