光照贴图和全局光照

简介

烘焙光照贴图允许预先生成来自类似于DirectionalLightPointLightSpotLight的灯的直接光照,包括灯产生的阴影。在运行时,而不是在片段着色器中执行适当的计算,在阴影的情况下,在实时中生成可能代价高昂的阴影贴图,而是采样预先生成的图像贴图。

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

每个模型生成一个光照贴图。即使模型具有多个次级网格,因此与多个材料相关联,也会为整个模型生成一个单一的光照贴图图像。

光照贴图使用光线追踪生成,光线追踪本质上是提供正确的遮挡("光不会穿过墙壁"),并且可能比实时光照和阴影映射技术生成更真实的阴影。

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

以下是一个简单的示例。场景包含四个矩形和一个球体模型,一个指向下方的PointLight和方向灯。矩形模型旋转了0度和90度,这夸大了实时光照计算的局限性,因为它们都与DirectionalLight的方向平行或垂直。

在右边,场景启用了光照贴图渲染,为所有五个模型进行了光照贴图烘焙。两个灯光都设置为完全烘焙,这意味着直接和间接光照都进行了烘焙。间接光照使用了256个样本和最多3个反弹次数。然后对生成的光照贴图进行了降噪处理。这给出了一幅明显更真实的图像。

实时光照

"Simple scene with sphere, rectangles, and two lights"

完全烘焙光照

"The same scene with both lights set to fully baked"

