ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

光照烘焙原理与实战:从Lightmap到漏光排查

光照烘焙原理与实战:从Lightmap到漏光排查

做游戏场景的人应该都有过这种经历:场景里摆了几十盏灯,实时光一开,帧率掉得亲妈都不认识;或者烘焙完一看,墙角漏光、模型接缝处一条条黑线,连美术小姐姐都不想跟你说话。光照烘焙(Light Baking)就是把“运行时算光照”这个重活提前到编辑器里干完,让游戏跑起来的时候只负责“贴图查询”,性能自然就上来了。

这篇文章从原理讲到实操,包含我在几个项目里踩过的坑和总结出的排查套路,适合刚接触烘焙的开发者,也适合那些已经烘焙过但总觉得“哪里不对”的朋友。我尽量不堆术语,该解释的地方用大白话拆开讲。

1. 先搞明白:烘焙到底在“烘”什么

1.1 光在计算机里是怎么“算”出来的

要理解烘焙,先得知道实时渲染的光照是怎么来的。游戏引擎渲染一帧画面,本质上是在计算每个像素最终显示什么颜色。这个颜色由两部分组成:直接照射到物体表面的光,以及经过其他物体反弹后照过来的光。

直接光好理解,太阳、灯泡直接照到脸上,亮就是亮,暗就是暗。间接光就不一样了——阳光照到地面,地面又把光反弹到墙壁上,墙壁再反弹到天花板上,最后整个房间都亮起来。现实中这种反弹是无限次的,但计算机不可能真的模拟无限次反弹,只能通过算法近似。

实时光照方案(比如实时全局光照)会在每一帧都做这些计算,代价是巨大的消耗。你想象一下,每秒钟60帧,每帧都要追踪成百上千条光线的路径,这相当于让CPU和GPU每秒做几十万次光线追踪。

1.2 烘焙的核心思想:把计算提前

烘焙的思路很朴素:既然这些计算每帧都在做,而且场景里静态物体的光照结果不会变,那我干脆在编辑器里就把光照算好,把结果存成一张张纹理贴图,运行时直接采样不就行了?

这就是 Lightmap(光照贴图)的由来。烘焙过程会先算出每个静态物体表面的光照亮度与颜色,写进贴图里,运行时引擎只需要把这张贴图叠加到物体本身的贴图上。

用个生活类比:实时光照就像你每天都要重新做一遍晚饭,烘焙就像提前把饭做好放进冰箱,吃的时候热一下就行。代价是什么?饭可能会变味——光照被“冻结”了,场景里任何物体的移动、灯光的颜色变化都不会影响烘焙结果。这就是为什么动态物体需要额外的方案(后面细讲)。

2. 光照烘焙的原理:算法与核心概念

2.1 直接光照与间接光照的分离处理

Unity 的烘焙系统把光照分成两条线处理。直接光照部分,引擎直接根据灯光参数和物体朝向算出结果,这个相对快;间接光照才是烘焙的重头戏,因为它涉及光线在场景里的多次反弹。

模拟间接光照最经典的算法是辐射度算法(Radiosity)。核心思想是把场景里所有表面划分成一个个小面片,每个面片既会接收光,又会向周围发射光。从一个光源出发,光先照到某些面片,这些面片再把自己的亮度贡献给相邻面片,循环往复,直到能量收敛。面片划分越细,结果越精确,但计算时间和内存消耗也越大。

Unity 实际用的方案经历了几个版本迭代。内置渲染管线的 Enlighten 系统就是基于类似的思路做的实时GI预计算;到了高清晰渲染管线(HDRP)和通用渲染管线(URP),又加入了基于 Progressive Lightmapper 的路径追踪方案——它模拟真实的光子运动,逐像素独立计算,质量和可控性都比老方案好不少。

2.2 Lightmap:静态物体的“光照贴图”

烘焙的结果主要是 Lightmap。每张 Lightmap 本质上是一张纹理,它记录的不是颜色贴图那种“物体表面画了啥”,而是“这个位置受到多少光照”。运行时渲染时,引擎把这套光照信息和物体自身的纹理混合在一起,就得到了最终看起来“被光照过”的效果。

