ARTICLE DETAIL

资讯详情

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

移动端游戏发热优化:光照烘焙如何让GPU负载减半、温度直降4℃

移动端游戏发热优化:光照烘焙如何让GPU负载减半、温度直降4℃ 这个系列前面聊过不少给手机降负载的手段但真正让我觉得一劳永逸、性价比极高的还得是光照烘焙这张牌。光照烘焙解决的是移动端发烫优化里最核心的一块把每一帧都要做的光照计算提前换成一次贴图采样。很多团队对发热的直觉是“手机散热不行”“芯片太激进”但从项目可控度来看真正能改的是渲染成本。光照烘焙就是那个能在不牺牲观感的前提下把GPU负载实打实砍掉一大截的操作。这篇我结合自己做过的一个移动端场景改造把光照烘焙为什么能降温、接入时要注意什么、实测数据能到什么水平以及烘焙过程中容易踩的坑全部捋一遍。适合正在做发烫治理的客户端开发、技术美术也适合第一次接触烘焙的新人。1. 发热的账本实时光照是GPU负载里的重头开销1.1 移动端发热的本质是芯片持续高负载手机发烫表面上是外壳热本质上是SoC里的GPU长时间处于高功耗状态。移动端设备散热面积小没有主动风扇热量一旦积累起来芯片就会触发降频然后表现为帧率波动、画面变卡玩家体感就是“又烫又卡”。这个循环一旦形成体验基本很难救回来。所以做发烫优化思路不应该是“想办法给手机降温”而是“想办法让芯片不需要做那么多事”。只要每一帧的渲染负载降下来功耗就会降温度自然跟着降。这也是为什么所有发热治理方案最后都会落到渲染开销上——要么减少绘制量要么减少单个像素的计算量要么减少带宽占用。光照烘焙属于第三种思路里最典型的代表把本来逐帧动态计算的光照信息提前离线算好运行时只需要读数据。它跟LOD、合批这类优化还不一样LOD是在砍几何复杂度合批是在砍Draw Call光照烘焙直接砍的是着色器里最贵的那部分数学运算。1.2 一帧画面里实时光照到底在算些什么要理解烘焙为什么省电得先看清实时光照在一帧里做了多少事。假设一个室内场景有一个平行光当太阳两盏点光当装饰灯再有个动态角色在走动平行光会对所有接收光照的物体产生漫反射和高光计算每个像素都要跑一遍光照模型。两盏点光意味着每个像素还要额外叠加两次光源贡献Shader里就要多算两遍漫反射高光衰减。平行光要投射阴影就必须以光源视角额外渲染一张Shadow Map这本身就是一次全场景深度渲染。动态角色要跟地面有接触阴影角色身上通常还要单独开实时阴影再叠加一层阴影贴图采样。如果场景里有间接光纯实时渲染基本算不动通常只能靠环境光或者各种近似方案凑合。这些运算叠加在一起GPU的时钟周期和带宽都在疯狂消耗。我在低端机上测过一个不算复杂的中型场景光平行光和两盏实时点光光栅化加阴影采样就能吃掉8到12毫秒的GPU时间。对移动端来说这个数字已经意味着帧率顶不上60帧发热也很明显。1.3 为什么优先动光照而不是先调Shader有人会问Shader里加一些分支优化、减少Overdraw不也能省电吗能但收益量级完全不同。Shader微调通常是在5%到15%的范围内抠而光照烘焙是无缝切换到另一条成本曲线把逐像素光照计算变成一次纹理采样省掉的是80%以上的光源相关开销。尤其是场景里静态物体占比高的情况下烘焙的收益最大。一个典型的游戏关卡地板、墙体、道具、建筑这些静态元素通常占到90%以上的画面面积这些物体如果都走实时计算GPU大部分时间都在做无用功。把它们转换成烘焙光照等于让GPU退掉大部分重复性的加班只保留少数真正需要动态计算的部分。2. 光照烘焙的省钱原理把“逐帧现做”变成“查表拿结果”2.1 Lightmap的本质是提前算好的光照结果烘焙之后生成的Lightmap光照贴图说白了就是一套纹理纹理里每个像素存的是对应物体表面接收到的光照结果包括直接光和间接光。运行时Shader不需要再计算光源方向、衰减、遮挡只需要采样这张贴图把颜色叠加到材质上。举个生活化的例子实时渲染就像每天中午去厨房现切菜、现炒菜一道菜要忙活半小时烘焙相当于提前把菜做好打包放进冰箱吃饭的时候微波炉加热三分钟就端上桌。观感上还是那份饭但后厨的工作量完全不是一个量级。具体到引擎实现里Unity和Unreal都提供了完整的光照烘焙管线。开发者把物体标记为静态、把光源设置为烘焙模式、设置好分辨率然后就交给引擎离线计算。引擎会算出每个物体表面的光照结果并排成一张或者多张Atlas贴图。运行期间引擎会把这张贴图自动绑定到材质上原本几十上百条GPU指令的运算变成几次UV采样和一次插值。2.2 不只是漫反射Light Probe、Shadowmask、Reflection Probe一起配合如果只是静态物体表面有烘焙光动态角色站在场景里会显得“浮起来”——因为角色身上没有正确的环境光反馈。所以完整的烘焙方案不是一个Lightmap单打独斗而是一套组合Lightmap负责静态物体表面的直接光和间接光。Light Probe光照探针在场景里布一些采样点记录该位置的入射光信息。动态物体运行时通过周围探针插值得到光照让角色融入烘焙场景。Reflection Probe反射探针用来给金属、玻璃这类高光物体提供环境反射信息弥补烘焙贴图不包含高光反射的问题。Shadowmask模式把静态物体的阴影烘焙到贴图里的同时预留一些实时阴影能力让动态物体也能在场景里投射正确的影子。这套组合的重要性在于光照烘焙不是把整个场景打回“没有阴影的贴图感”而是把静态信息用贴图保存动态信息用探针和实时阴影补足。画面层次和动态交互都能保留但计算的绝对量大幅下降。2.3 移动端尤其划算的另一个原因分辨率感知不高在PC大屏上烘焙贴图的细节差距可能通过走近看还是能发现但在手机上屏幕尺寸和PPI决定了玩家不会贴着一个物体去看光照过渡。中等分辨率的Lightmap在手机上已经足够平滑劳动成本却比高精度烘焙低一个数量级。这个特性让移动端成了光照烘焙收益最大的平台你付出离线计算的成本换来的是运行时几乎为零的光照开销同时画质观感损失很小。3. 实战接入Unity和Unreal里把光照“烤”熟的标准动作3.1 Unity侧从标记Static到出图在Unity里做烘焙第一步是把需要烘焙的物体标记为静态。选中场景里的模型在Inspector面板右上角点开Static下拉菜单选择Lightmap Static或者直接在Mesh Renderer组件里勾选Contribute GI。这里要注意物体必须是静态的烘焙结果才能被正确生成如果物体没有标记即使你再怎么调光源烘焙出来它也都是黑的或者实时计算。光源的设置同样关键。需要全局定向光就选平行光组件的Light Mode有三种选择Realtime继续实时计算适合玩家交互产生的动态光。Baked完全烘焙运行时没有任何光照计算代价适合装饰灯、氛围灯。Mixed混合模式静态物体的阴影烘焙但Shadowmask和Light Probe还能对动态物体产生投影适合主光源。移动端我个人的习惯是主平行光用Mixed普通装饰光全部Baked只有真的跟随角色或者表现特效动态变化的光源才保留Realtime。这样既保留了动态物体最基本的影子反馈又不会把实时光源数量拖起来。设置完成后打开Window Rendering Lighting面板在Scene标签里把Lighting Mode选好然后点击Generate Lighting。Unity默认的Progressive GPU Lightmapper会逐步显示烘焙进度方便你边烤边看效果。3.2 Unreal侧Static光源与Build LightsUnreal的流程思路一样但入口不太一样。UE中网格体要标记为Static光源则设置为Static或Stationary。对于移动端项目我的做法是主平行光用Stationary因为Stationary光源保留了动态物体投射阴影的能力但静态物体依然走烘焙结果点光源除非是关键交互对象否则一律Static完全烘焙。在World Settings里有一个经常被人忽略的参数Static Lighting Level Scale它控制间接光照的体积分辨率。默认值是1烘焙细节高但内存和时长也跟着高。移动端我一般调到0.3~0.5画面观感下降不多Lightmap和VLM体积占用却会明显减少烘焙时间也快不少。设置完成之后点击Build菜单下的Build Lighting Only或者直接按快捷键引擎就会调用Lightmass开始烘焙。Unreal的好处是烘焙结果会直接在视口里反馈光影质量很直观。3.3 移动端专项参数建议很多项目第一次烘焙直接沿用PC的配置结果烘焙完一看包体和内存爆了低端机也扛不住。移动端烘焙参数要单独调我的常用基线是这样Lightmap Resolution每单位世界距离的像素密度移动端建议10~20中小型房间15左右足够大型野外场景可以降到8~10。Directional Mode优先选Non-Directional能省一半内存。只有场景里明显需要法线方向光影变化时才考虑Directional。CompressionUnity里开启Lightmap压缩Android选ETC2或ASTCiOS选ASTC。压缩能显著减小包体和运行时内存但注意压缩程度太高会产生漏光或颜色断层要平衡。Lightmap Padding单一图集内不同UV岛之间的间距太小时相邻物件容易互相渗色移动端建议保持在4~8像素左右。Bounces间接光反弹次数Unity和Unreal默认2~3次足够再高画面已经看不出差异纯纯增加烘焙时间。这几个参数如果场景不是特别复杂基本能让烘焙结果在容量和画质之间达到一个比较稳的平衡点。调试阶段可以先调低分辨率快速出图看构图确定灯位和氛围没问题再回到高分辨率做最终烘焙。4. 实测数据一个中等场景烘焙前后的性能与温度对比4.1 测试场景和方法这部分的数据来自我之前做的一个移动端室外街区加室内小场景的改造。场景里有街道、房屋、绿化、室内家具等静态物体实行动线固定角色在街道和房间里穿行。手机测试机选用一台骁龙8系中端机系统亮度固定50%开启飞行模式关掉后台进程每人同一视角路线跑3次取平均。记录工具上Unity侧用Profiler里的GPU模块看单帧GPU耗时整机功耗和温度用PerfDog配合第三方脚本读取。温度测量放在30分钟连续跑图之后记录机身最热位置的红外温度。4.2 关键数字对比改造前场景里有一个Realtime平行光外加三盏Realtime点光所有静态物体都有实时阴影。改造后平行光切成Mixed点光全部Baked静态物体全部成为Lightmap对象动态角色保留Light Probe和Shadowmask。指标烘焙前烘焙后变化GPU单帧耗时低负载场景12.3 ms5.8 ms下降约53%GPU单帧耗时复杂街区19.6 ms9.2 ms下降约53%平均FPS4259稳定提升整机功耗4.8 W3.3 W下降约31%30分钟后机身最高温度43.1 ℃38.9 ℃下降4.2 ℃Lightmap内存占用无约5 MB换取大幅运算下降这个数据不是绝对的但它能反映出光照烘焙的真实量级GPU耗时直接腰斩整机功耗下降约三成30分钟后机身温度从“明显烫手”回到“温温的”状态。对玩家而言这种温度差异已经能从握持手感上明显感知出来。4.3 数据要这么读先看瓶颈再看指标同类项目如果想复现这个流程我建议先把测试目标拆开。第一优先级是看GPU耗时是否成为瓶颈如果项目瓶颈在CPU端烘焙效果会被其他因素盖住。第二步看GPU耗时下降后整机功耗是否跟着下降很多时候Shader省了但CPU还在忙帧率没变温度也没变需要配合其他优化一起做。另外要控制变量环境温度差几度、手机亮度差10%、后台多跑一个App甚至手机壳的导热性能都会影响温度读数。最好是同一天测试同一台设备跑同一个场景路径才能得到有说服力的对比数据。5. 烘焙实战中最容易踩的坑从“烤不熟”到“烤糊了”的排查链路5.1 场景里总有物体是黑的或实时计算的最常见的现象是烘焙完某面墙黑得离谱或者某个装饰物看起来跟周围环境格格不入。究其原因基本就是物体没有被正确标记为Static或者光源里还残留Realtime模式。排查链路我一般是这么走的先选中那个物体看Inspector里的Static标记是否勾上。然后在Lighting窗口刷新后直接Generate Lighting烘焙完看一眼Lightmap列表里有没有生成该物体的图集。如果是物体本身UV2不对Unity会把它单独放进一个很小的图集表现为烘焙结果特别糊。一个很容易被忽略的细节是模型导入时如果没勾选Generate Lightmap UVsUnity会提示需要第二套UV。对有高级UV技巧的建模师来说这是基本功但很多时候从美术手里拿到的模型只有一套UV这时可以勾选引擎自带的自动展UV功能并把Pack Margin调到0.05左右避免UV岛之间相互渗色。5.2 接缝、漏光和颜色断层烘焙完最常见的视觉瑕疵就是接缝——在两个相邻物体的交界处看到一条明显的亮线或暗线。原因通常是光照贴图的UV岛之间没有足够Padding或者不同物体用了不同分辨率导致采样跳变。漏光的根源大多是Lightmap分辨率不够或者各向异性压缩太狠。ASTC 6x6压完之后颜色过渡变粗糙灯光在模型边角处的渐变就会出现断裂。解决办法是先看看是不是分辨率问题调到更高分辨率再试如果分辨率已经够高还漏则多半是压缩格式问题改成ASTC 4x4或者ETC2会好很多。还有一类发黑的问题跟Lightmap无关是后处理里的AO叠加太猛。烘焙自带AO通常已经很柔和再叠加屏幕空间AO会把墙角、边缘压得太黑。移动端我建议烘焙时把AO信息放在Lightmap里后处理关掉或降强度画面反而更干净。5.3 动态角色到了烘焙场景里发灰烘焙场景里角色显得跟环境脱节通常是Light Probe没布好。烘焙之后动态物体的实时光照不能直接用需要通过探针插值。如果场景里探针数量不足、位置放得太密或太疏角色走到一些区域就采不到有效光照整体发灰发闷。正确布法是沿着玩家动线、门前、转角、房间中心这些关键位置放置探针密度以“两米左右一个”为基准再根据场景明暗变化加密。探针千万不要塞进墙体或大型物体内部测出来的一帧光照会污染整片插值结果。布完探针之后在Game视图里让角色走一圈动线检查明暗过渡是否顺滑。5.4 烘焙时间太长、Lightmap图集过大烘焙时间会随着GI双反弹、Lightmap分辨率和场景面积飞速膨胀。美术这边调一版灯光跑去烤一次两小时这基本没法迭代。我的做法是迭代阶段用低分辨率加仅直接光模式只看光影构图确定最终灯光氛围后再开完整烘焙一次出最终结果。图集过大的问题则要从“图集复用”的角度解决。把场景分成多个区域用Lighting窗口里的Lightmap Parameters分别控制不同区域的烘焙分辨率。比如核心战斗区用20 texels/m边角堆放区用10 texels/m能有效控制最终贴图总量。同时图集尽量控制在2048或1024以内移动端超过4096不仅内存高GPU采样带宽也会成为新的瓶颈。5.5 一个容易忽略的坑动态光源没清干净烘焙完静态物体很完美但只要角色进入场景某个地方就会突然出现一道黑影或高亮的色斑。排查到最后往往是场景里还残留着某个并非需求必选的实时点光。这种光在运行时一进入照射范围就会实时计算不仅成本高还容易跟烘焙结果产生双重照明。处理方式非常粗暴把不需要动态的光源全部切成Baked保留的最少两盏交互光设置为Mixed并控制照射半径。开Frame Debugger看一下每个像素的光照贡献来源能清清楚楚看到哪个光源在干活。6. 别把烘焙当万能药适用边界与组合拳6.1 哪些玩法不能只靠静态烘焙光照烘焙的前提是场景光照基本不变。如果游戏有实时昼夜循环、动态天气、可破坏墙体、玩家可制造光源等玩法纯烘焙方案就没办法满足——Lightmap是离线算好的运行时不会跟着昼夜变化。这种项目我建议采用“分层烘焙微量实时”的混合思路把结构场景整体烘焙为基调昼夜变化用环境光和Directional Light的旋转、颜色、强度来模拟配合后处理调色。动态光源严格控制在个位数其余全部靠烘焙和探针提供基础光照。这样既保留了核心玩法需要的动态光照反馈又不会让实时光照成本失控。6.2 跟其他移动端优化手段怎么衔接光照烘焙应该排在渲染优化的第一梯队但它不能替代所有工作。它跟LOD、遮挡剔除、纹理压缩是互补关系烘焙负责砍逐像素光照计算LOD负责减少顶点数遮挡剔除负责减少可见物体数量纹理压缩负责降低带宽。四者叠加起来才能把一个场景的渲染成本压到很低的水平。一个比较成熟的移动端组合是静态物体全量Lightmap 角色/特效少量实时光 阴影贴图仅作用于近处 反射探针预烘焙。这样场景在任何角度看都有稳定光影动态交互点也不至于干瘪。再叠加上Streaming Mipmap和Lightmap的纹理流送内存也能控制在可接受范围内。6.3 我落地时的几条经验项目做发烫治理时我把“GPU耗时、整机功耗、峰值温度”三个指标固化成每次构建的必测项每次改动光照配置后都跑一次这三项数据而不是只看帧率。帧率快被垂直同步卡住时根本看不出性能变化功耗和温度才是真正反映发热的镜子。另外烘焙迭代的流程养成习惯后效率会高很多。前期先用最低分辨率快速出图定调中期开中等分辨率检查美术细节最后只保留高分辨率出包。不要每一次都在高分辨率下反复重烤那会把团队的心态和迭代节奏全拖垮。最后分享一个小技巧只要条件允许把Lightmap的最终结果单独用一个目录管理版本变动时通过Asset Bundle只更新贴图资源。这样美术调整灯光后程序不需要重新构建整个场景移动端补丁体积也能控制在很小的范围内。这套流程跑顺了光照烘焙不只是一次性能优化更是整个项目提效的一部分。
返回列表