光照贴图和全局照明

简介

注: 截至 Qt 6.4,光贴图烘焙还处于早期技术预览状态。在未来的版本中,功能、质量和 API 很可能会发生变化。

烘焙光照贴图允许预先生成来自DirectionalLightPointLightSpotLight 等光源的直接光照,包括光源投射的阴影。在运行时,无需在片段着色器中执行适当的计算,如果是阴影,也无需实时生成代价可能很高的阴影贴图,而是对预先生成的图像贴图进行采样。

每个Model 会生成一个光照贴图。即使Model 有多个子模式,并因此与多个材质相关联,也会为整个模型生成一个光照贴图。

光线贴图是使用光线追踪技术生成的,它本质上提供了适当的遮蔽("光线不会穿墙"),而且可能比实时照明和阴影贴图技术生成的阴影更逼真。

更重要的是,光线贴图还可以烘焙间接照明,为全局照明提供解决方案。这将场景中其他表面反射的光线考虑在内。

下面是一个简单的例子。场景包含四个矩形(Rectangle)和一个球形(Sphere)模型,其中DirectionalLight 指向下方,PointLight 。矩形模型旋转了 0 度和 90 度,这夸大了实时照明计算的局限性,因为它们都与DirectionalLight 的方向平行或垂直。

在第二张图片中,在对所有五个模型进行光贴图烘焙后,启用光贴图对场景进行渲染。两个灯光都设置为完全烘焙,这意味着直接和间接照明都已烘焙。间接照明使用 256samples 和最大 3bounces 。然后对生成的光贴图进行去噪处理。这样得到的图像更加逼真。

实时照明

包含球体、矩形和两盏灯的简单场景

完全烘焙照明

同样的场景,两盏灯都设置为完全烘烤状态

下面的片段展示了如何实现光贴图效果。不同之处在于usedInBakedLightingbakeModebakedLightmap 属性。在本示例中,使用texelsPerUnit 属性缩小了光贴图的大小,以节省磁盘空间并缩短应用程序的加载时间。

DirectionalLight {
    bakeMode: Light.BakeModeAll

    eulerRotation.x: -90
    brightness: 0.5
    castsShadow: true
    shadowFactor: 75
}
PointLight {
    bakeMode: Light.BakeModeAll

    y: 200
    z: 100
    color: "#d9c62b"
    castsShadow: true
    shadowFactor: 75
}
Model {
    usedInBakedLighting: true
    bakedLightmap: BakedLightmap {
        enabled: true
        key: "sphere1"
    }

    source: "#Sphere"
    materials: PrincipledMaterial { }
    y: 100
}
Model {
    usedInBakedLighting: true
    bakedLightmap: BakedLightmap {
        enabled: true
        key: "rect1"
    }

    source: "#Rectangle"
    materials: PrincipledMaterial { }
    eulerRotation.x: -90
    scale: Qt.vector3d(10, 10, 10)
}

// ... three additional Rectangle models, with rotations 0, 90, and -90

上述示例使用了完全烘焙的灯光。也可以将灯光配置为仅使用烘焙灯光进行间接照明,同时实时执行直接照明和阴影贴图。下面的场景中有 5 个点光源,在第二张截图中,所有点光源都设置为BakeModeIndirect 。虽然直接照明和阴影看起来完全相同,但由于添加了一定程度的全局照明,第二张图片看起来要好得多。

实时照明

使用 Sponza 和 Suzanne 模型以及 5 点照明的场景

添加了烘焙间接光照

同样的场景,采用烘焙间接照明,但采用实时直接照明

使用灯光贴图时的重要注意事项

对烘焙照明有贡献的灯光的bakeMode 属性设置为Light.BakeModeIndirectLight.BakeModeAll 。后者表示该特定灯光的直接和间接贡献都来自光照贴图。直接贡献总是包括阴影。另一方面,如果使用光照贴图的目的只是为了给场景中的特定灯光添加间接照明,同时还能实时计算直接照明(并执行阴影贴图),那么该灯光应使用Light.BakeModeIndirect

注: 一般来说,灯光贴图适用于变换、几何体和材质都是静态的模型。这同样适用于参与烘焙照明的灯光。

例如,如果一个场景通过动画改变eulerRotation 属性来旋转Model ,那么当将光贴图应用到该Model 时,视觉效果将是不正确的。该特定Model 的渲染结果将是不正确的,因为预生成的光贴图只能捕捉对象的一个单一旋转状态。再举个例子,如果模型的一个子模型的材质根据时间(动画)或用户交互动态改变其baseColor 属性,情况也是一样。光线贴图只能捕捉一个给定的材质baseColor 。灯光也是如此。例如,随时间旋转、改变亮度和颜色等的DirectionalLight 不适合用于烘焙照明。

注: 另一方面,何时使用灯光贴图始终是设计者的选择。尤其是BakeModeIndirect 灯光,很可能会出现这样的场景:即使灯光映射场景中的某些对象采用了动态行为,但视觉效果仍然令人满意。

