学习笔记——基本光照模型简单实现

时间:2021-12-08 10:22:40

Lambert光照模型,根据光照向量与顶点发现的夹角来确定光照强度:

Shader "James/VP Shader/LightModel-Lambert" 
{
    Properties 
    {
        _MainTex ("MainTex", 2D) = "white" {}
    }
    SubShader 
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            float _LightColor0;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 lightDir : TEXCOORD1;
                float3 normal : TEXCOORD2;
            };
            
            v2f vert(appdata_full v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                // 从顶点位置到Light的局部向量
                o.lightDir = ObjSpaceLightDir(v.vertex);
                o.normal = v.normal;
                return o;
            }
            
            float4 frag(v2f i) : COLOR
            {
                i.lightDir = normalize(i.lightDir);
                i.normal = normalize(i.normal);
                // 纹理采样
                float4 c = tex2D(_MainTex, i.uv);
                // 光强度,法向和光照方向的cos值
                float diffuse = max(0, dot(i.normal, i.lightDir));
                // 纹理色 * 光源色 * 强度参数
                c = c * _LightColor0 * diffuse;
                return c * 2;
            }
            ENDCG
        }
    } 
    FallBack "Diffuse"
}

Phong光照模型,根据光照向量的反射向量与视线的夹角来计算镜面高光的强度,另外再加上漫射光的成分:

Shader "James/VP Shader/LightModel-Phong" 
{
    Properties 
    {
        _MainTex ("MainTex", 2D) = "white" {}
        _gloss ("Gloss", Float) = 1
    }
    SubShader 
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            float _LightColor0;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _gloss;
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : TEXCOORD1;
                float3 lightDir : TEXCOORD2;            // 光照方向
                float3 reflectLightDir : TEXCOORD3;        // 光照反射方向
                float3 viewDir : TEXCOORD4;                // 视线方向
            };
            
            v2f vert(appdata_full v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normal = v.normal;
                // 从顶点位置到light的局部向量
                o.lightDir = ObjSpaceLightDir(v.vertex);
                // 反射向量,注意需要使用-o.lightDir
                o.reflectLightDir = reflect(-o.lightDir, v.normal);
                // 视线,顶点到camera的局部变量
                o.viewDir = ObjSpaceViewDir(v.vertex);
                return o;
            }
            
            float4 frag(v2f i) : COLOR
            {
                i.normal = normalize(i.normal);
                i.reflectLightDir = normalize(i.reflectLightDir);
                i.viewDir = normalize(i.viewDir);
                // 纹理采样
                float4 c = tex2D(_MainTex, i.uv);
                // 漫射光强度
                float diffuse = max(0, dot(i.normal, i.lightDir));
                // 镜面光强度
                float specular = max(0, dot(i.reflectLightDir, i.viewDir));
                specular = pow(specular, 32) * _gloss;
                // 纹理色 * 光源色 * 强度参数
                c = c * _LightColor0 * (diffuse + specular);
                return c * 2;
            }
            ENDCG
        }
    } 
    FallBack "Diffuse"
}

BinnPhong光照模型,根据光照向量和视线的半角向量,与顶点法线的夹角来计算镜面高光的强度,另外再加上漫射光的成分:

Shader "James/VP Shader/LightModel-BinnPhong" 
{
    Properties 
    {
        _MainTex ("MainTex", 2D) = "white" {}
        _gloss ("Gloss", Float) = 5
    }
    SubShader 
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            float _LightColor0;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _gloss;
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : TEXCOORD1;        
                float3 lightDir : TEXCOORD2;        // 光照方向
            };
            
            v2f vert(appdata_full v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normal = v.normal;
                // 顶点->light的局部向量
                o.lightDir = ObjSpaceLightDir(v.vertex);
                // 顶点->camera的局部向量
                float3 viewDir = ObjSpaceViewDir(v.vertex);
                return o;
            }
            
            float4 frag(v2f i) : COLOR
            {
                i.lightDir = normalize(i.lightDir);
                i.normal = normalize(i.normal);
                // 半角向量,光照方向和视线的中间值
                float3 halfDir = (i.lightDir + i.normal) * 0.5;
                // 纹理采样
                float4 c = tex2D(_MainTex, i.uv);
                // 漫射光强度
                float diffuse = max(0, dot(i.normal, i.lightDir));
                // 镜面光强度
                float specular = max(0, dot(i.normal, halfDir));
                specular = pow(specular, 32) * _gloss;
                // 纹理色 * 光源色 * 强度参数
                c = c * _LightColor0 * (diffuse + specular);
                return c * 2;
            }
            ENDCG
        }
    } 
    FallBack "Diffuse"
}

BinnPhong比Phong少计算一次反射向量,会更简洁和高效。