尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
【光照】Unity[经验模型]和[物理模型]
【光照】Unity[经验模型]和[物理模型]在游戏开发中光照是营造沉浸感的关键。Unity 作为主流引擎提供了从简单到复杂的多种光照模型。本文将带你从基础概念出发逐步深入理解经验模型如 Lambert、Blinn-Phong和物理模型如 PBR并通过完整代码示例掌握它们的实现。## 一、光照基础概念为什么需要模型现实世界中光照与物体表面的交互极其复杂。为了在计算机中模拟这一过程我们使用光照模型——它是一组数学公式用于计算表面某点接收到的光能如何转化为人眼看到的颜色。-经验模型基于视觉直觉忽略物理正确性追求计算效率。-物理模型基于真实物理原理能量守恒、微表面理论追求真实感。Unity 的 Shader 是编写光照模型的主要工具。本文所有代码基于 Unity 内置渲染管线Built-in RP的 Surface Shader 和顶点/片元 Shader。—## 二、经验模型Lambert 与 Blinn-Phong### 2.1 Lambert 漫反射模型原理表面亮度与光线入射角余弦成正比即朗伯余弦定律。它假设表面粗糙光线向所有方向均匀反射。公式颜色 材质颜色 × 光源颜色 × max(0, dot(法线, 光源方向))完整 Shader 代码Unity Surface Shader 风格csharpShader Example/LambertDiffuse { Properties { _MainTex (Texture, 2D) white {} _Color (Color, Color) (1,1,1,1) } SubShader { Tags { RenderTypeOpaque } LOD 200 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; fixed4 _Color; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo c.rgb; // 漫反射颜色 o.Alpha c.a; } ENDCG } FallBack Diffuse}注释-#pragma surface surf Lambert告诉 Unity 使用内置 Lambert 光照函数。-SurfaceOutput结构体中的Albedo就是材质颜色。### 2.2 Blinn-Phong 高光模型原理在 Lambert 基础上增加高光使用半向量视线方向与光源方向的一半代替反射向量计算更高效。公式高光 光源颜色 × 高光强度 × pow(max(0, dot(法线, 半向量)), 光泽度)完整 Shader 代码顶点/片元 Shader 风格csharpShader Example/BlinnPhong { Properties { _MainTex (Texture, 2D) white {} _SpecColor (Specular Color, Color) (1,1,1,1) _Shininess (Shininess, Range(0.01, 10)) 1 } SubShader { Tags { RenderTypeOpaque } Pass { 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; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float4 _SpecColor; float _Shininess; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); o.uv TRANSFORM_TEX(v.uv, _MainTex); o.worldNormal UnityObjectToWorldNormal(v.normal); o.worldPos mul(unity_ObjectToWorld, v.vertex).xyz; return o; } fixed4 frag (v2f i) : SV_Target { // 采样纹理 fixed4 texColor tex2D(_MainTex, i.uv); // 法线归一化 float3 N normalize(i.worldNormal); // 光源方向假设只有一个主平行光 float3 L normalize(_WorldSpaceLightPos0.xyz); // 视线方向 float3 V normalize(_WorldSpaceCameraPos - i.worldPos); // 半向量 float3 H normalize(L V); // Lambert 漫反射 float NdotL max(0, dot(N, L)); float3 diffuse texColor.rgb * _LightColor0.rgb * NdotL; // Blinn-Phong 高光 float NdotH max(0, dot(N, H)); float spec pow(NdotH, _Shininess * 128); // 乘以128是为了匹配常见范围 float3 specular _SpecColor.rgb * _LightColor0.rgb * spec; // 环境光 float3 ambient UNITY_LIGHTMODEL_AMBIENT.rgb * texColor.rgb; // 最终颜色 return fixed4(ambient diffuse specular, 1); } ENDCG } } FallBack Diffuse}注释- 手动计算了法线、光源、视线和半向量。-_LightColor0是 Unity 内置的第一个平行光颜色。- 高光项乘以_Shininess * 128是为了让参数范围更直观。—## 三、物理模型PBR基于物理的渲染### 3.1 PBR 核心原理PBR 遵循两个物理原则1.能量守恒反射光 折射光次表面散射≤ 入射光。2.微表面理论表面由无数微小镜面组成粗糙度决定高光扩散程度。关键参数-Albedo基础颜色不含光照信息。-Metallic金属度1金属0非金属。-Smoothness光滑度1镜面0粗糙。-Normal Map法线贴图增加细节。### 3.2 Unity 标准着色器Standard ShaderUnity 内置了 PBR 实现只需在 Surface Shader 中指定#pragma surface surf StandardcsharpShader Example/PBR_Standard { Properties { _MainTex (Albedo (RGB), 2D) white {} _MetallicTex (Metallic (R), 2D) white {} _Smoothness (Smoothness, Range(0,1)) 0.5 _Metallic (Metallic, Range(0,1)) 0.0 } SubShader { Tags { RenderTypeOpaque } CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; sampler2D _MetallicTex; half _Smoothness; half _Metallic; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c tex2D (_MainTex, IN.uv_MainTex); o.Albedo c.rgb; // 从金属贴图读取金属度否则使用全局值 half metallic tex2D(_MetallicTex, IN.uv_MainTex).r * _Metallic; o.Metallic metallic; o.Smoothness _Smoothness; o.Alpha c.a; } ENDCG } FallBack Standard}注释-SurfaceOutputStandard是 PBR 专用的输出结构。-Metallic和Smoothness直接驱动能量守恒计算。- 设置fullforwardshadows支持阴影。### 3.3 手动实现简化 PBR高级用法为了深入理解我们实现一个简化版 Cook-Torrance BRDF包含漫反射Lambert和微表面高光GGX。csharpShader Example/SimplePBR { Properties { _MainTex (Albedo, 2D) white {} _Metallic (Metallic, Range(0,1)) 0 _Smoothness (Smoothness, Range(0,1)) 0.5 _NormalMap (Normal Map, 2D) bump {} } SubShader { Tags { RenderTypeOpaque } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc #include Lighting.cginc struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float3 worldPos : TEXCOORD1; float3 worldNormal : TEXCOORD2; float3 worldTangent : TEXCOORD3; float3 worldBinormal : TEXCOORD4; float4 vertex : SV_POSITION; }; sampler2D _MainTex; sampler2D _NormalMap; float _Metallic; float _Smoothness; // GGX 法线分布函数 float DistributionGGX(float3 N, float3 H, float roughness) { float a roughness * roughness; float a2 a * a; float NdotH max(dot(N, H), 0.0); float NdotH2 NdotH * NdotH; float denom (NdotH2 * (a2 - 1.0) 1.0); return a2 / (3.14159 * denom * denom); } v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); o.uv v.uv; o.worldPos mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal UnityObjectToWorldNormal(v.normal); o.worldTangent UnityObjectToWorldDir(v.tangent.xyz); o.worldBinormal cross(o.worldNormal, o.worldTangent) * v.tangent.w; return o; } fixed4 frag (v2f i) : SV_Target { // 法线贴图采样 float3 normalTS UnpackNormal(tex2D(_NormalMap, i.uv)); float3x3 TBN float3x3(i.worldTangent, i.worldBinormal, i.worldNormal); float3 N normalize(mul(normalTS, TBN)); float3 V normalize(_WorldSpaceCameraPos - i.worldPos); float3 L normalize(_WorldSpaceLightPos0.xyz); float3 H normalize(L V); float roughness 1.0 - _Smoothness; // 粗糙度 // 漫反射能量守恒金属无漫反射 float3 albedo tex2D(_MainTex, i.uv).rgb; float3 F0 lerp(0.04, albedo, _Metallic); // 菲涅尔基础反射率 float3 kS F0 (1.0 - F0) * pow(1.0 - max(dot(V, H), 0.0), 5.0); // Schlick菲涅尔 float3 kD (1.0 - kS) * (1.0 - _Metallic); // 漫反射比例 // 高光项 float D DistributionGGX(N, H, roughness); float NdotL max(dot(N, L), 0.0); float NdotV max(dot(N, V), 0.0); float3 specular (D * kS) / (4.0 * NdotL * NdotV 0.0001); float3 diffuse kD * albedo / 3.14159; float3 final (diffuse specular) * _LightColor0.rgb * NdotL; final UNITY_LIGHTMODEL_AMBIENT.rgb * albedo; return fixed4(final, 1); } ENDCG } } FallBack Standard}注释- 手动实现 TBN 矩阵将法线贴图转换到世界空间。-DistributionGGX函数模拟微表面法线分布。- 菲涅尔效应使用 Schlick 近似金属反射率取自albedo非金属固定 0.04。—## 四、经验模型 vs 物理模型如何选择| 维度 | 经验模型 | 物理模型 ||------|----------|----------||计算开销| 低适合移动端 | 高需 PS4/PC/高端手机 ||真实感| 卡通风格、非写实 | 照片级写实 ||参数直观性| 直接调颜色和强度 | 需理解金属度、粗糙度 ||环境一致性| 依赖美术调参 | 自动适配环境光照 |实践建议- 卡通游戏 → 使用 Blinn-Phong 或自定义经验模型。- 写实游戏 → 必须使用 PBRStandard Shader 或自研 BRDF。- 混合项目 → 低配设备用经验模型高配设备用 PBR。—## 总结从 Lambert 漫反射到 Blinn-Phong 高光再到 PBR 的 GGX 模型我们走过了一条从视觉近似到物理模拟的进化之路。经验模型以其高效和易控性适合风格化渲染而物理模型通过能量守恒和微表面理论带来了前所未有的真实感。在 Unity 中你可以通过 Surface Shader 快速使用标准 PBR也可以编写顶点/片元 Shader 实现自定义 BRDF。理解这两类模型的原理将让你在不同项目需求面前做出最优选择。
RELATED

