ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Unity Lightmap完全指南:烘焙原理、参数调优与内存优化实战

Unity Lightmap完全指南:烘焙原理、参数调优与内存优化实战 在实际游戏开发里提到“Lightmap”很多人的第一反应是“烘焙光照那个功能嘛场景里摆好灯点一下 Generate Lighting 等它跑完就行”。这话没毛病但真到了项目中期光照贴图接缝、场景加载发黑、烘焙时间失控、贴图占用内存爆炸……这些坑接踵而至的时候你才会意识到这套流程的细节远比想象中复杂。我这几年经手过好几个不同类型的 Unity 项目从 PC 端的数字孪生展示到移动端的休闲游戏再到 VR 应用每一类项目都绕不开 Lightmap。说句大实话Lightmap 是 Unity 里“看似简单、实则水很深”的系统之一。这篇文章我不打算写成一本官方文档手册而是想把从原理到实操再到底层坑位的完整链路梳理一遍尤其是那些文档里不会写、论坛里翻半天才找到的实战经验。我用的环境是 Unity 2021.3 LTS大部分结论也适用于 2019.4、2020.3 和 Unity 6个别参数名在不同版本里可能有差异大家以编辑器实际显示为准。这次会把选型逻辑、参数计算、过程记录和排障思路都放进来希望能帮你省掉几轮试错。1. 从原理开始Lightmap 到底是什么1.1 一次预计算解决两个核心问题场景里有光源物体表面就会收到光照。实时计算光照是游戏引擎的常规操作有了动态光灯一开一关物体表面的明暗立刻变化适合角色、交互物这种会动的东西。但实时光照有硬伤一个是性能开销另一个是它默认只能算到直接光照这一层要做好间接光照代价非常昂贵。这里说的间接光照就是光线在物体之间弹射之后带来的照明。举例来说一面白墙旁边的红沙发会让墙面蒙上一层淡淡的红色这就是间接光照里的“颜色溢出”。这种细腻的光感靠实时灯光硬打是出不来的除非你堆几十个灯光去近似但那样性能又撑不住。Lightmap 的思路很朴素既然静态物体不会动、灯光也不变那干脆把光照结果提前预计算好烘到一张或几张纹理里。运行时Unity 不需要再为这些静态物体做光照计算只要在渲染时采样这些预计算的纹理就能还原出非常逼真的光照效果。相当于一次性付了计算成本然后在运行期间反复免费使用。1.2 先分清几个高频概念在 Unity 界面上Lightmap 相关选项散落在多个地方新手特别容易搞混。我简单梳理一下Lighting Settings光照总设置面板负责烘焙模式、分辨率、采样数等全局参数。Lightmap Parameters一套烘焙质量参数资产可以给不同物体指定不同的精度。Lightmap 纹理烘焙产出的纹理资源运行时采样它。Lightmap UV也叫 UV2模型除主纹理 UV 外的第二套 UV用于在光照贴图图集里摆放采样区域。打个不准确的比方Lightmap UV 决定了“光照信息贴到模型的哪个位置”Lightmap 纹理决定“每个位置光照长什么样”Lightmap Parameters 则决定“这一次烘焙对某个物体花多少心思去算”。很多人一上来就点 Generate Lighting结果烘焙出来是错的就是这几个概念没理清。尤其 UV2很多模型在建模软件里根本没导出烘焙出来的光照全都糊成一团。1.3 烘焙模式Baked、Realtime 和 Mixed 怎么选Unity 的光照模式分成三种Baked所有静态光照全部预计算运行时零光照计算适合完全不变的环境。Realtime光照实时计算可以任意修改灯光位置和颜色。真正的实时 GI 需要配合 Realtime 灯光和 GI 缓存系统开销不低。Mixed混合模式一部分光照预烘焙一部分光照实时贡献。这是项目里最常见的配置尤其适合“场景基本不变但需要动态物体参与照明”的情况。在 Mixed 模式下还可以选 Directional Mode、Shadowmask 等子选项。Shadowmask 是很多移动端项目的常用选择它允许静态物体的阴影来自烘焙纹理而动态物体与静态场景之间的阴影通过 Shadowmask 纹理实时生成。这个方案兼顾了画面效果与性能缺点是会额外增加纹理内存我在后面内存优化章节再展开。2. 烘焙前的准备四件事没做对烘焙全白费2.1 模型和 UV2 检查烘焙 Lightmap 之前我的习惯是先检查模型资源。模型需要有两套 UV一套是常规的贴图 UVUV0另一套是光照贴图 UVUV1Unity 里叫 Lightmap UV。如果模型没有导出第二套 UV可以在导入设置里勾选 “Generate Lightmap UV”Unity 会基于第一套 UV 自动生成。但自动生成的 Lightmap UV 往往不是最优解。我的经验是重要角色和高精度的展示物体最好在建模软件里手动分好 UV2。自动生成容易出现两个问题一个是 UV 岛数量过多导致烘焙时贴图利用率下降、接缝风险增加另一个是 UV 比例不一致导致同一个物体上光照采样密度天差地别。勾选 “Generate Lightmap UV” 之后下方还有几个参数值得注意Pack Margin图表之间的间距默认是 4。如果烘焙出来的光照接缝特别明显可以适当调大这个值比如 8 或 16。代价是纹理利用率下降。Stretch Padding控制 UV 岛边缘的拉伸预留一般保持默认。Hard Angle大于这个角的相邻面会被拆成独立 UV 岛。硬边越大UV 岛越少但接缝越可能出现在可见的位置。还要养成一个习惯检查模型的面朝向和缩放。模型如果是负数缩放或者法线方向有问题烘焙出来的光照会出现黑色斑块这种问题查起来特别费时间。建议在初始检查时把模型的 “Read/Write” 打开调试一些奇怪的光照问题时更方便。2.2 静态标记与 Contribut GI在 Unity 中参与烘焙的物体必须打上静态标记。选中物体后在 Inspector 右上角勾选 Static 下拉菜单一般我会把不需要物理模拟的场景物件设成 Static或者至少勾选 “Contribute GI”。注意把物体设为 Static 会影响运行时行为例如它不会受到重力、不会被物理引擎移动所以动态物体一定不要勾。这里有个容易踩的坑Static 标记是分类型的工具栏里 Static 下拉菜单里有很多子项比如 Occluder Static、Batching Static、Navigation Static 等。如果你只是想让物体参与光照烘焙必须确保勾选了 Contribute GI或者叫 Lightmap Static而不是只看大 Static 被勾选了。尤其在一些手滑全选的项目里光勾个大 Static 有时并不包含 GI 贡献选项。还有个细节如果某个物体既想参与光照又想保持动态效果比如一扇可开的门你可以把它设置为 Lightmap Static 但保持刚体和其他动态属性这样门移动后光照不会实时更新但门本身可以动。这就是 Mixed 模式的意义。2.3 灯光的颜色和强度烘焙前检查灯光参数也是必备步骤。我的经验是先关掉场景里的 “Auto Generate” 自动生成选项再手动控制灯光强度。灯光强度单位在 Unity 里使用物理单位Lux、Candela 或 Lumen取决于光源类型容易遇到的问题是烘焙结果过曝或过暗。场景里平行光强度默认是 1但如果你在室内用的点光源强度开到几百甚至几千很可能最后烘出来是白茫茫一片。建议烘焙前用一个测试场景先把灯光强度确定下来再进主场景大范围铺灯。还有一个常见误区是直接在上万个点光源的场景里点烘焙这会导致烘焙时间暴涨而且在后续调光阶段浪费时间。我的建议是先只保留方向光和主要氛围灯光完成第一次粗烘焙确认整体光照基调再逐步添加补充灯光。每次增删灯光后重新烘焙的时间比一次性解决所有灯光问题要可控得多。3. 烘焙参数详解理解这些选项你才能调出好光影3.1 分辨率是光影细腻度的第一决定因素在 Lighting 窗口的 Scene 选项卡里第一个需要关注的参数就是 Lightmap Resolution单位是 texels per unit也就是每单位长度能分配多少个光照纹理像素。这个值直接决定了最终光照贴图的细腻度。数值越大光照细节越丰富但内存和烘焙时间也成倍增长。很多教程会告诉你“调成 2 就够了”但实际上这个值取决于项目的画面细腻程度和相机距离。同一面墙在 FPS 游戏里被贴着看2 texels/unit 会显得很糊在一个俯瞰视角的即时策略游戏里这个值可能已经绰绰有余。我自己常用的区间是移动端 1~2PC 端 2~4展示类项目数字孪生、VR 样板间4~8。从 2 提升到 4内存会变成原来的 4 倍左右因为纹理面积按平方增长这个账要算清楚。如果场景很大全局一个分辨率会出现局部浪费大空地和局部不足细节多的区域并存的情况。Unity 支持在 Lighting Settings 里设置多个 Lightmap Parameters并指定给不同对象。这样可以对重点展示区域用高分辨率烘焙对远景大块地面用低分辨率从整体上控制内存和烘焙时间的平衡。3.2 Direct Light、Indirect Light 和压缩开关Lightmap 在烘焙时可选择是否存储直接光、间接光和最终结果。实际项目里我通常的做法是如果场景里有动态物体或者需要做天气切换、昼夜变化建议只烘焙间接光把直接光留给实时灯光。这样动态物体才能与场景自然融合不至于出现“物体像贴纸一样挂在静态背景上”的违和感。Compress Lightmap 这个开关要注意开启后纹理内存大幅减小但某些平台上压缩格式会导致 Lightmap 颜色偏色尤其暗部容易出现色块。PC 端通常问题不大移动端的部分 GPU 对 HDR Lightmap 压缩支持不佳。我的建议是先在编辑器里不压缩烘焙一遍确认色彩再开启压缩测试效果如果偏色严重就只能保留未压缩格式。还有个容易忽略的参数是 Lightmap Padding它控制同一物体在 Lightmap Atlas 里多个 UV 岛之间的间距。运行时做点采样如果 Padding 太小相邻 UV 岛采样会互相污染出现黑边或颜色溢出。默认是 2接缝明显时调到 4 或更高代价是 UV 利用率降低、Atlas 需要更大空间。3.3 Progressive GPU 还是 CPU采样数怎么定Unity 的 Progressive 烘焙器分为 CPU 和 GPU 两种模式。CPU 模式对硬件兼容性好但速度慢GPU 模式在 N 卡上表现很好烘焙速度能快好几倍但显存不足时容易烘焙失败。项目里没有特殊需求的话我会优先用 GPU 模式并把采样数从默认的 64 提高到 128 或 256让间接光的弹射效果更细腻。采样数决定每次光线追踪的密集程度。数值太小光照会出现比较明显的噪声颗粒尤其在漫反射丰富的室内。但从 256 再往上提画质提升就很有限了烘焙时间却线性增加。如果遇到烘焙结果有噪点又不想大幅提高采样数可以试试调节降噪器Denoiser强度在 Lighting 窗口里可以选 Auto 或手动指定滤波器。我这里一般选 Auto能减少渲染噪点又不至于让画面过于模糊。AO环境光遮蔽也是 Lightmap 烘焙中很实用的功能。在 Lightmap Parameters 中勾选 Ambient Occlusion可以生成细腻的接触阴影强调物体缝隙感。但要注意AO 是额外成本而且在高分辨率下更容易暴露噪点如果场景本身光照对比不强AO 效果也不明显就不必开。4. 实操流程从准备到验收的完整步骤光讲参数不落地等于白讲。我把整套流程串起来下面的步骤可以当作一份快速操作清单按顺序做一般不会出问题。4.1 烘焙前最终检查清单把所有要参与烘焙的静态物体标记为 Contribute GI。检查模型导入设置确保 Generate Lightmap UV 为开启或者模型已包含正确的 UV2。检查灯光模式静态灯光用 Baked 或 Mixed动态灯光用 Realtime。在 Lighting Settings 中设置正确的烘焙模式推荐 Progressive GPU 或 CPU、分辨率、采样数和降噪参数。确认材质和贴图路径正常避免烘焙过程中资源导入冲突。最后点 Generate Lighting观察编辑器控制台是否出现报错。烘焙完成后检查纹理目录下新生成的 Lightmap 资源确认尺寸和数量是否符合预期。其中第四步经常被忽略如果灯光是 Realtime 模式但物体是静态的烘焙并不会帮你保存这些灯光对静态物体的贡献。实际效果是烘焙场景里静态物体依然很暗只有动态部分被点亮。4.2 烘焙过程怎么观察烘焙不是点完按钮就完事。Progressive 烘焙会先在编辑器底部弹出一个进度条此时可以在 Lighting 窗口里看到 Baking 字样。大场景烘焙动辄几十分钟这期间 Unity 编辑器会占用大量 CPU/GPU 资源最好别同时跑其他重型操作否则容易导致烘焙中断或编辑器卡死。一个技巧烘焙开始前先检查 Auto Generate 是否关闭然后手动点击 Generate Lighting。如果开着 Auto Generate场景里任何灯光、物体位置、静态标记的变化都会自动触发重新烘焙非常浪费资源尤其多人协作时很容易误触发。我在团队里会直接要求所有人提交前关闭这个选项避免同事改动场景后自己的编辑器默默烘了半天。4.3 烘焙结果怎么验收烘焙完成后并不代表所有光照都正确了。我习惯先在 Scene 视图右上角的下拉菜单里找到 Lightmap 相关渲染模式直接查看 UV2、光照贴图纹理等信息确认物体是否在 Atlas 中正常分配。再依次检查室内暗部有没有明显噪点。大面积墙面上有没有接缝或黑斑。动态角色走到场景中脚下阴影和场景光影是否融合。对比开启和关闭 Lightmap 两种显示确认物体确实读取了光照信息。如果出现接缝多数情况是 UV2 岛间距不够回到导入设置里调整 Pack Margin 或烘焙参数里的 Padding。如果是黑斑检查法线、UV 镜像和缩放是否为负。如果是噪点提高采样数或开 Denoiser。5. 高频问题排查我踩过的坑和解决思路5.1 接缝问题接缝是 Lightmap 最常见的坑尤其在墙体转角、模型 UV 分界处。出现接缝的直接原因是烘焙纹理在 UV 岛边界上的采样不连续。解决办法按顺序尝试调大 Lightmap Parameters 里的 Padding 值。在资源导入设置中调大 Pack Margin。如果接缝出现在硬转角处把 Hard Angle 调小让 Unity 在转角处断开 UV 岛避免贴图拉伸导致接缝。检查模型是否有重叠 UV 或镜像 UV如果有需要手动修复模型。有一个特别容易被忽略的场景同一个模型被复制了很多份且它们共享同一个 Atlas 区域。当模型移动位置后光照信息不会自动更新因为烘焙时每个物体都会被分配到 Atlas 中独立区域。如果你的复制物体有细微差别却共享 UV2烘焙结果大概率会串光。这种情况下建议为每类物体都生成独立 UV2或者用子图集隔离。5.2 漏光问题漏光指的是从两道墙的缝隙、门缝或物体间微小的空隙里透出强光烘焙后阴影显得不真实场景像漏了灯一样。漏光的本质是烘焙时光线追踪到了本不该看到的部分。常见原因包括物体没有 Mesh Collider烘焙器收不到碰撞信息光线穿过了几何体。墙与墙相接处有微小缝隙不管是建模公差还是摆放误差都可能让光线漏进来。使用了一些透明的、双面的材质导致光线穿模。解决办法第一步确认所有墙面、地面、天花板都带有 Mesh Collider 并标记为静态。第二步用自动重建光照 UV 或手动修补模型接缝。第三步如果只是微小缝隙可以在缝隙处补一块极薄的静态挡片。这样即便以后灯光变化缝隙也不会漏光。5.3 烘焙时间过长大场景烘焙几小时是非常痛苦的经历。实际优化思路有几个降低全局采样数和分辨率优先保证重点区域。对场景中的高精度模型适当降低 Lightmap Parameters 里的间接质量。删除场景里多余的高模或使用 LOD减少参与 GI 的几何复杂度。检查模型是否包含大量不在视野中的面片这类面片完全可以删掉。如果使用 GPU 烘焙考虑显存是否够用显存超了性能会断崖式下降。考虑分区块烘焙再在统一空间里合并区块的 Lightmap。这个方法需要小心处理区块边界一般只在场景非常大时才值得做。我项目里做过一次 100 万平方米的室外大场景最终方案是切割成 4 块小区块分别烘焙然后通过自定义工具合并 Lightmap 数据。过程非常麻烦但烘焙时间从 3 小时降到了 40 分钟内存也大幅下降。一般项目不建议一上来就搞这种方案先把材质、模型和分辨率优化好再说。5.4 场景加载后 Lightmap 丢失Lightmap 是场景数据的一部分但执行 Build 时烘焙产物会作为资源被打包进 AssetBundle 或场景。如果场景加载后 Lightmap 丢失最常见的原因是场景里的 Lightmap 设置与当前 Lighting Settings 配置不匹配。Lightmap 数据被单独放进 AssetBundle而加载流程没有正确关联。多个场景之间切换时Lightmap 索引冲突。解决办法检查 Build Settings 中 Scene 列表是否包含所有烘焙过的场景使用 SceneManager.LoadScene 加载场景时确认是 Single 还是 Additive 模式如果跨场景共享 Lightmap建议用 Light Probe 或其他方案代替避免跨场景光照索引错乱。6. 内存与加载性能Lightmap 的显隐成本6.1 纹理内存估算Lightmap 的纹理数量和尺寸直接决定内存开销。假设一个场景分辨率是 2048x2048一张 HDR 纹理在 RGBM 编码下可能占用 16MB 左右如果场景有 20 张贴图那就是 320MB。移动端主流机型的包体和内存上限通常非常紧张所以这个数字很可能直接触发内存告警。我的做法是从项目早期就实时关注 profiler 里 Texture 一项的占比。如果贴图数量过多优先降低全局分辨率并针对不同物体使用不同 Lightmap Parameters。另一个思路是启用压缩格式在许多安卓机型上仍可获得不错的画质内存能省一半以上。6.2 加载方式与流式加载Lightmap 纹理在场景加载时会一次性全部读入显存。对于大地图这会造成明显卡顿。解决办法之一是拆分成多个子场景每个子场景分别烘焙独立 Lightmap在玩家接近时才加载。如果用的是 Addressables 或 AssetBundle可以把 Lightmap 作为独立资源组与场景资源配合加载。还有一点混合光照的 Shadowmask 也会产生额外纹理Shadowmask 和 occlusion mask内存同样需要考虑。如果项目内存高度紧张可以退而求其次用 Distance Shadowmask 模式只在近距离实时更新阴影远距离用烘焙阴影兼顾效果和内存。6.3 它和 Light Probe、反射探针的分工Lightmap 只能作用于静态物体动态物体需要借助 Light Probe 来采光。很多人误以为烘焙之后动态物体就会自动匹配环境光实际上动态物体的间接光照来自 Light Probe 网络而不是 Lightmap。如果 Light Probe 摆得太少角色走进室内时会突然变暗这就是 Probe 插值不足。所以在健康的光照系统里Lightmap 负责静态物体烘焙Light Probe 负责动态物体的间接光采样反射探针负责镜面和光滑物体的反射三者分工明确缺一不可。这也是我检查场景光照时最后一步会做的把 Light Probe 组和反射探针组也加入验收清单确保动态物体在场景中移动时光照过渡自然。7. 最后的实战心得我自己做项目时从来不会把 Lightmap 当成一个“渲完就忘”的过程而是把它当作需要长期维护的数据资产来对待。每次改场景布景旧光照数据必然过时这很正常关键是要建立一个可重复的流程让每次烘焙的输入都可预期。我建议团队在项目初期就定好一套光照规范内容包括静态标记规则、UV2 命名规范、Lightmap Parameters 的命名和组织方式、各区域的默认分辨率、灯光强度规范。这样后来接手的美术或程序不需要反复试错也能保证每次烘焙跑出来的结果在预期范围内。最后再分享一个小技巧很多新手在调光照参数时改一个就重新烘一次效率极低。我的习惯是先用最低分辨率和低采样数做一次“预览烘焙”确认光影构图没问题后再切到正式高参数做最终烘焙。预览烘焙通常只需要一两分钟能省下好几个小时的重复等待。只要把分辨率从 4 降到 1采样数从 256 降到 32预览效果已经能反映大方向上的光影关系了。等构图确定再花时间跑高质量结果心里就有底得多。Lightmap 不是新鲜技术但它依然是 Unity 项目里性价比最高的全局光照方案之一。把原理吃透、参数调顺、坑位摸清至少能让你在这个环节上少浪费几十个小时的重复劳动。
返回列表