ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unity光照模型全解析:从Lambert/Phong到PBR,解决场景发黑过曝

Unity光照模型全解析:从Lambert/Phong到PBR,解决场景发黑过曝 做Unity的人迟早会遇到这么一件事照着老教程搭了个场景灯光一开材质看起来还行但换个渲染管线或者把项目升级到URP/HDRP同一个场景瞬间像换了个游戏。暴露出来的光照模型差异不是参数问题而是底层计算逻辑就不是一路人。Unity光照里常说的经验模型和物理模型正是这种差异的根源。这篇文章想把这套东西讲透。我会从光照模型到底在算什么问题讲起把Lambert、Phong、Blinn-Phong这些经验模型的思路拆开再对比基于能量守恒和微表面理论的PBR物理模型最后落到实际项目里最常踩的坑——切物理模型后场景发黑、过曝、材质发紫给你一条能复现的排查链路。适合正在做Unity渲染、刚接触URP/HDRP的开发者和TA也适合被光照模型这四个字困扰过一阵子的同学。1. 光照模型到底在算什么从看见颜色到BRDF1.1 每一次渲染都是一道积分题要理解光照模型先要把渲染这件事还原成它本来的样子一束光从光源出发打到一个物体表面一部分被反射、一部分被吸收、一部分穿进去再散射出来最终某一部分进入相机镜头。相机看到的就是这一束光的能量和方向。真实情况下任何一个表面点收到的光来自四面八方——直接光源、天光、环境反射、其他物体弹过来的间接光。这些光以不同角度打到面上又以不同方向弹出去。渲染的目标就是算出给定一个表面点、一个观察方向我到底该看到多亮的颜色。严格物理意义上这是一道积分题对表面上半球空间的所有入射方向把BRDF双向反射分布函数和入射辐照度乘起来再做积分。BRDF描述的是从某个方向来的光往某个方向反射的比例——它是所有光照模型的核心名片。但游戏引擎显然不会给每个像素做数值积分。所以从实时渲染诞生那天起人们就在物理正确和算得过来之间做取舍。光照模型本质上就是这道积分题的近似解或者说是看起来合理的闭式公式。经验模型和物理模型是这棵演化树上的两个大分支。1.2 经验模型和物理模型的分水岭经验模型英文叫Empirical Models。它们的共同特点是公式不是从物理定律推导出来的而是根据观察这样画出来像那么回事而拼凑出来的。典型代表是Lambert、Phong、Blinn-Phong。它们计算简单参数直观渲染结果在特定光照条件下足够骗过观众但没有能量守恒保证也不对材质物理属性负责。物理模型英文叫Physically Based Models实时渲染里特指PBR——基于物理的渲染。它的内核是Cook-Torrance微表面BRDF加上能量守恒约束再辅以线性空间、物理光强单位、HDR曝光这些配套措施。物理模型的产出不是某个好看的高光形状而是一整套材质在不同光照环境下都保持一致反应的机制。用一个词来概括物理模型的野心一致性。这里的一致性很关键。同一块粗糙金属放在夕阳下、室内灯光下、蓝天下物理模型的渲染结果会随环境合理变化而经验模型的高光可能一直在同一个位置、同一个强度跳动因为它压根没考虑入射能量和观察角度的真实关系。1.3 在Unity里你能碰到的三个渲染层级Unity光照模型的载体按渲染管线分三个层级理解它们你就知道自己在哪儿调什么。第一个是内置渲染管线Built-in RP下的旧Shader。Surface Shader里指定Lambert、BlinnPhong、Standard三种光照函数分别对应纯经验漫反射、经验高光模型、Tornatore简化的PBR模型。Windows下新建项目还能看到老式的Diffuse、Specular、Bumped Specular这些预置Shader全是经验模型时代的遗产。第二个是URP通用渲染管线下的Lit、Simple Lit、Complex Lit。Simple Lit本质上是经验模型思路的简化PBRLit才是完整物理模型Complex Lit增加布料和材质细节层仍是物理模型家族。URP里的光照模型选择直接影响移动端性能这也是很多项目在URP下纠结的原因。第三个是HDRP高清渲染管线下的Lit、StackLit、AxF/SSS这套。HDRP是物理模型最彻底的实现光强单位是lux、lumen、candela默认线性空间带自动曝光和物理天空。HDRP里没有一个参数叫光照强度5因为它压根不打算跟你玩经验模型那套单位。在同一个项目里灯光用的是某一个光照模型材质用的是另一个阴影系统又各管各的——这种混乱正是很多光照问题的来源。所以下一步我先把经验模型和物理模型各自的内部逻辑摊开看。2. 从Lambert到Blinn-Phong经验模型为什么够看但不够用2.1 漫反射的起点光一顿乱撞Lambert模型是漫反射的元祖公式简单到令人发指float3 diffuse albedo * lightColor * max(0, dot(N, L));一句话理解表面的亮度和光线入射角度的余弦成正比。光垂直照下来最亮掠射角越大越暗。这个模型假设表面是理想漫反射体——光打到面上后被均匀地散射到所有方向所以它完全不依赖观察方向。你在右边看和左边看漫反射亮度一模一样。Lambert模型能活到今天不只是因为它便宜而是因为很多真实材质墙面、布料、哑光塑料、纸张在宏观上确实接近理想漫反射。它的短板有两个一是没有高光二是完全非线性空间的物理性缺失。一个Lambert球在强光下会过曝在弱光下死黑剪辑感很强。在Unity Surface Shader里旧版本的默认漫反射就是Lambert。它也是URP Simple Lit漫反射部分的基底。明白了这点你就知道为什么老项目接URP时凡是用默认Lambert的材质观感变化往往不大——因为漫反射这部分经验模型和物理模型用的是同一个起点。2.2 Phong高光一段余弦函数撑起来的塑料质感Lambert说完没有高光不行这个问题立刻浮现。1975年Phong把镜面反射抽象成了一个幂函数float3 specular lightColor * pow(max(0, dot(R, V)), shininess);解释一下符号R是入射光的反射方向V是观察方向shininess是控制高光锐度的参数。这个公式的直觉是反射光和视线越接近高光越亮shininess越大高光衰减越快视觉上越锐利。Phong模型在当年是革命性的但它有两个经典毛病。第一它在反射方向完全对上视线时达到极值这个极值点很容易让高光变成一块发白的塑料斑物理上没有任何材质会这样。第二shininess调大以后高光区域的能量凭空变多既不守恒也不收敛美术调参时经常出现高光太炸的问题。Unity内置Shader里Specular、Bumped Specular这些用的就是Phong思路的变体。2.3 Blinn-Phong的内存换时间Phong模型要算反射方向R需要额外的归一化操作和向量减法而且R的计算对精度不稳定。Jim Blinn在1977年做了个聪明替换不比较R和V的夹角而是取L和V的中间向量H比较N和H的夹角float3 H normalize(L V); float3 specular lightColor * pow(max(0, dot(N, H)), shininess);只需要一个归一化操作计算量全面下降同时高光形状也比Phong更柔和、更不容易出现硬边。Blinn-Phong在Unity里是很传奇的存在——Surface Shader的BlinnPhong光照函数的默认高光参数多年来一直是移动端美术的默认选择。但你要是把Blinn-Phong高光放大到极限观察会发现它和PBR高光的差别在于能量不会随粗糙度变化自动重新分配。粗糙度高的材质在PBR里高光会变宽变暗而Blinn-Phong的shininess参数需要你手动调暗、调宽——它不知道高光变宽的同时总能量该减少这个物理约束。2.4 表面着色器里老三样怎么选如果你还在用内置渲染管线Surface Shader里这三个光照函数的取舍我直接给结论Lambert低模、纯风格化、完全没有高光的表面比如卡渲底色。性能最省但别指望细节。BlinnPhong移动端卡通、科技UI、经验风格材质。高光可控性能亲民。需要注意高光能量过头的问题实际项目中我通常会用_SpecIntensity额外乘一个衰减系数来压低高光。StandardPBR桌面端写实、数字孪生、需要全局光照支持的场景。它已经不是经验模型了你选择了它就得承担线性空间、光强校准这些配套成本。选型卡了壳的时候有个记忆锚点经验模型适合我知道这个材质长什么样的场景物理模型适合我希望这个材质在不同场景里都保持同一套反应规则的场景。二八定律在光照模型这里也是成立的——八成的项目其实只需要Lambert加Blinn-Phong就能撑住美术风格但另外两成需要PBR的项目硬用经验模型就是自己找罪受。3. 物理模型的核心不是漂亮而是能量守恒3.1 微表面理论粗糙不是参数是几何形态物理模型和微表面理论是绑在一起的。它把任何表面都想象成无数个微小平面microfacet的集合每个微平面都是一个完美的镜面反射器。宏观上看到的漫反射其实是光线在这些微平面之间多次反弹后随机散射的结果宏观上看到的高光则是微平面朝向恰好和L、V的对称方向重合时的一对一反射。所以粗糙度roughness在物理模型里不是一个软的视觉效果参数而是描述微平面法线方向分布的统计学参数。粗糙度越高微平面法线分布越乱高光就越散越暗粗糙度越低微平面越整齐高光越集中越亮。这个内在逻辑让PBR材质天然具备高光随粗糙度变化的行为——这正是经验模型需要人工弥补的。Unity的Standard Shader用的是反向参数smoothness光滑度。Smoothness 1减roughness美术习惯上越亮越光滑物理上越亮越反光本质指向一致但换算时经常出混淆。HDRP里直接叫roughness如果你从内置管线迁移到HDRP所有材质的smoothness参数都得做一次反转这是第一步也是很多人漏掉的。3.2 Cook-Torrance三件套的职责划分现代实时PBR的镜面反射模型基本都源自Cook-Torrance BRDFspecular D * F * G / (4 * NdotV * NdotL)三个因子各有分工D法线分布函数Normal Distribution Function描述微平面法线的统计分布。Unity Standard Shader用GGX/Trowbridge-Reitz它的长尾分布比Blinn-Phong的余弦幂更符合真实材质的掠射行为。F菲涅尔项Fresnel描述反射率随入射角变化的规律。所有非金属在掠射角都会反射率飙升这是为什么你在低角度观察一切物体时都会看到一层泛白反光——物理模型天然带出这个现象经验模型完全不管。G几何遮蔽项Geometry/Shadowing-Masking描述微平面互相遮挡导致的能量衰减。粗糙表面在高角度观察时微平面自遮挡严重G项能避免高光在掠射角过曝。这三项保证了能量守恒的大前提出射能量永远不可能大于入射能量。这也是物理模型和经验模型之间最本质的安身立命区别。3.3 Metallic工作流和Specular工作流到底选哪个Unity传统PBR给了两条工作流很多新手一上来就晕我帮你理清。Metallic工作流用Metallic和Smoothness两个参数。核心思想是材质要么是金属metalness1要么是非金属metalness0大部分真实材质都在两个极值附近。金属的漫反射被直接砍掉反射光带有albedo颜色非金属的漫反射保留反射光永远是白色。这套工作流参数少、规则硬是主流。Specular工作流用Specular颜色和Smoothness它能表达介于金属和非金属之间的复杂材质比如被氧化、涂漆、混合尘埃的表面视觉精度更高但参数更多校准也更难。内置渲染管线的Standard (Specular setup)和HDRP的Lit里都有切换开关。我的建议没有特殊需求就选Metallic工作流。原因很简单——一套参数走天下美术同学容易理解金属就拉满metallic这个直觉非常好用。只有做车辆涂装、旧化表面这种精细材质时Specular工作流的自由度才值得那点额外成本。3.4 为什么线性空间是PBR的地基这条值得单独拿出来讲因为它引发的坑最多。非线性色彩空间Gamma里中间调被压亮高光被压暗PBR的物理计算在数学上就站不住脚。举个例子能量守恒要求漫反射加镜面反射的总能量等于入射能量但Gamma空间里的乘法根本不是线性乘法结果就是高光处能量虚增漫反射处能量虚减材质看起来脏、糊、假。所以Unity官方从2018开始默认新项目使用线性色彩空间。URP、HDRP强制推荐线性空间HDRP直接只支持线性。你把老项目切成线性空间时所有贴图颜色都会变亮一档这个变亮不是bug是贴图在Gamma空间采样后肤色和材质色的真实面貌首次被看到。正确的做法是把颜色贴图在导入设置里设为sRGB把光滑度/法线/高度这些数据贴图设为Non-ColorUnity会在采样时自动处理解码。在这里顺带提醒一个高频问题某些自定义Shader或者老Shader用了gammaToLinearSpace和linearToGammaSpace手搓转换如果写了双份转换画面会出现莫名其妙的灰阶偏移。排查的时候先看材质Shader是内置还是自研自研的先翻转换代码。4. 同一盏灯同一个球经验模型和物理模型的差距肉眼可见4.1 观察角度经验模型有它的死角拿一个灰色球体做实验一盏方向光白色光照左边用Blinn-Phong右边用Standard PBR两个材质都把高光调成看起来差不多。让相机从正面慢慢移动到掠射角这时候差异会非常明显。Blinn-Phong的高光在正对方向很亮到掠射角位置时高光整体衰减甚至消失因为它的余弦幂函数在接近90度时趋近于零。而PBR的菲涅尔项保证了掠射角反射率必然上升所以同一位置的PBR球从低角度观察反而会出现一条边缘泛白的高光带真实感立刻拉高。用一句话总结经验模型正面看还行物理模型什么角度都站得住。这解释了很多数字孪生项目里的一个现象——用老Shader做建筑外观从正面看没什么转到侧面看玻璃幕墙和金属框架就发假。因为真实世界玻璃和金属在掠射角都有明显的镜面反射而经验模型直接把这个物理特征省略了。4.2 金属感物理模型真正能一次调对的地方调金属材质最能看出两者区别。Blinn-Phong里你想做一个金色金属得同时调高光颜色、高光强度、光泽度并且这三种参数叠加出来的效果还跟环境光强有关换个场景就不对。PBR里做法简单得多albedo填金色metallic拉到1smoothness按抛光程度调0.8左右完事。不论换到什么场景、什么光源密度金色金属始终是金色金属——因为它的镜面反射颜色直接由albedo决定金属的反射率特性被写进了模型。这里给一个实用参考表方便验收材质调得对不对材质类型metallicsmoothnessalbedo特征常见错误哑光塑料00.2~0.4任意纯色metallic拉到1导致发黑磨砂金属10.3~0.5明度较低的深色用了纯白色albedo金属感消失抛光金属10.85~1深色smoothness过低高光扩散成塑料木地板00.6~0.8清漆面带纹理颜色忘了加清漆层过渡皮肤00.3~0.6偏红且带贴图开了metallic导致皮肤金属化4.3 性能开销不是你想的那样悬殊很多人听到物理模型第一反应是很贵。实际上在移动端跑Unity的Standard/Lit Shader和Blinn-Phong的差距没有想象中那么大。PBR的额外开销主要集中在GGX的D项计算、菲涅尔近似、几何遮蔽G项、以及线性空间下的多几次乘法。这些是纯ALU计算对于现代GPU来说压力比一张多重采样的阴影贴图小得多。真正的性能杀手永远在光影特效而不是BRDF本身。一个300万面的角色用PBR材质和一个用Blinn-Phong材质在GPU上的差异一般在10%到20%左右视机器而定完全在可接受范围内。如果追求极致开销低URP的Simple Lit就是给移动端准备的折中方案它用Blinn-Phong高光公式加上少量PBR风格的参数组合保留了调metallic也能出效果的直觉又不带完整GGX计算。所以性能取舍不该扣在BRDF身上而应该扣在阴影分辨率、实时反射、体积光这些重头戏上。4.4 根据美术风格选模型的参考标准选经验模型还是物理模型最终由美术风格决定我见过很多硬套PBR把风格化项目搞得四不像的案例。给你一个判断框架风格化写实、数字孪生、建筑可视化、汽车场景直接上PBR理直气壮。你这类型的核心受众就是用视觉真实感做判断的。赛璐璐二次元、卡通渲染、低模复古经验模型占优势。二次元Shader普遍基于Ramp阈值、描边和Blinn-Phong高光控制物理正确反而是负担。URP里的Simple Lit或自研经验模型NRShaderNPR更合适。热词里提的unity二次元shader基本都走这条路。轻度风格化堡垒之夜那种写实卡渲混合用PBR参数做底再在光照阶段叠一层Ramp或HSV偏移。材质本身用物理模型光照响应是风格化高级化处理。这套选择一定要项目早期定下来因为到了中期再翻转光照模型等于把材质全盘重做一遍成本极高。我见过一个机械风格项目美术做了三个月写实PBR后期说要转卡通结果所有贴图开始返工最后整个渲染管线都换掉了。越早定调越省钱。5. 切到物理模型后场景发黑/过曝一次完整的排查链路5.1 根因一光强单位根本不是同一个量级这类问题的头号原因几乎永远是光强单位。热词里ue4构建光照后发黑本质上也是同一个根源——引擎在物理光照单位下默认方向光强度不是一个能被直觉理解的小数。在旧的内置渲染管线里Directional Light的Intensity写1就代表足够亮在HDRP里Directional Light的强度单位是lux勒克斯。真实的太阳直射光大约是100000 lux到120000 lux你保持Intensity等于1的阳光在HDRP里相当于暴风雨深夜的月光——几乎全黑。所以从内置管线或者URP迁移到HDRP第一件事就是重设光强。参考值如下光源类型HDRP推荐强度单位备注太阳100000~120000勒克斯lux方向光阴天室外10000~20000lux方向光室内主灯300~500lux点光源/聚光灯电子屏幕光100~300lux点光源加Rect蜡烛12~15坎德拉cd点光源近距离衰减URP不发生这个问题因为URP沿用了旧单位的习惯写法Intensity 1 就是1。如果你只是从内置管线升到URP画面发黑的原因大概率不在光强而在后面两个根因。5.2 根因二曝光与自动曝光把物理光强拉回了观感区间HDRP默认带物理相机和自动曝光Auto Exposure它用测光算法根据画面亮度自动调整曝光类似手机相机的HDR。如果你在HDRP里设了100000 lux的太阳场景理应过曝成一片白但自动曝光会把它拉回人眼舒适区——问题来了自动曝光在切换镜头、进出室内、昼夜循环时会导致画面忽明忽暗也就是热词里说的光照自适应现象再配合ue4构建光照后发黑的既视感很多人会在这时候怀疑是烘焙坏了。处理思路分两步先确认真实问题不是曝光而是光强单位再看自动曝光的范围设得太广。推荐做法是定死一个默认曝光参数比如不用自动曝光或者把AdaptationSpeed调慢把Min/Max Scene亮度范围缩窄先让美术在固定曝光下校准材质和光强。等主体统一了再考虑开自动曝光做昼夜切换。5.3 根因三老材质看着正常其实是假的还有一种情况光照没问题你切管线后材质发粉、发紫、过曝那是贴图和Shader出了问题。最常见的有三个一是贴图色彩空间不对。老项目在Gamma空间下创建的贴图直接拖到线性空间颜色会整体变亮尤其皮肤、灰墙、草地的贴图会浮起来。解决方法是检查导入设置的sRGB勾选必要时在材质里加一个Gamma转线性的手动节点兜底。二是法线贴图方向反了。不同引擎和Shader对法线贴图Y轴方向约定不同切到URP或HDRP后法线反转材质会出现奇怪的凹陷感或者高光飞边。在材质面板里把Normal的Scale从1改到-1试一下立刻出结果。三是老Shader在新管线里破功。Surface Shader在URP里不再自动编译默认效果会退化成粉紫色Unity的pink error shader也就是热词里那个unity材质变成紫红色的问题。遇到粉紫色材质第一反应不是找场景光照而是看Console里有没有Shader相关的编译错误提示。URP下老Shader需要升级到URP Shader比如用URP自带的Lit重新做材质参数而不是硬扛旧Shader。5.4 排查顺序推荐光照问题一定要按顺序排查别一上来就动材质参数。给你整理一个可复现的排查清单先确认渲染管线和色彩空间URP/HDRP线性还是GammaHDRP非法线方向一律排查。再确认所有光的单位HDRP下Directional Light是否用了物理lux100000量级Point Light是否用了坎德拉。单位不对调别的全是白调。确认曝光模式自动曝光是否在捣乱把固定曝光试一下。确认Shader编译粉紫色材质直接查Console确认每个材质用的Shader在当前管线里有没有对应升级版。最后才是贴图sRGB勾选、法线翻转、Metallic和Smoothness数值校准。我把这条顺序贴在项目组文档里美术反馈光照问题的效率直接翻倍。因为以前他们总是先乱调Smoothness调半天没用最后发现只是Directional Light强度少了几个零。6. 项目落地笔记校准、色彩空间、偷懒技巧与光照自适应6.1 从第一天就定下调子的几个开关光照模型的选择不是一个按钮而是一连串需要从项目第一天就确定下来的开关。我按优先级排一下渲染管线内置 / URP / HDRP。这个不定了后面全没法聊。色彩空间线性必须尽早开。项目做到一半再切线性所有贴图、Shader、后处理全部炸一遍。新项目我建议直接线性起步哪怕你最后做卡通渲染线性空间下做NPR也更干净。光照模型基座用PBR还是经验模型。上面讲过的风格决定。光影方案实时还是烘焙。烘焙配合Light Probe、Reflection Probe在移动端比一堆实时点灯省钱得多PBR在烘焙环境下的表现尤其稳定。这四项如果有一个是项目中期再看后面烧的钱一定是前期规划的十倍。6.2 光照强度的常用参考表不用背参数收藏一份就行。我的习惯是项目里放一个TestBox区域里头摆不同颜色、不同粗糙度的标准球配合参考光强快速校准。没有参考全靠肉眼看HDRP的lux效率实在太低了。场景方向光强度环境光/天空亮度备注正午室外100000 lux20000 lux高对比、锐利阴影黄昏10000~30000 lux5000 lux色温偏暖阴影拉长室内日光5000~10000 lux1000 lux注意窗口方向夜晚城市100~1000 lux50 lux强调人工光源洞穴全黑00必须靠点光源或UE Lightmass搬运光照URP和内置管线不适用这套lux表它们继续用Intensity的默认单位。记住这个前提别跨管线硬套参数。6.3 老项目迁移到PBR的最快路径很多团队不是新项目起步而是要升级老项目。给你的迁移顺序建议第一先做一次全场景盘点哪些材质用的是Standard Shader哪些是BlinnPhong或Lambert。把Standard的直接把Metallic/Smoothness补偿到合理范围BlinnPhong转Standard必出高光偏白的问题需要把Smoothness降低或重新贴图。第二把所有纯色塑料材质如UI面板、无关紧要的小道具保留为Simple Lit或Lambert没必要为次要资产承担PBR成本。Focal Point原则在光照这里也适用主角身上的PBR投入产出比最高。第三重新烘焙光照。旧项目的Lightmap在物理模型下会出现明显的接缝和暗区因为烘焙精度和滤波条件都基于旧模型假设。重烘焙以后画质提升往往是飞跃式的。第四材质参数尽量从参考值起步别从旧值换算。比如一个旧材质Smoothness是0.1你直觉觉得换成PBR应该是0.6这种换算思路陷阱很多。贴一张参考图重新从参考表的推荐值开始通常更快。6.4 关于光照自适应和动态曝光热词里有光照自适应这里点一下HDRP的自动曝光本质上就是一种光照自适应机制它让眼睛优先看到场景的相对关系而不是绝对亮度。好处是不同时段、不同场景都能保持可读性坏处是它会把你的艺术表达洗平——你想让一个洞口特别暗、特别有压迫感自动曝光会强行把它拉亮。所以我对动态曝光的建议是把自动曝光当成效果器而不是默认状态。工作时用固定曝光校准交付演示时再开自动曝光。如果项目里有昼夜循环把自动曝光的Min/Max范围限制在±1档以内避免大跳变。另有一个更可控的做法用Volume调Exposure关键帧完全手动控制自由度最高。在URP里没有HDRP这套物理光源它也能通过后处理Volume的Tone Mapping配合自动曝光脚本实现类似的效果。Post-processing的Auto Exposure可以绑定画面亮度让白天夜晚过渡更平滑。这块最常出现的问题是过度使用——背景板的东西永远在闪玩家容易晕。控制Adaptation Speed和时间标度就行。最后说说我个人实际操作的体会。光照模型这个事最怕的不是能力不够而是项目里每个人各调各的最后场景光照像拼盘。我现在的标准流程是进项目第一天写一份一页半的光照规范文档里面定死管线、色彩空间、光照模型基座、光强单位、材质验收参考表然后所有人照这个表干活。规范落地以后光照问题从每个人每天都在调变成每周只调一次景。物理模型也好、经验模型也好真正的分水岭不是哪个更先进而是你有没有一套让团队所有人都能对齐的执行标准。这篇文章里的校准表和排查链路就是一个可以照着抄的起点。
返回列表