相关推荐

从原型到产品化:企业级Agent平台年度架构演进与增长复盘

从原型到产品化:企业级Agent平台年度架构演进与增长复盘

从原型到产品化:企业级Agent平台年度架构演进与增长复盘 一、从单智能体Demo到多租户SaaS:当原型代码撞上生产墙 去年此时,整个项目还只是一段跑在Jupyter里的LangChain脚本。它能调用GPT-4完成单一任务,演示效果惊艳。但演示到生…

📅 2026/9/14 7:19:53
哎呀,我老大写Bug啦——记一次MessageQueue的优化

哎呀,我老大写Bug啦——记一次MessageQueue的优化

哎呀,我老大写Bug啦——记一次MessageQueue的优化 事故现场:消息队列为何“卡死”?在一个阳光明媚的周三下午,线上监控突然告警:核心服务的消息队列处理延迟从平均2ms飙升至30秒。我紧急排查,发现罪魁祸首竟…

📅 2026/9/14 7:20:30
5大实战场景深度解析:JD-GUI插件开发终极指南

5大实战场景深度解析:JD-GUI插件开发终极指南

5大实战场景深度解析:JD-GUI插件开发终极指南 【免费下载链接】jd-gui A standalone Java Decompiler GUI 项目地址: https://gitcode.com/gh_mirrors/jd/jd-gui JD-GUI是一款强大的Java反编译工具,能够将.class文件还原为可读的Java源代码&#…

