【光照】Unity中的[经验模型]

【光照】Unity中的[经验模型]

一、引言:什么是经验光照模型?在计算机图形学中,光照模型是模拟光线与物体交互的数学方法。物理精确的光照模型(如基于物理渲染PBR)虽然真实,但计算复杂、调参困难。相比之下,经验模型(Empirical Models)基于观察和简化假设,用较少的计算量获得“看起来不错”的效果。Unity作为游戏引擎,大量使用了经验光照模型来平衡性能与视觉质量。本文将深入剖析Unity中常用的经验光照模型,包括Lambert漫反射、Blinn-Phong高光反射,并通过可运行的代码示例展示其实现原理。## 二、核心经验模型原理### 2.1 Lambert漫反射模型Lambert模型是最基础的经验漫反射模型,它假设表面理想漫反射(即向所有方向均匀反射光)。其公式为:C_diffuse = C_light * C_material * max(0, dot(N, L))其中:-C_diffuse:最终漫反射颜色-C_light:光源颜色-C_material:材质颜色-N:表面法线-L:指向光源的方向向量(归一化)当dot(N, L) <= 0时,表面背光,结果为黑色。### 2.2 Blinn-Phong高光反射模型Blinn-Phong是对传统Phong模型的改进,通过计算半角向量H来简化运算:C_specular = C_light * C_spec * pow(max(0, dot(N, H)), gloss)其中:-H = normalize(L + V),V是视线方向-gloss:光泽度(高光指数),越大越尖锐该模型避免了Phong中反射向量R的计算,效率更高,且效果接近。## 三、Unity Shader实现:基础经验光照下面是一个完整的Unity Shader代码,实现Lambert漫反射 + Blinn-Phong高光反射的经验光照模型。glslShader "Custom/ExperienceLighting" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _Gloss ("Glossiness", Range(0, 1)) = 0.5 _SpecColor ("Specular Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // 使用 Lambert + BlinnPhong 光照函数 #pragma surface surf StandardBlinnPhong fullforwardshadows // 用户输入 sampler2D _MainTex; float _Gloss; fixed4 _SpecColor; struct Input { float2 uv_MainTex; }; // 自定义光照函数:Lambert漫反射 + BlinnPhong高光 inline fixed4 LightingStandardBlinnPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten) { // 漫反射部分:Lambert模型 half3 halfDir = normalize (lightDir + viewDir); fixed diff = max (0, dot (s.Normal, lightDir)); fixed4 c; c.rgb = s.Albedo * _LightColor0.rgb * diff * atten; // 高光部分:Blinn-Phong模型 float spec = pow (max (0, dot (s.Normal, halfDir)), _Gloss * 128); // gloss映射到0-128 c.rgb += _SpecColor.rgb * _LightColor0.rgb * spec * atten; c.a = s.Alpha; return c; } void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse"}代码注释说明:-LightingStandardBlinnPhong函数重写了Unity的标准光照,合并了漫反射和高光。-diff计算Lambert漫反射系数。-spec使用Blinn-Phong公式,_Gloss * 128将0-1范围映射到合理的光泽度范围。-atten是光源衰减因子,支持点光源、聚光灯等。## 四、进阶:多光源支持与顶点光照经验模型在多光源场景下需要额外处理。下面展示一个顶点光照版本,支持最多4个光源:glslShader "Custom/VertexLightingExperience" { Properties { _MainTex ("Texture", 2D) = "white" {} _Gloss ("Gloss", Range(0,1)) = 0.5 } SubShader { Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float3 worldNormal : TEXCOORD1; float3 worldPos : TEXCOORD2; float3 vertexLighting : TEXCOORD3; // 存储顶点光照结果 }; sampler2D _MainTex; float _Gloss; v2f vert (appdata v) { v2f o; o.uv = v.uv; o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 顶点光照计算:遍历最多4个光源 float3 vertexLight = float3(0,0,0); #ifdef VERTEXLIGHT_ON for (int i = 0; i < 4; i++) { float3 lightDir = float3(unity_LightPosition[i].x, unity_LightPosition[i].y, unity_LightPosition[i].z) - o.worldPos; float distance = length(lightDir); lightDir = normalize(lightDir); float attenuation = 1.0 / (1.0 + distance * distance * unity_LightAtten[i].z); // 衰减 // Lambert漫反射 float diff = max(0, dot(o.worldNormal, lightDir)); vertexLight += unity_LightColor[i].rgb * diff * attenuation; } #endif o.vertexLighting = vertexLight; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 texColor = tex2D(_MainTex, i.uv); // 主光源(方向光)使用BlinnPhong高光 float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos); float3 halfDir = normalize(lightDir + viewDir); float diff = max(0, dot(i.worldNormal, lightDir)); float spec = pow(max(0, dot(i.worldNormal, halfDir)), _Gloss * 64); fixed3 color = texColor.rgb * (_LightColor0.rgb * diff + _LightColor0.rgb * spec); // 加上顶点光照(漫反射) color += texColor.rgb * i.vertexLighting; return fixed4(color, 1); } ENDCG } }}关键点解析:-VERTEXLIGHT_ON宏控制顶点光照的启用。-unity_LightPositionunity_LightColorunity_LightAtten是Unity内置数组,存储非方向光源信息。- 顶点光照只计算漫反射,避免顶点处高光闪烁(因为法线插值会失真)。- 主光源(通常是方向光)在片段着色器中计算高光,保证质量。## 五、经验模型的局限性尽管经验模型在性能上优势明显,但也有明显缺点:1.能量不守恒:漫反射+高光的总能量可能超过入射光,物理不真实。2.无法模拟金属质感:金属反射特性与绝缘体不同,经验模型无法区分。3.微表面效果缺失:无法表现粗糙度对反射的影响(如PBR的粗糙度贴图)。Unity从5.0开始引入PBR(Standard Shader),正是为了克服这些局限。但对于移动端、旧设备或艺术风格化的项目,经验模型依然是首选。## 六、总结本文从原理到实践,深入剖析了Unity中的经验光照模型:-Lambert漫反射:简单高效的漫反射近似,公式max(0, dot(N,L))。-Blinn-Phong高光:通过半角向量简化高光计算,公式pow(dot(N,H), gloss)。- 提供了两个可运行Shader示例,分别实现基础版本和多光源顶点光照版本。- 讨论了经验模型的优缺点,帮助读者在项目中选择合适的光照方案。经验模型是图形学入门的基石,理解它们能让你更深刻地掌握Unity光照系统,也为学习PBR打下坚实基础。在实际开发中,建议根据目标平台和视觉效果需求,灵活选择或组合使用。