以下代码片段显示如何实现光照贴图的结果。区别在于usedInBakedLightingbakeModebakedLightmap属性。为此示例,使用了lightmapBaseResolution属性减少了光照贴图的大小,以节省磁盘空间并减少应用程序加载时间。

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
    lightmapBaseResolution: 256
    bakedLightmap: BakedLightmap {
        enabled: true
        key: "sphere1"
    }

    source: "#Sphere"
    materials: PrincipledMaterial { }
    y: 100
}
Model {
    usedInBakedLighting: true
    lightmapBaseResolution: 256
    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进行正确的截图。虽然直接光照和阴影看起来相同,但由于增加了程度的环境光照,右边的图像看起来明显更好。

实时光照

"Scene with Sponza and Suzanne models and 5 point lights"

添加烘焙间接照明后的效果

"Same scene with baked indirect but real-time direct lighting"

处理光照图时的重要注意事项

对烘焙照明有贡献的灯光,其bakeMode属性设置为BakeModeIndirect或BakeModeAll。后者表示该特定灯光的直接和间接贡献都来自光照图。直接贡献始终包括阴影。另一方面,如果光照图的意图仅是为了为特定灯光添加间接照明到场景,同时仍然在实时计算直接照明(并执行阴影映射),则灯光应使用BakeModeIndirect。

注意:一般来说,光照图适用于在变换、几何和材质方面都是静态的模型。参与烘焙照明的灯光也是如此。

例如,通过动画eulerRotation属性来旋转模型的场景,当将光照图应用到该模型上时,将给出视觉上错误的结果。该特定模型的渲染结果将是错误的,因为预先生成的光照图只捕捉到对象的单一旋转状态。同样的情况,如果一个模型子网格的材质(基于时间(动画)或某些用户交互)会动态地改变其基本颜色属性,结果也是如此。光照图只能捕捉到一个特定的材质颜色。这对灯光也是如此。例如,一个随着时间的推移旋转、改变其亮度、颜色等的方向性照明不适合烘焙照明。

注意:另一方面,何时使用光照图总是设计师的选择。特别是在使用BakeModeIndirect灯光时,即使光照图场景中的某些对象采用了动态行为,结果仍可能是视觉上令人满意的。

光照图是一个复杂的引擎和工具特性。它取代并重新实现了引擎渲染管道的几个部分。在烘焙光照图时,它使用了一个基本的不同的渲染模型,同时仍然消耗和操作相同的场景结构、资产数据和引擎数据结构。基于光线追踪的结果通常优于实时替代方案,有时幅度很大,但这是以模型和灯光必须静态的限制,以及有时特定于光照图的质量和渲染伪影问题为代价的。

在实践中,设计师将通过艺术选择来确定使用何种类型的光照,以及何时使用。所有三个 bakeMode 设置都有它们的作用,复杂的大型场景可能非常有效地利用所有三个不同的光源,具体取决于哪些是场景某个部分的合适选择,以及存在哪些类型的模型、材质和动态行为。光照贴图不是一种简单的开启/关闭类型切换开关,可以为任何场景和应用程序启用,而是一种强大功能,它假定对特定场景的照明需求进行仔细评估,并且通常需要相应地设计场景内容和行为,并结合测试和调整循环,在确定最终方法和相关设置之前探索和测试不同的光照贴图烘焙和质量设置。

注意:光照贴图不支持两面对称的表面。在实时光照中,具有 cull modeMaterial.NoCulling 的材质会根据片段的面向自动颠倒法线。这意味着光照贴图不支持此选项,因为光照贴图烘焙不操作在视图空间中。因此,避免对于将依赖于此的模型应用烘焙光照。

烘焙光照贴图

与烘焙光照贴图相关的属性和类型,即意味着捕获直接和间接光照的图像生成过程,可以在应用程序的后续运行中由渲染器使用

自 Qt 6.4 版本起,光照贴图烘焙过程需要手动触发。只要存在命令行参数 --bake-lightmaps,或者环境变量 QT_QUICK3D_BAKE_LIGHTMAPS 设置为 1(或非零值),引擎将进入烘焙模式,并在烘焙完成后退出应用程序。可以通过检查调试输出中的消息跟踪烘焙过程的步骤。结果是在当前目录中生成了一个 .exr 文件集,其中每个文件名都有 qlm_ 前缀,后跟 BakedLightmap::key 的唯一键。

准备一个光照贴图场景需要进行以下主要步骤

  • 确定哪些模型应该使用光照贴图,以及哪些模型应该贡献光照贴图。应该是光照贴图场景中的一部分的模型应将 Model::usedInBakedLighting 设置为 true。此外,应该将光照贴图模型(即,将对它们进行烘焙的光照贴图)的 Model::bakedLightmap 设置为启用的 BakedLightmap 对象,它提供了一个唯一键,将该特定的 Model 对象实例持久性识别出来。(这是因为 Qt 需要一个键来识别持久性磁盘存储中的模型数据)只有具有静态几何、变换和材质的模型在运行时灯光贴图时才能保证获得正确的结果。通常,任何导致非静态世界变换随着时间的推移(例如,动态改变或动画的位置、旋转或缩放)的都将使模型不符合参与资格。然而,艺术需求可以替代这一规定,特别是在只有为烘焙直接光照做出贡献而不自身光照贴图的模型中。对于这些,动态变换在视觉效果上通常是可以接受的,但这始终取决于模型和场景。
  • 确定哪些灯光应该贡献,以及贡献的程度。 Light::bakeMode 提供三种选项
    • Light.BakeModeDisabled,默认选项,将会有效忽略灯光在所有光照贴图场景中的用途。
    • Light.BakeModeIndirect通常是“安全”的选择,如果唯一目标是实现场景中一定程度的全局照明(间接光照),而不会以其他方式影响光照渲染结果。在此模式下,渲染器将继续执行所有光照,包括漫反射、镜面、天空/环境贡献以及该灯光的阴影贴图,使用标准实时技术。然而,该灯光将通过预烘焙数据间接贡献光照,可能会照亮那些通常不会被标准实时光照计算影响的表面。
    • Light.BakeModeAll是一种可能只适用于特定灯光的选项,基于设计师对给定场景适当性的评估。在此模式下,所有来自灯光的贡献都将烘焙,包括阴影。截至Qt 6.4,不会有镜面光照作为烘焙光照的一部分被支持,因此这类灯光没有镜面贡献。然而,它们将生成可追踪的烘焙阴影,并有适当的光照遮挡(例如,不会“穿过墙壁”),因为这里在光照贴图烘焙时通过光线追踪计算了所有由灯光引发的光照贡献,而不是在运行时计算。此外,与BakeModeIndirect一样,间接光照也会被烘焙。
  • 在烘焙模式下运行场景(应用程序),确保光照贴图成功生成。截至Qt 6.4,应用程序应按结构排序,以便光照贴圖的场景是第一个显示的视图,或者可以加载带有qml工具等QML查看器的场景。一旦烘焙完成,进度可以在控制台/调试输出中跟踪,然后应用程序退出。
  • 正常运行场景(应用程序),以查看加载光照贴图后的外观。然后可以开始微调
    • 对于某些模型,将lightmapBaseResolution从默认的1024降低到更小值是有意义的。这尤其适用于内置原语以及几何足够简单的任何内容。这将导致光照贴图更小且烘焙时间更快。第一次烘焙时,默认设置应该足够,以后可以进行调整。
    • 光贴图器对象公开了许多具有合理默认值的设置,但需要调整以匹配设计师的期望的可能并不少见。例如,samplesbounces的更改可以影响间接光照的质量,而indirectLightFactor允许使间接贡献更加突出。如果发生伪影(特别是阴影周围),可以微调bias
    • 生成光照贴图去噪是必不可少的。间接光照是通过路径追踪计算的,它产生的图像根据使用的samples数量呈现噪声。增加样本计数可以减少噪声,但会增加生成光照贴图所需的时间。无论样本计数如何,对生成的光照贴图运行去噪器基本上都是合理的,这些光照贴图是以.exr文件存储的32位RGBA浮点图像。

截至 Qt 6.5,通过DebugView提供交互式的运行时解决方案。在“工具”下现在有一个按钮,按下后会触发烘焙过程。一个窗口会弹出显示当前过程。可以通过点击取消按钮或关闭此窗口来取消。完成后,如果可能,它将尝试使用 loadPrefix 覆盖现有的 .exr 文件,否则将写入当前目录。目前,即使使用运行时解决方案,去噪仍然是一个手动过程。

去噪

下面是一个Cornell box场景的示例,首先使用带有 256 个 样本和最大次数 3 个 反射烘焙的光照图渲染。在第二个示例中,生成的图像文件已通过Open Image Denoise库进行处理。结果看起来明显更好,噪声几乎全部消失。

原始

"Cornell box scene with one point light, fully baked lightmap"

去噪

"Cornell box scene with the lightmaps denoised"

一个简单的基于 Qt 的 OIDN 命令行包装器可以与 Qt Quick 3D 生成的 qlm_*.exr 图像一起使用,可以在https://git.qt.io/laagocs/qlmdenoiser找到。目前需要从源代码构建,没有提供预编译的二进制文件。

光照图 UV

光照图 UV 坐标不使用与常规纹理相同的 UV 数据。在渲染光照图时,渲染器在采样光照图时不使用 UV0 或 UV1 数据。相反,在网格中有一个额外的、专用的 UV 通道,其中包含适用于光照图的 UV 图案。这涉及到避免重叠,并在适当位置有填充。对于常规 UV 数据,没有这样的要求,人们可能很希望为一个以上的顶点使用相同的 U 和 V 坐标。

生成合适的 UV 集的过程称为光照图 UV 拆包。Qt 总是在运行(加载)时执行此操作,无论是烘焙光照图还是通常渲染场景。

为了避免在运行时为光照图模型生成光照图 UV 数据,以改进网格加载时间,有两种选项

  • 对于没有光照图 UV 数据可用模型,光照图烘焙过程也会输出一个包含 qlm_*.mesh 文件的集合,名称基于 BakedLightmap::key,类似于 .exr 图像。如果应用程序希望,可以将其额外的 .mesh 文件与 .exr 资产一起分发。如果存在,这些网格文件将用于常规模型数据的替代品,并且相关的光照图数据已准备好使用。然而,这是一个完全可选的选项。如果运行时找不到 qlm_key.mesh,则在运行时执行 UV 拆包,对应用程序是透明的。
  • 或者,balsam 工具在资产导入时间提供了预先生成光照图 UV 数据的选项。这意味着模型的 .mesh 文件将从一开始就包含所需的数据,在光照图烘焙过程中不会生成额外的网格,因此没有额外的应用程序资产需要分发(当然,除了光照图图像之外)。为此,向 balsam 传递 --generateLightmapUV

光照图纹理大小

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

默认设置通常适合,不需要调整,尤其是对于中等到高复杂度的模型。

对于非常简单的模型,手动减小尺寸可能是期望的,然而,因为较小的光图尺寸仍然可以提供视觉上良好看的结果,同时减少资源(光图图像)的大小可以节省磁盘空间和内存。要这样做,将lightmapBaseResolution设置为合适的较小的数字。常见的选择是256、512或1024,但也可以是其他数字,最小为128。实际的光图宽度和高度可能不同,但大致与指定的大小相同。

更改值时,应始终重新烘焙光图并视觉上检查结果,以评估更改光图大小的影响。

使用运行时光图

在运行时使用预烘焙光图时相关的属性和类型

一旦烘焙成功完成,在正常运行应用程序时(没有设置命令行参数或环境变量)现在将自动选择生成的光图图像并正确渲染,这是在光图烘焙之前不可能实现的。如果需要,应用程序可以将这些放于不同位置,或通过Qt资源系统将其作为可执行文件的一部分分发。这是通过BakedLightmap::loadPrefix属性启用的。

以上方示例代码中的球体和四个矩形为例,烘焙过程生成了五个.exr文件(qlm_sphere1.exrqlm_rect1.exrqml_rect2.exr等),并提供了一个列表文件qlm_list.txt,这对于支持一次性处理多个文件的降噪工具很有用,但在运行时没有其他用途。应用程序需要提供这些.exr文件,以便引擎能够在同一目录中找到它们或在loadPrefix指定的位置中。

加载规则也适用于可选的.mesh文件,例如qlm_sphere1.meshqlm_rect1.mesh。如果应用程序希望加速场景加载时间,它应将与.exr光图图像放在一起的这些额外的.mesh文件分发。

另请参阅 Qt Quick 3D - 烤焙光图示例

© 2024 Qt公司。此处包含的文档贡献是各自拥有者的版权。所提供文档受由自由软件基金会发布的GNU自由文档许可协议版本1.3的条款约束。Qt及其相关标志是芬兰及其它国家的The Qt Company Ltd.的商标。所有其他商标归各自的所有者所有。