📅 2026/10/7 22:30:38
MORE NEWS

更多资讯

📰

市南区电工证含金量高吗?证书过期了不知道怎么复审延期怎么办

市南区电工证含金量高吗?证书过期了不知道怎么复审延期怎么办 证书过期了不知道怎么复审延期,这种情况下很多人会慌,尤其是像你这样在一线干活的电工师傅, 市南区电工证 不仅是你从业的“通行证”,更是你 含金量…

📰

工地太忙根本没时间复习考试?应急管理电工证种类保姆级教程来了

工地太忙根本没时间复习考试?应急管理电工证种类保姆级教程来了 工地太忙根本没时间复习考试?别慌,咱们今天就来聊聊 应急管理电工证种类…

📰

哪个部门检查电工证证怎么转异地

你外地考的电工证能不能转回老家?保姆级教程全在这 之前考的证在外省能不能转过来?这是很多特种作业人员,尤其是像我这样的信阳浉河区转岗从业者最关心的问题。现在全国多地都在推进“放管服”改革,电工证等特种作业操作证的转接流程也更加规范和清晰。这…

📰

荔浦市考电工证在哪里 上岗必备怎么避坑

荔浦市考电工证在哪里 上岗必备怎么避坑 你是不是也遇到过这种情况, 不知道去哪报名怕被中介坑 ?特别是像电工证这种 上岗必备 的证书,关系到能否顺利入职、持证上岗,一旦出错,不仅浪费时间还影响工作安排。今天就通过一个真实案例,带你看清楚…

📰

阜平县电工证报名考试千万别踩坑,怕考不过白交培训费

阜平县电工证报名考试千万别踩坑,怕考不过白交培训费 你是不是也这样,花了几千块报了个电工证培训班,结果到考试的时候才发现,要么题目太难,要么流程太复杂,最后干脆放弃了? 怕考不过白交培训费 ,这事儿我见过太多了, 千万别踩坑…

📰

没有电工证配电间合闸实操考试考什么?网上查询合格标准太关键了

没有电工证配电间合闸实操考试考什么?网上查询合格标准太关键了 怕考不过白交培训费?别急,今天就从 没有电工证配电间合闸 的实操考试入手,帮你理清考试内容、合格标准和备考技巧,尤其针对 信阳浉河区公路工程从业者…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