烘焙光照贴图

前言

烘焙的光照贴图也是一种为场景添加间接光照(也叫全烘焙光照)的工作流程。与 使用 GIProbe 的方法不同,烘焙光照贴图在低端PC和移动设备上运行良好,因为在运行时几乎不消耗资源。与 GIProbe 的另一个不同点是,烘焙光照贴图还能够用来保存直接光照,可以进一步提升性能。

烘焙光照贴图与 GIProbe 不同,是完全静态的,一旦被烘焙就完全不能被修改。它也不能为场景提供反射,所以如果要达到较好的画质,在室内场景(或者是使用 Sky 的室外场景)中就需要和 反射探针 搭配使用。

因为是烘焙出来的,所以在光线渗透方面的问题就比 GIProbe 少很多,并且间接光照也会看上去更漂亮。烘焙光照贴图的缺点是烘焙所需的时间比 GIProbe 长很多,GIProbe 几秒钟就能搞定的烘焙,换成烘焙光照贴图就可能至少得花上几分钟。这会严重拖慢迭代速度,所以推荐只在确实有查看光照变化的需求时进行光照贴图的烘焙。

烘焙光照贴图还会占用被烘焙材质的 UV2 栏位,也就是说你无法再把 UV2 用于该材质的其它用途(无论是内置的 空间材质 还是自定义着色器)。

最后,决定哪种间接光照方式更好取决于你的用途。一般来说,GIProbe 用起来更方便并且能够更好地应用到动态对象。但如果需要兼容移动设备和低端设备,你只能选择烘焙光照贴图。

视觉比较

以下是 BakedLightmap 和 GIProbe 的一些显示效果比较。可以看到光照贴图更精确,但由于使用的是展开后的纹理,所以过渡以及分辨率可能就没有那么理想。GIProbe 看上去没有那么精确(因为是近似估算),但总体上更平滑。

../../_images/baked_light_comparison.png

设置

首先,在光照贴图器可以执行任何操作之前,要烘焙的对象需要 UV2 图层和纹理大小。UV2 图层是一组辅助纹理坐标,可确保对象中的任何面在 UV 贴图中都有自己的位置。面与面之间不得共享纹理中的像素。

这里有几种方法可以确保您的对象具有唯一的 UV2 层和纹理大小:

场景导入时展开

总体来说,这可能是最好的方法。唯一的缺点是,在大型模型上,导入时展开可能需要一段时间。不过Godot会在重新导入时缓存UV2,所以只会在需要时重新生成。

在文件系统面板中选择被导入的场景,然后切换到导入面板。这里可以修改以下选项:

../../_images/baked_light_import.png

需要把 Light Baking 光照烘焙模式设置为 Gen Lightmaps,同时还必须提供世界单位的纹素大小,因为这将确定光照贴图纹理的最终大小(由此确定地图中的 UV 填充)。

设置此选项的效果是场景中的所有网格都将正确生成其UV2贴图.

警告

如果在场景中复用了网格,请注意生成 UV 时只会使用第一个找到的实例。如果复用时使用了不同的缩放比例(并且相差很大,超过了一半或者两倍),会导致生成低效的光照贴图。如果你想用光照贴图,就不要在复用原始网格时使用明显不同的缩放比例。

另外,请不要在版本控制系统中忽略 *.unwrap_cache 文件,这些文件可以用来保证不同平台、不同版本的引擎在重新导入 UV2 时的一致性。

使用 Godot 进行展开

Godot可以选择打开网格并可视化UV通道. 它可以在Mesh菜单中找到:

../../_images/baked_light_mesh_menu.png

这将生成第二组UV2坐标, 可用于烘焙, 并且还将自动设置纹理大小.

使用 3D DCC 展开

最后一种方法是在你喜欢的 3D 应用程序中进行操作。通常不推荐这种做法,但为了让你知道它的存在,这里还是解释一下。这种做法的主要优势在于,针对可能要经常重新导入的复杂对象,在 Godot 中进行纹理生成的代价可能相当高,所以在导入前展开可以提高速度。

只需在第二个UV2层上进行展开即可。

../../_images/baked_light_blender.png

然后正常导入 3D 场景。记得在导入后为网格设置纹理大小。

../../_images/baked_light_lmsize.png

如果在导入时使用外部网格, 则将保留大小. 请注意,3D DCC中的大多数解包器都不是面向质量的, 因为它们可以快速工作. 您通常需要使用接缝或其他技术来创建更好的展开.

检查 UV2

在前面提到的网格菜单中, 可以显示UV2纹理坐标. 如果出现问题, 请确保检查网格是否具有以下UV2坐标:

../../_images/baked_light_uvchannel.png

设置场景

首先需要在场景中添加一个 BakedLightmap 节点。添加后,场景中的所有节点(和子节点)就都可以进行光照烘焙了,甚至实例化的场景也可以。

../../_images/baked_light_scene.png

烘焙器支持同一子场景存在多个实例,它们会有各自独立的光照贴图(前提是你得遵守之前提过的关于缩放的规则):

配置边界