这里有个知识点:Lightmap 的精度由 UV 决定。模型必须有一套独立的“光照UV”(也叫第二套UV),这套UV要尽量铺满整个纹理空间、没有重叠,否则烘焙出来的光照信息会互相覆盖,表现就是奇怪的色块或接缝。Unity 对静态物体默认会自动生成光照UV,但自动生成的结果在复杂模型上往往不理想,后面实操部分我会说怎么手动控制。

2.3 Light Probe:给动态物体补光

静态物体可以用 Lightmap,动态物体呢——角色走来走去、怪物刷出来,它们没有固定的光照结果可用,全靠运行时计算又太贵。Light Probe(光照探针)就是用来解决这个问题的。

你可以把探针理解成“采样点”。在场景里摆上一组探针,烘焙时引擎会计算每个探针位置的光照信息,运行时对动态物体,引擎根据它周围几个探针的数据做插值,模拟出当前位置的光照。

探针摆放密度需要根据场景复杂度调整。摆太稀,动态物体从一个区域走到另一个区域时光线变化很突兀;摆太密,烘焙时间和内存都上去了。经验值是:室内场景转角处、门口、走廊拐角这些光照变化剧烈的地方必放,空旷区域可以放宽。

3. 完整烘焙流程实操

3.1 场景预处理:能静态的别动态,能简化的别复杂

烘焙第一步不是点按钮,是检查场景。我见过不少新人一上来就开烘,结果烘焙完一堆问题,根源其实在前期准备就没做对。

静态物体和动态物体要明确区分。烘焙只处理标记为 Static(静态)的物体,所有不动的物体——墙体、地板、柱子、家具——都应该标记为 Static。但这里有个坑:Unity 的 Static 是一个大总开关,勾选它意味着这个物体同时被静态批处理、遮挡剔除、光照烘焙等多个系统接管。如果你只需要光照烘焙,记得打开 Static 下拉菜单,只勾选 Contribute GI(贡献全局光照),而不是直接勾选总 Static,不然可能引入其他系统的额外开销。

模型处理上注意两点。第一,光照UV要检查,X轴和Y轴的UV范围应该在0到1之间,超出会被裁剪;第二,物体之间的间距要留够,物理上完全贴合的模型很容易烘焙出漏光,因为光线在极小缝隙里反弹时采样会出错,后面排查部分细说。

3.2 灯光与材质设置

烘焙用的灯光和实时灯光在参数上有微妙差别。灯光组件上的 Mode 属性是关键:设为 Baked(烘焙)或者 Mixed(混合)。Baked 灯光只参与烘焙,运行时完全不产生光照,适合那些永远不会变的光源;Mixed 灯光则既烘焙静态部分,又实时照亮动态物体,是室内场景最常见的方案。

材质方面,烘焙关心的是材质的反射属性。金属和镜面类材质在烘焙时处理方式不一样,如果你用了标准着色器,注意 Smoothness(光滑度)和 Metallic(金属度)的设置。一个常见误区:Smoothness 拉满的材质在实时渲染里很亮,但烘焙出来的间接光照可能偏暗,因为高光滑表面会把光反射走而不是漫反射到周围。遇到烘焙结果比预期暗,优先检查大块地面和墙壁的材质参数。

3.3 烘焙参数详解

打开 Lighting 窗口(菜单 Window → Rendering → Lighting),切到 Scene 标签页,这里是核心配置区。

直接光照强度(Direct Samples):控制直接光照的采样数,数值越高,直接光越平滑,但烘焙时间线性增长。一般64到128够用,除非场景里有大量锐利的阴影边缘需要更干净的效果。

间接光照强度(Indirect Samples):影响间接光的平滑度,这个数值决定光线反弹时采样的细密程度。室内场景建议256起步,大面积露天场景可以降到128。采样数不够的表现是:间接光区域出现一层一层的“噪点条带”,尤其是墙角、天花板上特别明显。

反弹次数(Bounces):间接光反弹几轮,0就是只有直接光,1轮反弹画面会显得平,一般2到3轮就能得到令人满意的全局光照效果。再往上,收益递减,烘焙时间成倍增加,不划算。

