光照贴图和全局照明
简介
烘焙光照贴图可以预先生成来自DirectionalLight 、PointLight 和SpotLight 等灯光的直接光照,包括灯光投射的阴影。在运行时,无需在片段着色器中执行适当的计算,如果是阴影,也无需实时生成代价可能很高的阴影贴图,而是对预先生成的图像贴图进行采样。
注: 截至 Qt 6.4,光贴图烘焙还处于早期技术预览状态。在未来的版本中,功能、质量和 API 很可能会发生变化。
每个模型都会生成一个光贴图。即使一个模型有多个子模型,并因此与多个材质相关联,也会为整个模型生成一张光贴图。
光线贴图是使用光线追踪技术生成的,这种技术本质上可以提供适当的遮挡("光线不会穿墙"),而且可能比实时照明和阴影贴图技术提供更逼真的阴影。
更重要的是,光线贴图还可以烘焙间接照明,为全局照明提供解决方案。这将场景中其他表面反射的光线考虑在内。
下面是一个简单的例子。该场景包含四个矩形(Rectangle)和一个球形(Sphere)模型,一个方向光(DirectionLight)指向下方,一个PointLight 。矩形模型旋转了 0 度和 90 度,这夸大了实时照明计算的局限性,因为它们都与DirectionalLight 的方向平行或垂直。
右图是启用光贴图后渲染的场景,所有五个模型的光贴图都已烘焙完成。两个灯光都设置为完全烘焙,这意味着直接和间接照明都已烘焙。间接照明使用 256samples 和最大 3bounces 。然后对生成的光贴图进行去噪处理。这样得到的图像更加逼真。
实时照明
完全烘焙照明
下面的片段展示了如何实现光贴图效果。不同之处在于usedInBakedLighting 、bakeMode 和bakedLightmap 属性。在本示例中,使用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 。虽然直接照明和阴影看起来完全相同,但由于添加了一定程度的全局照明,右边的图像看起来要好得多。
实时照明
添加了烘焙间接光照
使用灯光贴图时的重要注意事项
有助于烘焙光照的光照可将其bakeMode 属性设置为 BakeModeIndirect 或 BakeModeAll。后者表示该特定灯光的直接和间接贡献都来自光贴图。直接贡献始终也包括阴影。另一方面,如果使用光照贴图的目的只是为场景中的特定灯光添加间接光照,同时仍实时计算直接光照(并执行阴影贴图),那么该灯光应使用 BakeModeIndirect(间接烘焙模式)。
注: 一般来说,灯光贴图适用于变换、几何体和材质都是静态的模型。这同样适用于参与烘焙照明的灯光。
例如,如果一个场景通过动画eulerRotation 属性来旋转一个模型,那么在该模型上应用光照贴图时就会产生视觉上不正确的结果。该特定模型的渲染结果将是不正确的,因为预生成的光贴图只能捕捉对象的单一旋转状态。再举个例子,如果模型的一个子模型的材质根据时间(动画)或某些用户交互动态改变其baseColor 属性,也会出现同样的情况。光线贴图只能捕捉一个给定的材质baseColor 。灯光也是如此。例如,随时间旋转、改变亮度和颜色等的DirectionalLight 不适合用于烘焙照明。
注: 另一方面,何时使用灯光贴图始终是设计者的选择。尤其是BakeModeIndirect 灯光,很可能会出现这样的场景:即使灯光映射场景中的某些对象采用了动态行为,但视觉效果仍然令人满意。
光线贴图是一个复杂的引擎和工具功能。它取代并重新实现了引擎渲染管道的多个部分。在制作光贴图时,它使用的是一种根本不同的渲染模型,同时仍使用相同的场景结构、资产数据和引擎数据结构并与之互操作。基于光线追踪的效果通常会优于实时效果,有时甚至会大幅优于实时效果,但这是以各种限制为代价的,例如模型和灯光必须是静态的,有时还会出现光贴图特有的质量和渲染伪影问题。
在实践中,使用哪种类型的灯光以及何时使用都是设计师的艺术选择。这三种bakeMode 设置都有各自的用途,复杂的大型场景很可能同时使用这三种不同的灯光,具体取决于场景的特定部分适合使用哪种灯光,以及存在哪种模型、材质和动态行为。光贴图并不是一种可以在任何场景和应用中启用的简单开关,而是一种强大的功能,它需要对给定场景的照明需求进行仔细评估,通常需要对场景内容和行为进行相应设计,并结合测试和调整循环,在决定最终方法和相关设置之前,对不同的光贴图烘焙和质量设置进行探索和测试。
注意: 光贴图不支持双面表面。Material.NoCulling
在实时光照下,cull mode 的材质会根据片段的朝向自动反转法线。光贴图不支持这种做法,因为光贴图的烘焙不在视图空间中进行。因此,对于需要使用烘焙光照的模型,应避免使用烘焙光照。
烘焙光贴图
与烘焙光照贴图相关的属性和类型,即生成图像贴图的离线过程,图像贴图可捕捉直接和间接光照,并可在应用程序的后续运行中被渲染器使用:
- Model::usedInBakedLighting
- Model::lightmapBaseResolution,
- Light::bakeMode,
- Lightmapper 和SceneEnvironment::lightmapper
- BakedLightmap 和Model::bakedLightmap
从 Qt 6.4 开始,光照贴图烘焙过程必须手动触发。只要命令行参数--bake-lightmaps
出现,或环境变量QT_QUICK3D_BAKE_LIGHTMAPS
设置为1
(或其他非零值),引擎就会以烘焙模式工作,并在烘焙完成后退出应用程序。通过查看调试输出中打印的信息,可以跟踪烘焙过程的各个步骤。结果是一组写入当前目录的.exr
文件,其中每个文件名都有一个qlm_
前缀,后面是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
工具)加载相关场景。烘烤完成后,应用程序退出,其进度可在控制台/调试输出中查看。 - 正常运行场景(应用程序),查看加载光贴图后的效果。然后就可以开始调整了:
- 对于某些模型,将lightmapBaseResolution 从默认的 1024 减小到更小是有意义的。这尤其适用于内置原型和任何几何形状足够简单的模型。这将导致光贴图更小,烘焙时间更快。第一次烘焙时,默认值就足够了,之后可以对值进行调整。
- 光贴图对象提供了许多具有合理默认值的设置,但其中有些设置需要调整以符合设计者的期望,这也不是不可能的。例如,可以更改samples 和bounces 来影响间接照明的质量,而indirectLightFactor 则可以使间接照明更加突出。如果出现伪影,尤其是阴影周围的伪影,bias 可以进行微调。
- 对生成的光照图进行去噪处理至关重要。间接照明是通过路径追踪来计算的,而路径追踪会产生噪声图像,这取决于所使用的samples 的数量。增加样本数可以减少噪点,但会增加生成光照图所需的时间。无论样本数多少,在生成的光照贴图上运行去噪器几乎总是有意义的,光照贴图是以 .exr 文件格式存储的 32 位 RGBA 浮点图像。
从 Qt 6.5 开始,运行时解决方案可通过DebugView 交互式提供。现在,在 "工具"(Tools)下有一个按钮,按下后将触发烘焙过程。此时会弹出一个窗口,显示当前进程。点击取消按钮或关闭窗口即可取消。完成后,如果可能,它会尝试使用 loadPrefix 覆盖现有的 .exr 文件,否则这些文件将被写入当前目录。目前,即使使用运行时解决方案,去噪仍然是一个手动过程。
去噪
下面是一个康奈尔方块场景的示例,首先使用 256samples 和最大 3bounces 的光贴图烘焙进行渲染。在第二个示例中,使用开放图像去噪库对生成的图像文件进行了去噪处理。效果明显好转,噪点基本消失。
原始图像
去噪
一个简单的、基于 Qt XML 命令行的 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
文件从一开始就包含必要的数据,在光贴图烘焙过程中不会生成额外的网格,因此应用程序无需随附额外的资产(当然除了光贴图图像)。为此,请将--generateLightmapUV
传递给 balsam。
光贴图纹理尺寸
在光贴图 UV 生成阶段,光贴图烘焙过程将为每个模型(包括所有子模型)确定合适的光贴图纹理尺寸。这对质量、性能和资源使用(磁盘和内存)都有影响。
默认值通常是合适的,无需调整,特别是对于中高复杂度的模型。
不过,对于非常简单的模型,手动减小尺寸可能是可取的,因为较小的光贴图尺寸仍然可以提供视觉效果良好的结果,而减小资产(光贴图图像)的尺寸则可以节省磁盘空间和内存。为此,请将lightmapBaseResolution 设置为一个适当的小数。常见的选择是 256、512 或 1024,但也可以是其他数字,最小为 128。实际的光贴图宽度和高度可能会有所不同,但与指定的大小大致相同。
在更改数值时,应重新绘制光贴图并目测结果,以评估更改光贴图大小的效果。
在运行时使用光贴图
在运行时使用预制光贴图的相关属性和类型:
烘焙成功完成后,正常运行应用程序(不使用命令行参数或环境变量设置)就可以获取生成的光贴图图像并正确渲染,而这在光贴图烘焙之前是不可能实现的。如果需要,应用程序可将光贴图放在不同的位置,或通过 Qt 资源系统将其作为可执行文件的一部分发送。这可以通过BakedLightmap::loadPrefix 属性启用。
以上面带有球体和四个矩形的示例代码为例,烘焙过程会生成五个.exr
文件(qlm_sphere1.exr
,qlm_rect1.exr
,qml_rect2.exr
等),以及一个列表文件qlm_list.txt
,该文件可作为支持一次性处理多个文件的去噪工具的输入,但在运行时不会使用。应用程序需要装载.exr
文件,以便引擎能在包含组件的同一目录或loadPrefix 指定的位置找到它们。
加载规则也适用于可选的.mesh
文件,如qlm_sphere1.mesh
或qlm_rect1.mesh
。如果应用程序希望加快场景加载时间,则应在.exr
光贴图图像旁边加载这些额外的.mesh
文件。
另请参阅 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.