光线贴图是一个复杂的引擎和工具功能。它取代并重新实现了引擎渲染管道的多个部分。在制作光贴图时,它使用的是一种根本不同的渲染模型,同时仍使用相同的场景结构、资产数据和引擎数据结构并与之互操作。基于光线追踪的效果通常会优于实时效果,有时甚至会大幅优于实时效果,但这是以各种限制为代价的,例如模型和灯光必须是静态的,有时还会出现光贴图特有的质量和渲染伪影问题。

在实践中,使用哪种类型的灯光以及何时使用都是设计师的艺术选择。这三种bakeMode 设置都有各自的用途,复杂的大型场景很可能同时使用这三种不同的灯光,具体取决于场景的特定部分适合使用哪种灯光,以及存在哪种模型、材质和动态行为。光贴图并不是一种可以在任何场景和应用中启用的简单开关,而是一种强大的功能,它需要对给定场景的照明需求进行仔细评估,通常需要对场景内容和行为进行相应设计,并结合测试和调整循环,在决定最终方法和相关设置之前,对不同的光贴图烘焙和质量设置进行探索和测试。

注意: 光贴图不支持双面表面。Material.NoCulling 在实时光照下,cull mode 的材质会根据片段的朝向自动反转法线。光贴图不支持这种做法,因为光贴图的烘焙不在视图空间中进行。因此,对于需要使用烘焙光照的模型,应避免使用烘焙光照。

烘焙光贴图

与烘焙光照贴图相关的属性和类型,即生成图像贴图的离线过程,图像贴图可捕捉直接和间接光照,并可在应用程序的后续运行中供渲染器使用:

从 Qt 6.4 开始,光照贴图烘焙过程必须手动触发。只要命令行参数--bake-lightmaps 出现,或环境变量QT_QUICK3D_BAKE_LIGHTMAPS 设置为1 (或其他非零值),引擎就会以烘焙模式工作,并在烘焙完成后退出应用程序。通过查看调试输出中打印的信息,可以跟踪烘焙过程的各个步骤。结果是一个写入当前工作目录的二进制文件(默认为lightmaps.bin ),其中包含场景中所有已烘焙的光贴图。此外,还将创建一个.raw 文件,其中包含整个光贴图和一些去噪所需的额外数据。文件中的每个光贴图都由来自BakedLightmap::key 的唯一密钥标识。

准备光贴图场景的主要步骤如下:

  • 确定哪些模型应使用光贴图,哪些模型应为光贴图做出贡献。作为光贴图场景一部分的模型应将Model::usedInBakedLighting 设置为 true。被光贴图的模型(即要为其烘焙光贴图)还应将Model::bakedLightmap 设置为启用的BakedLightmap 对象,该对象提供一个唯一的键,用于持久标识特定的模型对象实例(这是因为 Qt 需要一个键来标识持久磁盘存储中的模型数据)。只有具有静态几何图形、变换和材料的模型才能保证在运行时光绘时获得正确的结果。最常见的情况是,任何会导致世界随时间发生非静态变换的东西,如动态变化或动画的位置、旋转或缩放,都会使模型失去参与的资格。不过,艺术上的需要可以超越这一点,特别是对于那些只对烘焙间接光照有贡献但本身没有光映射的模型。对于这些模型,通常在视觉上可以接受动态变换,但这始终取决于模型和相关场景。
  • Light::bakeMode 提供了三个选项:
    • Light.BakeModeDisabled(已禁用烘焙模式),这是默认设置,可有效忽略所有光源贴图。
    • Light.BakeModeIndirect(间接烘焙模式)通常是 "安全 "的选择,如果唯一的目的是在场景中设置一定程度的全局照明(间接照明),同时又不影响灯光的其他渲染结果。在此模式下,渲染器将继续使用标准的实时技术为该灯光执行所有照明,包括漫反射、镜面反射、天空/环境光照和阴影贴图。不过,该灯光将使用预烘焙数据进行间接照明,可能会照亮标准实时照明计算未触及的表面。
    • 灯光烘焙模式(Light.BakeModeAll)是一个选项,根据设计者对特定场景的评估,可能只用于某些灯光。在该模式下,灯光的所有贡献都会被烘焙,包括阴影。从 Qt 6.4 开始,烘焙照明不支持镜面反射照明,因此此类灯光不会产生镜面反射效果。另一方面,它们会生成光线跟踪的烘焙阴影,并为光线提供适当的遮蔽(例如,不会 "穿过墙壁"),因为光线产生的所有直接照明贡献都是在光线贴图烘焙时进行光线跟踪的,而不是在运行时计算的。此外,间接光照也会像 BakeModeIndirect(间接烘焙模式)一样进行烘焙。
  • 在烘焙模式下运行场景(应用程序),确保成功生成光贴图。从 Qt 6.4 开始,应用程序的结构应使光照贴图场景成为显示的第一个视图,或可使用 Qt QML Viewer(如qml 工具)加载相关场景。烘烤完成后,应用程序退出,其进度可通过控制台/调试输出跟踪。
  • 正常运行场景(应用程序),查看加载光贴图后的效果。然后就可以开始调整了:
    • 对于某些模型,将texelsPerUnit 从默认值减小到更小是有意义的。这尤其适用于内置原型和任何几何形状足够简单的模型。这将使光照贴图更小,烘焙时间更快。第一次烘焙时,默认值就足够了,之后可以对值进行调整。
    • 光贴图对象提供了许多具有合理默认值的设置,但其中有些设置需要调整以符合设计者的期望,这也不是不可能的。例如,可以更改samplesbounces 来影响间接照明的质量,而indirectLightFactor 则可以使间接照明更加突出。如果出现伪影,尤其是阴影周围的伪影,bias 可以进行微调。
    • 对生成的光照图进行去噪处理至关重要。间接照明是通过路径追踪来计算的,而路径追踪会产生噪声图像,这取决于所使用的samples 的数量。增加样本数可以减少噪点,但会增加生成光照图所需的时间。无论采样数多少,在生成的光照贴图上运行去噪器几乎总是有意义的,光照贴图是存储在二进制文件中的 32 位 RGBA 浮点图像。