烘焙分辨率(Lightmap Resolution):单位是“像素/单位长度”(texels per unit)。这个参数直接决定细节。常见值:小房间、近景物体用30到50,中等场景20到30,大型室外场景10到15。分辨率越高,贴图越大,内存占用和显存占用都翻倍,别盲目追求高分辨率。

灯光贴图大小(Lightmap Size):最终生成的光照贴图单张尺寸上限,一般是512、1024、2048。如果你的场景很大,分辨率又高,Unity 会自动把贴图切分成多张;切分太多会导致物体之间出现可见的接缝,需要配合下面的参数控制。

3.4 执行烘焙与结果验收

参数设置好,点 Generate Lighting(生成光照),Unity 会开始计算。中间进度条显示的是各个进程的状态,我习惯开着日志窗口观察有没有警告信息。

烘焙完成后第一件事:别急着保存,先跑一遍场景。在 Game 视图里转一圈,重点看几个地方——墙角有没有漏光、物体交界处有没有黑缝、大面积平坦区域有没有色块断层。发现问题,先别直接调烘焙参数,先想想是场景问题还是参数问题。我自己的习惯是:先拉低间接采样找噪点,再用分辨率找细节问题,逐项排查而不是一把梭把参数全部拉满。

4. 常见问题与排查技巧实录

4.1 漏光:最让人头痛的场景污染

漏光的典型表现:两个房间之间隔着墙,烘焙完却能看到隔墙透光,或者天花板边缘莫名其妙亮一块。原理不复杂:光照贴图的分辨率有限,两个靠得很近的面在贴图上的采样容易互相“串色”,光线从物体缝隙间“挤”了出去。

排查和解决手段按优先级排列:

  • 检查模型间距离,物体之间至少要留出场景规模的千分之一左右的空间,墙体和地板不要做无缝贴合。
  • 用 Unity 自带的模型检查模式打开网格预览,看光照UV是否重叠,重叠就是漏光的直接根源。
  • 在光照贴图上用调试视图直接看漏光区域对应的纹素位置,定位到具体是哪块UV出了问题。
  • 终极手段:在漏光位置手动加一个隐藏的盒子碰撞体作为遮挡,但这是“打补丁”的做法,能不用尽量不用。

4.2 接缝黑边:UV问题的经典症状

模型上出现一条条黑色的细线,位置通常在多个部件拼接处或者UV接缝处。原因是光照贴图的滤波(Filtering)把不同UV岛(UV岛就是UV上一块块独立的区域)之间的像素混合了,在边界处产生暗色晕染。

解决办法有几种:一是在模型导入设置里勾选“Generate Lightmap UV”并调整打包参数,让UV岛之间留出更多边距(Padding);二是把光照贴图的 Filtering 模式从 Automatic 改成表格式固定值,配合扩大边缘的像素数。还有一个偏方:如果你用的是 HDRP,把烘焙后处理里的“接缝修复”强度调高,能在一定程度上掩盖线性接缝。

4.3 噪点和光照条纹:采样不足的信号

噪点表现为大面积漫反射区域上星星点点的亮暗颗粒,条纹则表现为一圈一圈的同心圆状色带。两个都是采样数不够导致,直接提高 Indirect Samples。但有些情况是场景中有些大型物体表面面积过大,采样密度在局部不足,这时候与其全局提高采样数,不如把大物体拆分成多块子网格,或者用碰撞体遮挡分割。

还要记住一个重点:调大采样数后,如果问题消失了但烘焙时间暴涨2-3倍,说明你的场景结构不合理。优化方向是精简高面数模型、删除细碎小物体(比如大量螺钉、小零件),这些物体在烘焙时一样参与计算,成本极高但对画面贡献却很小。

4.4 动态物体看起来“假”

动态物体的光照来自光照探针,如果探针没摆好,角色走过去会“忽明忽暗”,甚至站在明亮区域里身上黑乎乎一片。常见原因:场景里探针数量太少,或探针在烘焙时被大物体挡住。

排查方法:Scene 视图里切到光照探针显示,看每个探针的位置是否在空间通路上,别把探针嵌进墙体或地板内部。摆放间距在室内场景建议不超过5米,高低错落覆盖角色的整个活动高度范围。还有一个小技巧:在角色脚底和头顶各放一排探针,可以让角色站立和跳跃时的光照过渡更自然。

