移动端发烫?光照烘焙:渲染优化的性价比之王
做移动端渲染优化的朋友基本都经历过这么一幕手机上跑个带实时点光的室内场景电量跟瀑布似的往下掉背板烫到能当暖手宝。前几篇我聊过热管理、降频曲线、渲染分辨率这些方向这回专门讲讲光照这块。光照烘焙不算新技术但在发烫优化这个主题里它是典型的性价比之王——改动量不大、帧率立涨、温度直线回落而且对美术效果的破坏比想象中小很多。这篇就结合这些年做三维渲染和移动端优化的实际项目把烘焙的原理、参数怎么给、为什么对发烫这么有效一次性聊透。1. 发热背后的性能账单先别急着加风扇1.1 发热的本质每一次计算都在烧电费聊降温之前先把发热的账算明白。移动端发热主要来自SoC里的GPU和CPU高频运转而渲染一张画面的费用得按像素一笔一笔算。顶点变换、三角形光栅化、像素着色、混合、写入帧缓冲每个环节都在消耗电流。光源在里头是典型的“烧钱大户”GPU每帧要做逐像素光照就得朝每一个被照亮的片元再跑一遍光照模型点光源算距离衰减、方向光算角度点积、聚光灯还要套锥形范围一盏灯下来一堆指令。关键是这个计算不是一次性的它每帧都来一遍。游戏30帧每秒就是30遍60帧就是60遍。晶体管开关越频繁发热就越明显发热一高系统开始降频保温度降频导致帧率掉帧率掉用户感知就是“卡”。所以在不少机型上发烫根本不是硬件极限的问题而是GPU在重复干同一件高成本的事。要降温第一刀砍在光源和光照计算上永远比换散热设计更直接。1.2 实时光照的算力陷阱移动端尤其吃亏实时光照这张账单桌面端可能还能挂账移动端是真扛不住。移动GPU的每瓦性能远低于桌面端再加上手机是被动散热热量在薄薄的机身里堆着出不去。更麻烦的是如今手机屏幕都是高PPI像素数量大片元阶段的计算量等比放大。几座大山叠在一起光照计算就成了发烫的头号嫌疑犯。实时阴影更是能耗刺客。阴影本身要额外渲染一个深度pass从光源视角写着色器再做深度比较移动端通常用低分辨率shadowmap糊弄但依然很占GPU时间。如果场景里再有几个动态角色、几个点光、几片半透明雾效功耗直接起飞。我有一个习惯项目发烫先不着急优化Shader第一步就是检查画面里到底有几个实时光和实时阴影十次里有八次能从这里找出大头。1.3 为什么说它是“性价比之王”这里说的性价比不是省了一点点开销而是把一个“每帧都要跑一遍”的计算换成“只跑一次、后面读贴图”。实时计算按100万像素、场景里3盏灯算每帧就是300万次光照计算。烘焙后的运行期只需要多一次贴图采样。纹理采样在移动端是硬件加速的成本几乎可以忽略。这笔账算下来等于把每帧几百万次实时计算直接清零。我拿一个室内样板间项目做过对比场景有8盏点光和2个方向光开实时渲染时中端机GPU负载飙到60%温度一路往60度以上爬把静态光照切成烘焙光照贴图后GPU负载掉到28%附近同样画质温度低了五六度帧率稳如狗。当时项目组同事第一反应是“你是不是把画面效果砍了”实际上画面几乎没区别因为烘焙存下来的光照信息比实时算出来的更柔和、更自然。这就是它配得上“性价比之王”的原因。2. 光照烘焙到底烘了什么原理搞懂参数才不会瞎填2.1 提前把光“烤进”贴图光照烘焙的本质是对场景光照做预计算。引擎在编辑器阶段启动离线光照模拟工具追踪光源发出的光在场景里的反射、弹射与吸收最终把结果记录到一张或者多张纹理上。运行时物体表面就不再计算“光怎么照过来”而是直接按UV坐标从贴图里读取颜色值。理解这一步之后很多参数就有了直觉。分辨率决定贴图上每个像素texel对应模型表面多大的区域弹射次数bounce决定光线在场景里反射几次才被记录间接光的丰富程度跟它直接挂钩采样数则是光线追踪时用来去噪的抽样量。说白了烘焙就是把“现场做饭”改成“提前炖好一锅吃的时候热一下”。热一下当然比现场炒菜省火贴图质量也稳定。2.2 三个关键参数决定温度与画质的平衡光照贴图分辨率Texel Density移动端我通常用1个texel对应1到4个屏幕像素来评估。实操里Unity的Lightmap Resolution常用的默认值是50 texels per unit对移动端偏高我一般调到12到25。举个例子一个12米乘8米的展厅texel density给12最终lightmap图集大概2048像素、两三张贴图运行内存可以接受开到30的话图集可能变成八九张内存翻三倍烘焙时间也跟着翻但肉眼很难分辨出差异。这就是性价比的关键别人开50你开15你的温度自然就赢了一大截。间接光弹射次数Bounces室内场景2次就够用3次影子交界处更柔和室外1到2次即可。注意bounces每加一次烘焙时间经常翻两三倍画质提升却很有限属于典型的边际递减。移动端项目不追求离线渲染级别的真实感把次数压住是明智的。采样数Samples决定烘焙噪点。数值太低会出现颗粒感像老相机的ISO噪点太高则烘焙时间爆炸。我一般移动端设为1000到2000烘焙速度和噪点控制都还能接受。2.3 三种光照模式别一律烘烤主流引擎里光源模式一般分Baked、Mixed、Realtime三种。很多同学上来把所有灯光全改成Baked结果动态角色一进场景完全不受光照控制站在阴影里漆黑一片。这里要说明白烘焙解决的是静态物体的光照信息动态物体的光影得靠混合模式和光照探针一起补齐。我的习惯是纯静态场景全部Baked关掉实时阴影帧率最稳带动态角色的主场景关键方向光设成Mixed开启Shadowmask或者Distance Shadowmask静态部分走烘焙阴影动态物体只投影出短距离的实时阴影远处阴影读贴图立体感和性价比两头都能占住。少数需要特殊表演的局部比如一个需要手电筒光效的剧情关卡单独用Realtime但必须限定影响范围别让它照个没完。3. 实操环节一套不翻车的移动端烘焙流程3.1 开烘之前的三件小事第一件静态标记。这是最容易被忽略、又最容易出问题的环节。Unity里要把参与烘焙的物体勾上Static或者单独勾Lightmap StaticUE里要标记Static并生成Lightmap UV。只有标记为静态引擎才允许把光照信息写进贴图。我曾经接过一个项目墙面模型全都没勾Static烘焙完整个房间没有光照排查到凌晨才发现是这个原因当场血压就上来了。第二件UV2检查。烘焙需要第二套UV也叫lightmap UV。这套UV要求所有三角形在0到1的范围内不重叠并且要留出足够的padding间隔否则光照贴图颜色会互相覆盖出现莫名其妙的“漏光”和花纹。建议烘焙前在编辑器里开启Overlap检查或者导入三维软件里拆好UV2再进来。这一步虽然耗时但能省下后面大量排查时间。第三件场景分组。大场景别一次全丢给烘焙器拆成区块非常必要。关卡按房间、区域、楼层分批烘焙不但单次时间可控之后更新某个局部场景时也不用整套重烘。我遇到过地上叠了五百多把椅子的项目整场景烘焙直接卡了半小时后来按区域分批每批控制在两三分钟搞定效率天差地别。3.2 参数怎么给才是真正的“性价比”以Unity的Lightmap Settings为例我这里给一套适合移动端的起点配置参数建议值说明LightmapperProgressive GPU有显卡CPU烘焙也可以但慢Lightmap Resolution12~25 texels/unit根据场景实际尺寸微调别用默认50Bounces室内2室外1再高边际收益很小Samples1000~2000噪点与时间平衡Ambient Occlusion开启角落接触阴影几乎零运行时开销Directional ModeNon-Directional移动端省内存效果够用UE里面对应的是Lightmass相关设置比如StaticLightingLevelScale、Num Indirect Lighting Bounces、Indirect Lighting Quality概念都是等价的。核心思路移动端贴图密度控制在能接受的程度就行别拿桌面级的烘焙标准往手机上搬最后跑起来吃亏的还是自己。3.3 烘焙执行与结果验证参数调好之后点Generate LightingUnity或BuildUE等进度条走完。烘焙属于“憋大招”的环节强烈建议先选一个小范围区域试烘一次确认效果能接受再放全场景。我第一次做整套烘焙时没试烘结果场景一半泛白一半偏紫重新调参数重烘白白浪费了一个多小时。烘焙完成后别急着打包先在编辑器里切到光照显示模式检查结果。重点看三件事lightmap贴图有没有接缝和漏光阴影方向是不是符合光源摆放AO是不是自然墙角和桌底有没有出现奇怪的黑边。这些在编辑器里就能修上了真机再发现问题代价就大了。真机验证其实更重要。用Profile工具记录GPU耗时再用红外测温或者系统API记录温度曲线。我习惯烘焙前后各跑5分钟对比烘焙前GPU 60%负载、温度从36度爬升到61度烘焙后同样的场景压到28%负载温度稳定在50度左右这种数据就是最硬的交付物。所以每次项目做完我都愿意把这个对比实验跑一遍既验证方案也好向团队交代价值。3.4 动态物体的光影用探针补齐光照烘焙管的是静态场景动态角色总不能黑乎乎地站桩这里就要铅上烘焙光照探针Unity里的Light Probe和UE里的Lightmass Character Indirect Detail Volume都算这类方案。原理很简单在场景里提前布置一批采样点烘焙时记录每个采样点的入射光方向和颜色运行期动态物体找到最近的几个采样点做插值模拟出受到环境光照的效果。探针布置有个度的问题太密浪费内存太稀又会在角色路过光照突变区域时出现“变脸”。室内场景我习惯每隔2到4米放一个同一点高低位置错开走廊按3到5米一个基本能满足绝大多数移动端需求。这部分工作量不大但效果提升非常明显。角色进出阴影、站在门框这种双向光交界的区域时没有探针一定穿帮有探针就能平滑过渡。之前有个项目一开始没放探针角色在走廊里走过去时整张脸一会儿黑一会儿亮放完探针后问题完全消失美术同事还专门来问是不是更新了引擎。4. 常见问题与排查技巧实录4.1 漏光与接缝烘焙里最磨人的两个毛病漏光的典型场景是两堵墙的夹角处本该是暗角却亮得反常就像有光源从缝隙里渗出来。多数原因是UV2重叠或者lightmap padding太窄少数情况是模型Scale出现负值导致包围盒异常。排查顺序建议第一步看UV2有没有重叠第二步看模型的变换矩阵有没有负缩放第三步再扩大引擎里的Expanded Bounds参数试试。我一般会先检查UV因为这个问题在导入阶段就能发现非要烘焙完才发现返工成本高。接缝则是两块模型共享光照贴图时边缘出现一条明显差异的线。除了UV padding要预留到位还得检查Normals和Tangents是否统一。建模师经常在模型边缘忘了加光滑组导致法线突变烘焙结果自然在边缘位置产生不连续。这个坑属于“模型侧的问题烘焙背锅”建议把接缝检查写进资产验收流程里比事后补救效率高。4.2 烘焙时间爆炸先查这四个地方遇到烘焙时间异常优先级排序是Bounces设置太高、Samples太大、分辨率过高、场景过大没分组。还有一个容易被忽略的显卡太忙。Unity的GPU烘焙需要占用显卡计算如果编辑器视图还开着实时渲染两者会抢算力烘焙自然慢。解决办法是先关掉游戏视图暂停实时渲染再开始烘焙。时间衡量的标准线中等室内场景用CPU烘焙20到30分钟算正常GPU一般几分钟到十几分钟超过1小时基本就是参数有问题。我建议第一次做项目时用小盒子先试参数时间控制在1分钟内把质量档位和速度的关系摸透再套到正式场景。4.3 运行时光影跟编辑器不一致最典型的是烘焙完以后场景没有阴影或者角色阴影忽大忽小。先检查光源模式Baked光源不会再产生实时阴影动态角色阴影要由Shadowmask模式去接。Shadowmask下实时阴影的距离半径要控制住Shadow Distance推太大会把实时阴影成本重新拉回全场景等于前面全白干。移动端我一般把Shadow Distance设在15米左右再远的部分让贴图阴影撑住视觉上几乎看不出区别。还有一个容易踩的烘焙后调整了方向光的强度或颜色却没有重新烘。结果就是现场光照变成了黄昏影子却还是中午的样子玩家一眼就能看出来这种细节特别败观感。我们内部定过一个规矩凡是动过光源参数、材质Emission开关、修改了模型UV就必须重新烘焙否则不能提交版本。4.4 内存与包体膨胀也要提前管lightmap图集同样吃内存多开几个像素就是成倍的显存开销。移动端建议所有lightmap图集总尺寸控制在4096像素以内压缩格式优先ASTCAndroid和ETC2或BC7iOS千万别用RGBA32无压缩格式。一张2048的无压缩贴图就是16MB放几个区域内存和包体双双爆炸这是新手团队最容易忽略的点。图集数量也要控制不是越碎越好。尽量把同一区域、同一种材质的物体合并到一张图集里减少运行时加载次数和显存交换。纹理格式的优化属于润物细无声不做也能跑做了之后内存曲线会好看很多发热也能跟着降一点。5. 最后几个我每次都会用的实战细节这里分享几个平时不太有人系统的技巧。第一烘焙完的lightmap不要去手动改材质尤其不要随手关掉Emission再重开这会让烘焙数据和材质缓存对不上直接表现就是部分区域光照丢失。第二做移动端优化时别拿旗舰机做基准旗舰GPU算力太强实时光照也能跑得很欢没法暴露问题找一台中端机或者上代低端机来验温得到的数据才有参考价值。第三烘焙的分辨率和图集尺寸不只是美术问题还是发热和功耗问题。每一张多余的lightmap贴图都在增加内存带宽开销而带宽恰恰是移动端GPU发热的重要来源。所以做烘焙优化时我用“能省则省”的心态对待像素密度经常能在画质不变的前提下多砍掉几十毫瓦功耗。这几年接的工程项目里凡是发烫问题十有七八把光照层处理好就解决了一大半。光照烘焙不是万能的但它确实是我名单里优先度最高的低成本高收益手段。如果手头项目还在被高温和掉帧折磨建议先别急着换机型、加外置散热把手电筒一样的实时灯关掉把烘焙参数调明白下一帧的温度会告诉你答案。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →