Unity Shader 光照模型详解:Lambert、Phong 和 Blinn-Phong(附完整代码)

从左到右依次为Lambert、Phong、Blinn-Phong模型在Unity引擎中的实现效果

引言

光照模型是 Shader 编程的核心部分,它决定了物体表面如何与光线交互,从而影响最终的视觉效果。在 Unity 中,常见的光照模型包括 Lambert 漫反射模型Phong 高光反射模型Blinn-Phong 模型
本文将详细介绍这些光照模型的原理,并展示如何在 Unity Shader 中实现它们。


1. Lambert 光照模型

1.1 基本原理

Lambert 光照模型是最基础的漫反射模型,基于 Lambert 余弦定律,模拟光线在粗糙表面的均匀散射效果。其核心公式为:
I = I 0 ⋅ cos ⁡ ( θ ) I = I_0 \cdot \cos(\theta) I=I0cos(θ)
其中:

  • I I I 是反射光强度。
  • I 0 I_0 I0 是入射光强度。
  • θ \theta θ 是光线入射方向与表面法线方向的夹角。

1.2 Unity Shader 实现

以下是 Lambert 光照模型的实现代码:

Shader "LightModel/LambertShader"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
    }
        SubShader
        {
            Tags { "RenderType" = "Opaque" }
            LOD 200

            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

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

                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float3 worldNormal : TEXCOORD1;
                    float4 vertex : SV_POSITION;
                };

                sampler2D _MainTex;
                float4 _MainTex_ST;
                float4 _Color;

                v2f vert(appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);  // 顶点变换
                    o.worldNormal = UnityObjectToWorldNormal(v.normal);  // 法线变换
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);  // 纹理坐标变换
                    return o;
                }

                fixed4 frag(v2f i) : SV_Target
                {
                    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);  // 光源方向
                    float3 normal = normalize(i.worldNormal);  // 法线方向
                    float diff = max(dot(normal, lightDir), 0);  // 漫反射强度
                    fixed4 col = tex2D(_MainTex, i.uv) * _Color * diff;  // 最终颜色
                    return col;
                }
                ENDCG
            }
        }
            FallBack "Diffuse"
}

Lambert模型实现效果如图:
在这里插入图片描述


2. Phong 光照模型

2.1 基本原理

Phong 光照模型在 Lambert 模型的基础上增加了 高光反射,模拟光线在光滑表面的镜面反射效果。其核心公式为:
I = I diffuse + I specular I = I_{\text{diffuse}} + I_{\text{specular}} I=Idiffuse+Ispecular
其中:

  • I diffuse I_{\text{diffuse}} Idiffuse 是漫反射强度(Lambert 模型)。
  • I specular I_{\text{specular}} Ispecular 是高光反射强度,计算公式为:
    I specular = I 0 ⋅ ( R ⋅ V ) n I_{\text{specular}} = I_0 \cdot (\mathbf{R} \cdot \mathbf{V})^n Ispecular=I0(RV)n
  • R \mathbf{R} R 是反射方向。
  • V \mathbf{V} V 是视线方向。
  • n n n 是高光指数,控制高光的集中程度。

2.2 Unity Shader 实现

以下是 Phong 光照模型的实现代码:

Shader "LightModel/PhongShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
        _SpecularColor("Specular Color", Color) = (1,1,1,1)  // 高光颜色
        _Gloss("Gloss", Range(1, 256)) = 32   // 高光指数(控制高光集中程度
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
                float3 worldPos : TEXCOORD2;    // 世界空间顶点位置
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            float4 _SpecularColor;
            float _Gloss;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);  // 法线变换
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;  // 顶点变换到世界空间
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);  // 光源方向
                float3 normal = normalize(i.worldNormal);              // 法线方向
                float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);  // 视线方向
                float3 reflectDir = reflect(-lightDir, normal);        // 反射方向

                float diff = max(dot(normal, lightDir), 0);            // 漫反射强度
                float spec = pow(max(dot(reflectDir, viewDir), 0), _Gloss);  // 高光强度

                fixed4 texColor = tex2D(_MainTex, i.uv); // 纹理颜色
                fixed4 col = texColor * _Color * diff + spec;  // 最终颜色
                return col;
            }
            ENDCG
        }
    }
}

Phong模型实现效果如图:
在这里插入图片描述


3. Blinn-Phong 光照模型

3.1 基本原理

Blinn-Phong 模型是 Phong 模型的改进版,通过引入 半角向量 简化了高光反射的计算。其核心公式为:
I = I diffuse + I specular I = I_{\text{diffuse}} + I_{\text{specular}} I=Idiffuse+Ispecular
其中:

  • I specular I_{\text{specular}} Ispecular 的计算公式为:
    I specular = I 0 ⋅ ( N ⋅ H ) n I_{\text{specular}} = I_0 \cdot (\mathbf{N} \cdot \mathbf{H})^n Ispecular=I0(NH)n
  • H \mathbf{H} H 是半角向量,计算公式为:
    H = L + V ∣ L + V ∣ \mathbf{H} = \frac{\mathbf{L} + \mathbf{V}}{|\mathbf{L} + \mathbf{V}|} H=L+VL+V

# 3.2 Unity Shader 实现

以下是 Blinn-Phong 光照模型的实现代码:

Shader "LightModel/BlinnPhongShader"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
        _SpecularColor("Specular Color", Color) = (1,1,1,1)  // 高光颜色
        _Gloss("Gloss", Range(1, 256)) = 32   // 高光指数(控制高光集中程度
    }
        SubShader
        {
            Tags { "RenderType" = "Opaque" }
            LOD 200

            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

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

                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float3 worldNormal : TEXCOORD1;
                    float3 worldPos : TEXCOORD2;    // 世界空间顶点位置
                    float4 vertex : SV_POSITION;
                };

                sampler2D _MainTex;
                float4 _MainTex_ST;
                float4 _Color;
                float4 _SpecularColor;
                float _Gloss;

                v2f vert(appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.worldNormal = UnityObjectToWorldNormal(v.normal);  // 法线变换
                    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;  // 顶点变换到世界空间
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    return o;
                }

                fixed4 frag(v2f i) : SV_Target
                {
                    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);  // 光源方向
                    float3 normal = normalize(i.worldNormal);              // 法线方向
                    float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);  // 视线方向
                    float3 halfDir = normalize(lightDir + viewDir);        // 半角向量

                    float diff = max(dot(normal, lightDir), 0);            // 漫反射强度
                    float spec = pow(max(dot(normal, halfDir), 0), _Gloss);  // 高光强度

                    fixed4 texColor = tex2D(_MainTex, i.uv); // 纹理颜色
                    fixed4 col = texColor * _Color * diff + spec;  // 最终颜色
                    return col;
                }
                ENDCG
            }
        }
}

Blinn-Phong模型实现效果如图:
在这里插入图片描述


4. 光照模型的对比与选择

4.1 对比

  • Lambert 模型:简单高效,适合粗糙表面,但缺乏高光反射。
  • Phong 模型:增加了高光反射,适合光滑表面,但计算量较大。
  • Blinn-Phong 模型:高光计算更高效,适合大多数场景。

4.2 选择建议

  • 如果需要快速实现基础光照效果,选择 Lambert 模型
  • 如果需要表现光滑表面的高光效果,选择 Phong 模型Blinn-Phong 模型
  • 在性能要求较高的场景中,优先选择 Blinn-Phong 模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值