光线贴图(Lightmap)需要一个受影响区域的近似体积, 因为它将光线传递给在该体积内部的动态对象(稍后再谈). 就像使用 GIProbe 一样, 用体积覆盖场景:

../../_images/baked_light_bounds.png

设置网格

如果需要让 MeshInstance 节点参与烘焙,需要启用 Use in Baked Light 属性。

../../_images/baked_light_use.png

在场景导入时自动生成光照贴图时, 会自动启用此功能.

设置灯光

默认情况下, 灯光采用间接灯光烘焙. 这意味着阴影贴图和光照仍然是动态的并影响移动的物体, 但是光线反射的光将被烘焙.

灯可以禁用(不烘焙)或完全烘焙(直接和间接). 这可以通过灯光中的 烘焙模式 菜单进行控制:

../../_images/baked_light_bake_mode.png

模式有:

Disabled(禁用)

烘焙光照贴图时忽略灯光。注意烘焙时隐藏灯光是无效的,必须在这里设置禁用才能达到隐藏灯光节点的效果。

这个模式可以用于动态光照效果,例如爆炸和武器特效。

Indirect(间接)

这是默认的模式,是性能与实时友好性的折衷。只会烘焙间接光照。直接灯光和阴影仍旧是实时的,与不使用 BakedLightmap 时一致。

这个模式可以在保持显示效果相对正确的同时,允许进行灯光颜色、能量、以及位置的 微调 。例如,你可以借此实现静态火把的闪烁,它的间接光照仍然是烘焙的。

All(全部)

间接和直接光照都会被烘焙。因为静态表面可以完全跳过光照和阴影的计算,所以这个模式可以提供最佳的性能,并且实现不随距离衰减的平滑的阴影。实时灯光不会再影响烘焙的表面,但仍然可以影响动态的对象。在光源上使用 All (全部)烘焙模式时,动态对象不会在烘焙表面上投射阴影,所以你需要使用别的方法,比如软阴影。软阴影既可以通过 Sprite3D + RayCast 实现,也可以通过把朝下的反 SpotLight 的烘焙模式设置成 禁用 实现。

游戏过程中无法调整灯光。灯光的移动、变色、能量调整不会对静态表面产生影响。

因为烘焙模式可以分光源调整,所以可以设置出混合的烘焙光照。一个比较流行的做法是使用实时 DirectionalLight 并把它的烘焙模式设置成 Indirect,然后把 OmniLight 和 SpotLight 的烘焙模式都设置成 All。这样做的性能不错,同时也允许动态对象在室外投射实时阴影。

选中灯光的 All 烘焙选项之后,你还可以在检查器中将灯光的 Size(大小)设置为大于 0 的值。这个大小值可以用来根据投射阴影与接受阴影物体之间的距离来提供更柔和的阴影。这样就可以模拟现实生活中影子的样子:

../../_images/baked_light_omnilight_size.png

灯光的 Size 属性会被实时阴影忽略;只有烘焙阴影会受其影响。改变 Size 后,必须重新生成光照贴图才能让修改效果显现出来。

烘焙

开始烘焙只需在选中 BakedLightmap 节点后点击上方的 烘焙光照贴图 按钮:

../../_images/baked_light_bake.png

根据场景大小、所选烘焙方法以及质量的不同,其过程可能花费几秒钟到几分钟不等(也可能是几小时)。

平衡烘焙时间和质量

因为高质量的烘焙可能花费非常长的时间(大型复杂场景可能需要若干小时),推荐首先设置成较低质量,将场景中的灯光布置成满意的效果后再改成较高的质量,在导出项目前进行“终极”烘焙。

备注

默认情况下,光照贴图烘焙器会使用系统中所有逻辑 CPU 核心来加速烘焙,可能会降低系统的响应能力。要在烘焙光照贴图时保留系统的响应能力,你可以减少用于烘焙光照贴图的 CPU 线程数。保留 1 到 2 个 CPU 线程可以帮助提高系统响应能力,有利于在烘焙光照贴图时进行多任务,但也会稍微降低光照贴图的烘焙速度。

实现方法是,打开编辑器 -> 编辑器设置*并调整 **Editors > 3d > Lightmap Baking Number Of Cpu Threads(编辑器 > 3D > 光照贴图烘焙 CPU 线程数)。默认值(0)会使用所有的系统逻辑 CPU 核心。正值可以指定所使用的线程数,负值则是从系统逻辑 CPU 核心的总数中去减。例如,在拥有 8 个逻辑 CPU 核心的系统上,将其设置为 -1 会在烘焙光照贴图时使用 7 个 CPU 线程。

配置烘焙

烘焙还有几个选项:

  • Bake Extents(烘焙界限) :受影响的区域大小,也可以使用 3D 编辑器视图中的手柄进行编辑。所有支持烘焙光照贴图并且与烘焙界限 有交集 的对象都会被烘焙上光照贴图,但动态对象捕获也只会在界限之中进行。