4.5 一键排查速查表

症状首要怀疑推荐手段
墙角漏光物体贴合过近 / UV重叠模型间距调整,检查光照UV
拼接处黑线光照UV岛间距不足加大Padding,调整Filtering
大面积噪点间接采样不足提高Indirect Samples
色块断层分辨率过低提高Lightmap Resolution
动态物体明暗跳动探针摆放不当重建探针布局

5. 光照烘焙在项目里的实战建议

5.1 选对渲染管线和烘焙器

你在开始搭建项目之前就要想清楚用哪套方案。内置渲染管线配 Enlighten 适合老项目和小型移动游戏,烘焙快但质量上限低;URP 配 Progressive Lightmapper 是目前大部分新项目的主流,质量好、可控性强,移动端表现也过得去;HDRP 的烘焙质量最高,但对显存和烘焙机的硬件要求也最高,适合PC和主机项目。

我做过一个对比:同一个室内场景,Enlighten 烘焙用了十几分钟,Progressive 用了一个多小时,但 Progressive 的间接光质量肉眼可见地更自然。如果你的项目对画质有要求,花这个时间值得;如果只是需要快速出效果验证玩法,先用 Enlighten 也不是不行,后面再切换烘焙器重新烘焙就好——注意烘焙数据要清理干净再切,否则可能出现新旧数据混用的脏状态。

5.2 场景分块烘焙与合批策略

场景特别大的时候,一次性烘焙整个关卡既慢又难调试。我的做法是:按功能区把场景拆分成多个子关卡,每个子关卡单独烘焙,最后通过加载拼接。这样做的额外好处是——某个区域改了之后,不需要整个大场景重新烘焙,只重烘受影响的子关卡就行,迭代效率提升非常多。

这里有个容易忽略的约束:跨场景的光照数据能不能衔接好,取决于每个子关卡是否用了相同的烘焙参数和灯光设置。建议把烘焙参数做成一个预设(Preset),项目组所有成员统一用同一套,避免各人调各人的导致拼接处光照跳变。

5.3 烘焙与后续迭代的工作流

实际项目里,场景不是一锤定音,美术会反复调灯光、调材质、动模型。如果每次修改都全量重烘,时间成本吃不消。进阶工作流是:日常迭代只开低分辨率、低采样快速烘焙,确认整体氛围没问题后,再切高参数做“出图级”烘焙。Unity 支持把烘焙结果缓存起来,灯光参数微调后可以增量烘焙,只重算受影响的光源附近区域。

还有一个省时间的技巧:把烘焙任务丢给专门的烘焙机远程执行,本地继续做其他事情。如果有多个项目并行,用命令行批处理模式跑烘焙脚本,然后自动把结果打包回来。这个我之前在团队里搭过一次,流程跑通之后烘焙效率提升非常明显。

5.4 性能预算与存储控制

说了这么多烘焙的好处,也得提醒一句:Lightmap 不是免费的,它消耗的是内存和显存。一张1024x1024的 RGB9E5 格式光照贴图大约占4MB,一个大型场景可能有几十上百张贴图,算下来几百MB就没了。移动端项目尤其要控制:分辨率宁低勿高,贴图格式尽量选择带压缩的,定期检查场景内光照贴图的总容量。

我之前有个项目就因为光照贴图过大导致低端机闪退,排查下来发现是有个露天大场景用了太高的烘焙分辨率,浪费在几乎纯色的天空区域。后来把该场景的烘焙分辨率降了三分之二,画质肉眼几乎没变化,内存问题直接解决。光照贴图和贴图资源一样,也要定期做“瘦身”审计。

个人体会

做光照烘焙这几年,我最深的感觉是:烘焙不是“点个按钮就完事”的步骤,而是一套需要综合考虑场景设计、模型规范、参数取舍的工程流程。烘焙前多花十分钟检查模型和UV,比烘焙后花两小时修问题划算得多。参数上也不存在万能配置,每个场景的最佳参数都需要根据最终画面和性能预算反复试出来。如果你刚开始学烘焙,先拿一个小房间练手,把每个参数的作用摸清楚,再进大型场景就不会手足无措了。

RELATED READING

延伸阅读

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