从 Qt 6.5 开始,运行时解决方案可通过DebugView 交互式提供。现在,在 "工具"(Tools)下有一个按钮,按下后将触发烘焙过程。这时会弹出一个窗口,显示当前进程。点击取消按钮或关闭窗口即可取消。完成后,它将把光绘二进制文件写入当前目录。

去噪

下面是一个康奈尔方块场景的示例,首先使用 256samples 和最大 3bounces 的光照贴图进行渲染。在第二个示例中,生成的图像文件经过去噪处理,效果明显好转,噪点基本消失。

原始图像

使用一个点光源的 Cornell Box 场景,全烘焙光贴图

去噪

经过光贴图去噪处理的康奈尔盒场景

每个烘焙的光贴图都会自动进行去噪处理。如果工作目录中存在现有的烘焙.raw 光贴图文件,则可以只进行去噪处理,方法是点击DebugView 中的Denoise 按钮。也可以使用--denoise-lightmaps 参数调用应用程序进行去噪处理。要调整去噪的强度,可以使用denoiseSigma 属性。

光贴图 UV

光贴图 UV 坐标与普通纹理使用的 UV 数据不同。使用光贴图进行渲染时,渲染器在对光贴图进行采样时不会使用 UV0 或 UV1 数据。相反,网格中有一个额外的专用 UV 通道,其中包含以适合光贴图目的的方式布局的 UV 图表。这就需要避免重叠,并在适当的地方进行填充。对于常规 UV 数据,则没有这样的要求,而且我们很可能希望对多个顶点使用相同的 U 和 V 坐标。

生成合适 UV 集的过程称为光贴图 UV 展开。Qt 会在烘焙光贴图时执行此操作,并将生成的网格存储在光贴图文件中,以便始终加载兼容的网格并用于生成的光贴图。这意味着,如果模型总是使用烘焙光照,那么源网格文件就不需要随应用程序一起提供。

光贴图纹理大小

对于每个模型,包括其所有子网格,光贴图烘焙过程将在光贴图 UV 生成阶段确定合适的光贴图纹理尺寸。这对质量、性能和资源使用(包括磁盘和内存)都有影响。

默认值通常是合适的,无需调整,特别是对于中高复杂度的模型。

不过,对于非常简单的模型,手动减小尺寸可能是可取的,因为较小的光贴图尺寸仍然可以提供视觉效果良好的结果,而减小资产(光贴图图像)的尺寸则可以节省磁盘空间和内存。为此,请将texelsPerUnit 设置为一个适当的小数。然后,实际的光贴图宽度和高度会根据模型的大小和几何形状,尽量接近像素密度,以便与texelsPerUnit 匹配。

在更改数值时,应重新绘制光贴图并目测结果,以评估更改光贴图尺寸后的效果。

在运行时使用光贴图

在运行时使用预制光贴图的相关属性和类型:

烘焙成功完成后,正常运行应用程序(不使用命令行参数或环境变量设置)就能获取生成的光贴图图像并正确渲染,而这在光贴图烘焙之前是不可能实现的。如果需要,应用程序可将光贴图放在不同的位置,或通过 Qt 资源系统将其作为可执行文件的一部分发送。这可以通过source 属性启用。

以上面的球体和四个矩形为例,烘焙过程会生成一个lightmaps.bin 文件,其中包含所有烘焙过的网格和光贴图。应用程序需要将该文件发送到source 指定的位置,以便引擎可以找到它。

另请参阅 Qt Quick 3D - 已烘焙光贴图示例

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.