Tweaks(调整)

  • Quality(质量):提供了四种烘焙质量:Low(低级)、Medium(中级)、High(高级)、Ultra(超级)。质量越高所需的时间越长,但最终光照贴图的显示效果越好、噪点也越少。针对发光材质或者几乎没有直接光照的地方,不同质量之间的区别尤为显著。

  • Bounces(反弹) :间接光照的反弹次数。默认值(3)是烘焙时间和质量之间的一个平衡点。取值越高,光线在停止之前反弹的次数越多,间接光照的效果也就越好(同时也越亮)。在做最初的光照迭代工作时,建议把反弹次数减小到 1,这样可以加快烘焙速度。注意降低反弹次数会让场景变暗。

  • Use Denoiser(使用降噪器) :启用时,会使用 OpenImageDenoise 显著降低光照贴图中的噪点。在增加烘焙时间的同时有时也会引入伪影,不过获得的效果经常是物有所值的。

  • Use Hdr(使用 HDR) :禁用时,光照贴图会占用更少的磁盘空间,但就会无法捕捉过亮(大于 1.0)的灯光。如果场景中存在较亮的光源,就会造成可见的边界。禁用 HDR 后光照贴图中也有可能出现条状区域。

  • Use Color(使用彩色) :禁用时,光照贴图会占用更少的磁盘空间,但就会无法保存彩色灯光。仅烘焙间接光照时,由于间接灯光通常对比度不高,所以可能几乎看不出区别。然而使用 All 烘焙模式同时烘焙直接和间接光照时,就会导致彩色灯光变成灰度灯光。该选项和 HDR 一起禁用后可以得到相同分辨率下最小的光照贴图。

  • Bias(偏置) :阴影的偏移量,使用 3D 单位。除非你遇到了光线渗透问题或者光照贴图在烘焙后存在暗区,否则通常情况下不需要修改这个值。该选项不会影响投射在烘焙表面上的实时阴影。

  • Default Texels Per Unit(默认单位纹素数):未指定光照贴图纹素密度的网格会使用此值。取值越高,光照贴图的分辨率越低,使用模糊的间接光影换取更快的烘焙时间以及更小的文件大小。

Atlas(图谱)

  • Generate(生成) :启用时,会为光照贴图生成图谱纹理,可以让渲染更高效,但仅适用于 GLES3 渲染器。所以如果你的项目允许退回到 GLES2(默认未启用,只能在项目设置中手动启用),请禁用此选项。 如果项目使用 GLES2,那么该选项默认会被忽略。

  • Max Size(最大尺寸) :图谱的最大尺寸,单位为像素。取值越高,图谱效率越高,但也就越不兼容较旧或者低端的硬件。如果没有把握,就请使用该选项的默认值(4096)。

Capture(捕获)

  • Enabled(启用) :启用探针捕获,使得动态对象能够 接受 间接光照。无论此选项是否启用,动态对象都无法对场景 造成 间接光照,这是光照贴图的局限性。

  • Cell Size(单元格大小) :光照贴图探针的间距,使用 3D 单位。取值越高,探针布置地越稀疏,通过牺牲动态对象光照的精确度带来烘焙时间的缩短以及文件大小的减小。

  • Quality(质量) :光照贴图探针的生成质量。取值越高,光照越精确,烘焙时间也越长。此选项不会影响光照贴图探针的 密度 ,只影响质量。

  • Propagation(传播) :类似于 GIProbe 的 Propagation 属性。取值越高,动态对象间接光照就越亮、漫反射越强。根据场景的实际情况调整此选项,可以让动态对象更好地融入静态的烘焙光照。

数据

  • Light Data(光照数据) :烘焙后包含光照烘焙数据。纹理会被保存到磁盘上,但这里还会包含动态对象的捕获数据,数据量可能非常大。如果你使用的是 .tscn 格式的场景,应该将此资源保存成外部的二进制 .lmbake 文件,否则 .tscn 场景可能因为使用 Base64 编码二进制数据而变得巨大。

光照数据资源里有两个额外的属性可供编辑:

  • Energy(能量) :调整光照贴图的亮度。取值越高,光照贴图越亮。此选项可以在运行时调整,用来实现类似雷暴的短期的动态效果。不过请注意,修改会影响 所有 被烘焙的灯光。

  • Interior(室内) :启用时,动态对象不会使用环境的光照设置,只会使用光照探针作为环境光。禁用时,环境的光照设置和光照探针会同时作用于动态对象的光照。

小技巧

如果有后期处理的需要,可以使用图像编辑器查看并编辑所生成的 EXR 文件。不过请注意,重新烘焙贴图会覆盖你对 EXR 文件的修改。

动态对象

在其它引擎以及光照贴图器的实现中,一般会需要你在关卡中手动放置一些叫做“光照探针”的细小对象,用以生成捕获数据。然后就会借此将光照传递给在场景中到处移动的动态对象。

但是,本光照映射器的实现使用了一种不同的方法,其过程是自动的,不需要你做任何事情,只要移动物体就能得到对应的光照。当然,你还是需要确保相应地设置场景边界,否则会将无法正常工作。

../../_images/baked_light_indirect.gif