ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

光照烘焙原理与参数配置:有效降低GPU负载和设备发热

光照烘焙原理与参数配置:有效降低GPU负载和设备发热 1. 光照烘焙到底在烘什么先搞懂原理再谈降温做移动端或者低端机优化的朋友对“发烫”这个词应该都不陌生。上篇我们聊过场景复杂度对温度的影响这次换个更直接、性价比也更高的思路——光照烘焙。在游戏开发里光照烘焙Light Baking指的是把静态场景中原本需要实时计算的光照信息提前算好并写入到纹理光照贴图Lightmap里等运行时直接采样。简单说就是把“现场算”变成了“提前查表”。作为系列第6篇我默认能看到这里的读者已经对发热问题有了基本认识发热的本质是长时间高频计算导致芯片功耗居高不下。而光照运算在渲染管线里是非常吃算力的重活尤其是场景里光源多、范围大、还开了实时阴影的项目GPU每一帧都要为这些光追来追去、算来算去温度想降都难。把这部分挪到离线完成等于给运行时减负一大截这是光照烘焙能成为“降温性价比之王”的根本原因。这个内容适合谁参考主要是做Unity和Unreal项目优化的开发者特别是正在做移动端适配、主机平台性能调优、以及云游戏压测的同行。如果你刚接触烘焙也能从这篇文章里拿到一整套可直接落地的参数和流程。但需要提前说清楚烘焙不是万能钥匙静态场景它效果好到惊人动态灯光多的场景则要谨慎使用否则会出现烘焙光照和实时光照“打架”的视觉问题。这部分我们后面专门聊。1.1 实时光照的算力黑洞设备为什么会发烫先看一个最容易被忽略的事实场景里哪怕只有一个平行光只要开启了实时阴影GPU在每帧渲染时都需要完成一次额外的深度图生成。如果场景中有6盏动态光源、每盏都投射阴影那就相当于每帧多绘制了6张深度图再依次做光照计算和阴影采样。做过性能分析的人会很清楚这种状态下GPU的负载曲线几乎是直线拉满功耗和发热量自然居高不下。打个比方实时光照就像你在餐厅每点一道菜后厨都要当场洗菜、切菜、炒菜哪怕同一个菜品点了100次后厨还得重复100次流程光照烘焙则像是把所有菜提前做成预制菜顾客下单时只需要微波炉加热一下速度、能耗完全不同。设备发烫的背后就是那颗GPU在扮演“倒霉后厨”的角色每帧都在重复劳动。所以优化的核心思路有两个方向要么减少重复计算的量要么把计算从“实时”挪到“离线”。光照烘焙正是后者最典型的代表。它适用于静态场景、固定光源、以及不会频繁变化的间接光照把这些光照效果提前“熨”进贴图里运行时只需一次纹理采样就能拿到几乎无差别、甚至更细腻的光影结果。1.2 烘焙的本质把“计算”变成“查表”光照烘焙的字面意思是“把光照信息烘烤到纹理上”。具体过程是美术和TA在编辑器里把所有场景模型整理好设置好光源参数然后由引擎离线计算每个表面点的光照信息——包括直接光照的亮度和颜色、阴影边界、间接光照的能量反射等。计算完成后这些值会写入到一张或多张称为Lightmap的纹理中并和模型UV一一对应。运行时引擎不再计算光照公式而是根据像素的UV坐标去Lightmap纹理里采样对应颜色再和漫反射材质做一次乘法运算就得到了最终的光照效果。你可以把它理解为“查表法”渲染——表中的数据在游戏运行前就已经准备好运行时需要的只是一个寻址操作和一次乘法运算这比实时计算光照要快上好几个数量级。这还不是全部。烘焙的好处不仅在于“快”还在于“省电、省熱”。GPU的功耗和负载直接相关负载降了单位时间内发出的热量就少了。实测下来在一个中等大小的移动端场景里把静态主光源、辅光、补光都从实时改为烘焙设备表面温度能下降3到6摄氏度帧率稳定度反而提升了。这就是为什么我做发烫优化时第一刀往往就是砍实时光照第二刀才是砍场景面数。1.3 哪些光照适合烘焙哪些必须保留实时新手最容易犯的错误是“全烘”把场景里所有光源都设为烘焙模式结果角色身上的光照效果就变得非常奇怪——角色走过一个固定路灯时身上没有影子变化或者明明站在火堆旁边火光却完全不影响角色。原因很简单动态物体不能被静态光照影响。所以在动手烘焙之前必须对场景光源进行分类。适合烘焙的光源包括场景中不移动也不改变颜色的主光源如太阳、固定位置的室内灯、自然反射的间接光照、以及美术已经定稿的氛围光。这些光源的共同特点是“稳定”——不随玩法变化也不受角色交互影响。必须保留实时的光源包括跟随角色的手电筒、动态火把、爆炸闪光、以及任何会影响角色或可移动物体的光源。真实项目中通常采用“混合光照”方案让烘焙光负责大面积、稳定的光照氛围实时光只做点睛之笔。这样既保证了画质又把每帧GPU负载控制到了最低发热自然被压住了。2. 烘焙参数怎么定直接照着抄的配置表明白了原理下一步就是上手操作。不管是Unity还是Unreal光照烘焙参数虽然名字不同底层逻辑大同小异。这里直接给一套我常用的、经过多个项目验证的默认配置你再根据自己项目实际画质要求做微调就行。先说结论烘焙的主要参数集中在“光照贴图分辨率”“直接光照强度”“间接光照反弹次数”“阴影设置”“降噪采样”这几个维度。你把以下配置作为起点通常能在“画质-烘焙时间-运行时性能”三者之间找到不错的平衡点光照贴图分辨率Lightmap Resolution移动端场景建议40~60 texels per unitPC端中等场景80~100大世界则要配合Lightmap Streaming做分块管理优先保证大头贴图质量。最大反弹次数Max Bounces室内场景4~6次室外场景2~3次就够了。反弹越多间接光越丰富但烘焙时间成倍增长而且噪声也会变多不是越大越好。直接光照强度Direct Light Intensity默认1.0但建议在烘焙前把所有光源的Intensity统一调好避免烘焙后再去改否则所有Lightmap要重新生成。阴影偏移Shadow Offset/Bias室内小模型多的话默认值经常会出现“漏光”或“阴影接缝”问题适当增加Bias可以缓解但Bias过大会导致阴影“浮空”这个值需要反复微调。2.1 光照贴图分辨率怎么选别贪高够用就行光照贴图分辨率是整个烘焙流程里最容易被炫技误伤的参数。很多朋友一上来就顶着4096甚至8192去烘烘出来的光影确实细腻但伴随而来的是巨大的贴图内存消耗和GPU采样开销。在发烫优化这个目标下这完全是本末倒置——你为了降温做的优化结果引入了新的发热源。选分辨率的原则是“按面的大小分配密度”。一面墙壁占屏幕比例很大值得给它较高的分辨率一个不起眼的小摆件给太高分辨率纯属浪费。光线放在小物体上最终呈现的细节肉眼根本分辨不出来。实际项目中我习惯使用Unity里的“UV Resizing Dilation”和Unreal里的“Lightmass UV”自动布局功能让引擎根据对象体积和重要性自动分配密度再手动微调几处关键大件。另一个容易忽略的指标是“Texels Per Unit”单位面积内的纹理像素也叫像素密度。举个例子同样是一张1米见方的桌子50 texels per unit意味着这张桌子上会分配约2500个烘焙像素如果两项百分比都拉满内存就会翻倍而视觉提升只有10%怎么看都不划算。移动端建议整场景控制在40~60 texels per unit渲染距离远的大平原类场景例外那类场景光照信息本身就简单分辨率低一点反而更合理。2.2 间接光照强度与反弹次数一个数字带来的画质提升直接光照容易理解太阳照着哪块哪块就亮但现实世界里“没有直射光”的区域并不是全黑的比如室内墙角、屋檐底下依然能有微弱的亮度感知这就是间接光照Indirect Light。烘焙时引擎会模拟光线在场景中的多次反弹每次反弹会把上一次面片反射出的颜色继续向下传递直到能量衰减到忽略为止。反弹次数越少间接光越“干瘪”反弹次数太多烘焙时间会像坐火箭一样飙升。实践下来室内密闭小场景用4次反弹就足够丰富开放大世界场景2~3次反而更自然——因为开阔环境下间接光本身衰减得很快反弹次数的边际收益很低。如果想要更好的间接光效果高性价比的做法反而是在场景中多补几个低强度、不发光的“反射探针”区域让引擎能捕捉到更多方向性信息。需要注意的是烘焙时“间接光照强度”这个数值大小直接乘在反弹结果上。数值设到1.2~1.5之间可以抵消部分烘焙造成的能量损失让暗部细节更清楚。但如果拉太高整个场景会发灰、发闷看起来像蒙了一层雾。我的建议是先默认1.0看第一版烘焙效果再决定要不要调不要一上来就激进提亮。2.3 阴影设置与采样质量细节堆出来的质感差距烘焙阴影看起来是个二值化的问题——阴影边界锐利还是柔和但实现上没那么简单。实时渲染时阴影柔和度取决于光源面积、遮挡物距离和半影效应烘焙时引擎会对遮挡边缘做大量的超采样再通过降噪得到最终的柔和阴影。这个采样数的下限直接决定了烘焙时间和边缘质量。在Unity里对应的是“Anti-aliasing Samples”和“Directional Light Samples”Unreal里则是“Shadow Sample Count”和“Filtering”。想要快速测试先用低采样数4~8烘一版看整体布局确认光源位置、强度都没问题后再把采样数提高到16~32做最终版。上来就全用64甚至128去烘一张光照贴图烘半小时起步完全不值得。另一个和阴影设置强相关的参数是“Shadow Distance”。做烘焙前要检查相机的阴影距离设置——如果场景里存在动态阴影引擎会在该距离内额外启用实时阴影。距离设得太远几十个物体同时投射实时阴影发热照样压不住设得太近远处的动态物体会出现“无影”的视觉割裂需要按场景尺寸精细调节。3. 从零到一一次完整的光照烘焙实操流程参数讲了一堆最后还是要落到手上。这一节直接按流程走把一个标准室内场景的烘焙过程过一遍。我会以Unity为例但Unreal的流程逻辑完全一致我会在关键步骤标注两者的差异。先交代一下测试环境Unity 2021.3 LTS内置渲染管线场景是一个50平米左右的室内办公空间模型面数约60万面静态光源4盏主平行光1盏、室内点/聚光灯3盏目标平台是骁龙8系移动设备。这是很典型的“发烫重点户”——模型不算大但实时光照和实时阴影让中端机能跑到45度以上。3.1 场景准备与UV处理烘焙前的第一道坎很多新手烘焙时漏光、黑块往往不是参数问题而是UV没处理好。烘焙和贴图采样一样依赖模型的第二UVLightmap UV。如果第二UV有重叠、拉伸或者超出[0,1]区间光照信息在写入时就会“打架”反映到画面上就是漏光、黑斑、边缘错位。好在引擎提供了自动展开功能。Unity里的操作是选中模型在Inspector的Model标签页勾选“Generate Lightmap UV”并设置“Margin”为4~8像素引擎会自动计算不重叠的第二UVUnreal则在静态网格体编辑器里点击“Generate Lightmap UVs”按钮。对于自动展开效果不理想的大曲面、复杂拓扑模型强烈建议在建模软件里重新展开第二UV这是人力和效果的权衡——你多花10分钟做UV能省掉后面两小时的修漏光功夫。场景里所有需要烘焙光照的物体还要确保它们的Static标记是勾选状态。Unity中物体要有“Contribute GI”和“Static”勾选Unreal中则要标记为“Static”或“Stationary”。这个步骤没有技术含量但漏了某一个组件整个烘焙结果就会出现“一块亮一块暗”的奇怪现象排查起来非常浪费时间。3.2 光源属性调整与烘焙参数配置把数值调对再动手准备阶段完成后进入光源和光参数配置环节。光源的设置原则是首先确定哪些光源要用烘焙模式哪些保留实时或混合。在Unity中光源Inspector里的“Mode”有三个选项Realtime、Mixed、Baked。静止不动的室内光源直接选Baked作为主光的太阳光可以选Mixed这样它能同时影响静态物和动态角色。同时把每个光源的“Intensity”“Range”“Color”都按美术最终效果调整好。这里我有一个独家习惯在烘焙前把所有光源的间接光强度先按0.8倍预估因为烘焙后的间接光往往会比实时预览时亮一点留出修正空间等第一版结果出来后再统一回调节点。然后打开烘焙面板。Unity里是Window → Rendering → Lighting在Scene页签下设置“Lighting Mode”为Baked Indirect或Substractive并填好上面章节提到的Rebounce、Lightmap Resolution等参数。Unreal里则是World Settings面板中的Lightmass设置项链路的参数是“Static Light Level Scale”“Indirect Lighting Quality”“Num Indirect Lighting Bounces”等。在最终烘焙前强烈建议先用低分辨率20 texels per unit和最低采样数烘一版预览确认光照布局、亮度、阴影方向都符合预期。这一步只要几分钟能帮你提前发现90%的方向性问题避免高参时白等半小时。3.3 烘焙执行与结果校验不是点一下“Bake”就完事了配置检查完毕点击“Generate Lighting”。等待时间取决于场景复杂度、反弹次数和采样数——测试场景大概需要5~10分钟。这期间引擎会做这样几件事先对场景进行光照贴图UV布局标定然后通过算法模拟直接光照和间接光照的反弹计算最后对结果做降噪和后处理。中间CPU会全力运行所以烘焙时不要同时跑其他重型任务否则进度会卡到离谱。烘焙完成后别急着打包测试。第一步要肉眼检查场景中是不是有漏光、阴影断裂、暗部过黑等问题第二步用引擎自带的“Overdraw”或“Lightmap Preview”视图查看光照贴图的分布是否均匀有无大面积浪费的空白区域第三步看Lightmap贴图的总内存占用是否在可控范围。这里插一句光照贴图的内存占用是发烫优化的隐藏杀手。一张1024x1024的RGBA16浮点贴图直接内存是8MB如果场景里有几十张这种贴图光贴图就吃掉了几百MB。一旦内存吃紧系统会频繁进入GC、内存换页甚至内存压缩反而引起新的卡顿和发热。所以打包前一定要检查Lightmap Atlas总大小如果超标优先降低贴图分辨率再配合纹理压缩格式解决。4. 烘焙后的运行时优化Lightmap也要“喂”给硬件吃烘焙本身只是降低实时光照的GPU负载但如果烘焙产物处理不当新的负载会从别处冒出来。光照贴图的运行时优化是整个流程中很多人忽略但又至关重要的一环。核心问题有三个方向贴图数量与格式、贴图加载方式、以及动态物和静态物在光照上的衔接。这三方面处理好了发烫优化才算真正闭环。4.1 贴图压缩与格式选择别让Lightmap拖了后腿烘焙默认产出的Lightmap是高清高位的浮点纹理因为光照信息需要很大的动态范围才有细腻的明暗过渡。但浮点纹理在移动端不仅解码开销大内存占用也高得吓人。所以正式发布时一定要做格式压缩和精度转换。以Unity为例项目打在移动端时把“Lightmap Encoding”设置为“Low Quality”对应RGBM或LDR并启用“Compress Lightmap”选项贴图格式会从RGBA Half Float降为DXT5或ASTC。这里“精度损失”没有你想象的那么严重——视觉上虽然暗部细节会少一点但换来的是内存减半到四分之一、GPU采样带宽大幅下降发热也随之降低。安卓平台如果支持ASTC 6x6或8x8建议优先用它在同等体积下画质保留比DXT要好。Unreal里的对应位置是“Lightmap Format”设置推荐选择“Force Virtual Texture”或者用“Texture Streaming”把大贴图切成小块按需加载。这里的原则都是一样的Lightmap是“静态数据”完全没必要一次性全部塞进内存卡着可见范围分块加载能显著降低移动端的内存峰值使用。4.2 动态物体与静态物体的取舍角色身上的光坑多得要命角色是动态物体不受Lightmap影响。如果你把所有光源都烘焙了站在阴影里的角色就会变成“发光体”——和周围环境明显不是一个曝光系统非常出戏。这也是很多项目“一烘焙就翻车”的核心原因。处理方式有两种。第一种是全自动方案使用引擎的Light Probe光照探针系统在场景中每隔一到两米放一个探针运行时引擎会自动采样周围的探针插值出角色所在位置的简要光照信息。这个方案优点是省心缺点是光照细节弱角色在颜色剧烈的场景过渡时会有“漂移感”。第二种是半自动方案把主光设为Mixed模式引擎会为混合光烘焙静态阴影贴图同时保留动态物体与静态物体之间接触点的实时阴影计算。这样静态地面、墙壁直接用烘焙好的阴影角色靠近墙壁时也能得到正确的接触阴影视觉和性能都能兼顾。这个方案在大型开放世界项目里用得多移动端如果主光比较少也不算贵亲测FPS掉得不多。4.3 混合光照模式的应用从土办法到正式解决方案Unity的Mixed光照模式提供了Baked Indirect、Subtractive和Shadowmask三种细分方式Unreal里也有对应的Stationary Light和静态光照概念。选错模式视觉和性能都会出问题。Baked Indirect直接光仍是实时计算只有反弹的间接光是烘焙的。这个方案最灵活但实时直接阴影仍然会带来部分发热。Subtractive直接光、间接光、阴影都烘焙到Lightmap角色身上只保留一个较粗糙的实时光照。移动端场景因为没有太多动态光影需求这套最省电、最不热但视觉动态度最弱。Shadowmask静态物体和动态物体各自有不同的光影层兼顾了画质和性能。如果你的游戏是3D第三人称RPG角色经常和场景交互这是最好用的模式。不少项目采用“ShadowmaskStationary主光实时点光源”的方案大面积氛围光靠烘焙靠近角色或重要交互区域用实时光点缀最后用光照探针在角色周围做过渡缓冲。这套方案在多个项目中验证过性能开销和画质平衡性都很好建议优先考虑。5. 常见问题与排查技巧实录踩坑记录全放送光照烘焙用久了谁都会遇到几个老熟人级别的坑。下面几条是我在多个项目里反复踩过的写成速查表方便大家对照排查。症状可能原因快速排查/解决墙角漏光、阴影出现“裂缝”Lightmap UV拉伸/重叠、Bias设置太小、模型法线不一致检查第二UV在UV编辑器里看是否拉伸增大阴影Bias平滑法线烘焙后整体偏亮/偏灰间接光反弹次数太少、环境光强度过高降低Environment Lighting强度或减少反弹次数适当调低间接光强度某个物体没有烘焙效果忘了勾选Static/Contribute GI或者物体带Lightmap UV缺失检查物体Static标记重新生成第二UV重新烘焙烘焙后帧率反而更低Lightmap分辨率过高、贴图格式未压缩、场景亮面采样过重降低分辨率或压缩格式限制动态阴影距离检查Shader里是否有多余采样室内场景烘焙时间超长反弹次数设置过高、场景面数过多、贴图分辨率过大先低参预览再高参反弹次数降到4次以内整体降低分辨率角色进入烘焙区域后发光/发暗光照探针没有布置或数量不够在角色活动区域密集布置光照探针检查探针的颜色插值结果烘焙结果有大量噪点低采样数导致或者灯光方向性过强增加Anti-aliasing Samples / Shadow Samples弱化强方向光增加环境光5.1 漏光和黑缝的终极排查思路漏光问题排查起来最费时间因为它在画面上的表现是“小面积异常”你往往要先花十几分钟定位它是哪块模型的问题。这里提供一个高效的排查顺序先开引擎Lightmap视图看异常区域的贴图UV分布如果UV重叠马上回建模软件处理如果UV正常检查模型法线是否翻转法线反了光照计算方向不对容易产生暗色“负光”面积最后再调阴影Bias这是兜底方案不建议太早改动。还有一招是我常用的在异常位置摆一个高强度实时点光源用实时光对比烘焙光的显示结果。如果实时光正常、烘焙光异常确认是烘焙问题如果实时光也不正常那就是模型网格本身的问题例如面片朝向、原点位置异常别跟烘焙死磕。5.2 动态角色与静态光照的接缝感探针密度才是关键有时候角色站在静态大物件旁边肩膀和头部的光照已经正确了但脚下却会出现一圈奇怪的“泛白”或者“泛黑”这通常是光照探针密度不足造成的。探针插值算法本质是三维空间线性插值如果两个探针之间跨越了亮度差异很大的区域角色移动过去的瞬间就会“闪变”或“漂移”。解决方式是在敌人刷怪点、玩家常驻交互区、色调变化明显的门洞和斜坡处手动增加探针密度。操作很简单但很多同学会忽略“探针也要手动指定作用范围”。默认探针是涵盖整个场景网格的在大场景里会导致某些区域探针过密造成内存浪费同时计算插值的性能也会变高。我做项目时会把探针网格拆分到几个独立区域只做房间和重要战斗场地的加密其他地方保持稀疏。这样既省内存发热也更贴心因为探针数量少了运行时插值计算的量也变少了。5.3 烘焙时间过长怎么办降低迭代成本的三板斧每次改一个小参数都要等十几分钟烘焙人的耐心会被快速消耗。这里分享三个降低迭代成本的技巧。第一做“预览烘焙”把分辨率降到20 texels per unit、反弹次数降到2、采样数降到2~4烘焙速度能缩短到原来的五分之一甚至十分之一。配合低精度预览图足够你判断光源布局是不是满意。第二锁定变化区域Unity支持“Bake Only”某一片区域操作方法是把光源、模型改动都集中在某个Cell或区域后用Selection Bake在Lighting面板里选择烘Selected只烘焙选中对象的影响范围。这样就不用全图重新烘焙了在Unreal里则可以把全局的LightmassImportanceVolume缩小到关注区域其他区域不动。第三用专业工具辅助如果你的项目用的是Progressive GPU LightmapperUnity内置烘焙时会用到显卡CUDA加速速度比CPU烘焙快一个数量级。前提是电脑有较新的N卡或A卡且显存足够大。这个工具在项目配置好后迭代效率提升非常明显强烈建议有条件的朋友用起来。6. 实测效果复盘温度降了多少、代价是什么讲完理论、参数、流程和坑最后放一组我自己项目里的实测数据给大家一个直观的感觉。测试项目是比较典型的ARPG战斗场景一个90平方米的古典庭院静态模型大约120万面静态光源有5盏1个主方向光、4个补光另有两个跟随角色动作的动态特效光源。设备是小米11测试软件为PerfDog。优化前场景开着实时光照和实时阴影稳定跑30帧功耗在5.5W到6.2W之间机身温度在10分钟游戏后达到44.5度帧率已经有轻微波动。实施光照烘焙后同样是这个场景静态光全部烘焙只保留两个动态特效光为实时模式。相同测试条件下功耗降至3.8W到4.1W机身温度稳定在40.5度左右帧率几乎全程贴着30帧不动。降幅大约是3到4度功耗降低了30%到35%——对于一个中低端移动设备玩家来说这已经是“不烫手”和“烫手”的明显分界线了。代价也不是没有。首先是烘焙时间场景初烘加上三轮迭代调整总共耗时约一个半小时其次是内存增加Lightmap Atlas总大小约120MB压缩至ASTC 6x6后不到50MB在可接受范围内。最关键的是动态角色的接触阴影质量有所下降必须靠探针和Shadowmask模式的补足来减弱影响。但这个“代价换温度”的综合性价比在发烫优化里确实高得离谱。7. 写在最后的一些个人体会做了这么多年渲染优化我最大的感受是——很多团队一遇到发热问题第一反应就是砍画质、降分辨率、锁帧率结果玩家体验直线下滑。但光照烘焙这种“换时间”的思路其实是在保质的情况下把成本从运行时转嫁到了开发期。机器的温度降了画质还没怎么丢这是最理想的结果。最后再分享一个小习惯每次调整完烘焙参数我会把当时的参数截图存到一个专门的经验文档里记录场景类型、烘焙耗时、最终温度和内存占用。攒了几十个案例之后新项目启动时我基本上能直接预测它烘焙后的大概表现不用再从头试参数。你也可以试着建一个这样的“参数-表现”对照表时间久了你会发现自己对项目性能的把控越来越准。光照烘焙不是银弹但它是发烫优化工具箱里最应该先拿出来的那把扳手。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进