引言
光照模型是 Shader 编程的核心部分,它决定了物体表面如何与光线交互,从而影响最终的视觉效果。在 Unity 中,常见的光照模型包括 Lambert 漫反射模型、Phong 高光反射模型 和 Blinn-Phong 模型。
本文将详细介绍这些光照模型的原理,并展示如何在 Unity Shader 中实现它们。
1. Lambert 光照模型
1.1 基本原理
Lambert 光照模型是最基础的漫反射模型,基于 Lambert 余弦定律,模拟光线在粗糙表面的均匀散射效果。其核心公式为:
$$
I = I_0 \cdot \cos(\theta)
$$
其中:
- $I$ 是反射光强度。
- $I_0$ 是入射光强度。
- $\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 200Pass{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_{\text{diffuse}} + I_{\text{specular}}
$$
其中:
- $I_{\text{diffuse}}$ 是漫反射强度(Lambert 模型)。
- $I_{\text{specular}}$ 是高光反射强度,计算公式为:
$$
I_{\text{specular}} = I_0 \cdot (\mathbf{R} \cdot \mathbf{V})^n
$$ - $\mathbf{R}$ 是反射方向。
- $\mathbf{V}$ 是视线方向。
- $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 200Pass{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_{\text{diffuse}} + I_{\text{specular}}
$$
其中:
- $I_{\text{specular}}$ 的计算公式为:
$$
I_{\text{specular}} = I_0 \cdot (\mathbf{N} \cdot \mathbf{H})^n
$$ - $\mathbf{H}$ 是半角向量,计算公式为:
$$
\mathbf{H} = \frac{\mathbf{L} + \mathbf{V}}{|\mathbf{L} + \mathbf{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 200Pass{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 模型。
光照模型是 Shader 编程的核心部分,它决定了物体表面如何与光线交互,从而影响最终的视觉效果。在 Unity 中,常见的光照模型包括 Lambert 漫反射模型、Phong 高光反射模型 和 Blinn-Phong 模型。
本文将详细介绍这些光照模型的原理,并展示如何在 Unity Shader 中实现它们。