借助 Unity 优化移动游戏的光照

光照是游戏中最重要的元素之一。它能起到营造气氛、指引玩家、标明威胁或目标等作用。光照可以左右游戏视觉效果的成败。例如,良好的光照能够改善拙劣的模型在游戏中的视觉效果,而蹩脚的光照却会让原本出色的模型大为失色。

本指南将提供关于如何在移动游戏中改善光照性能的信息。您决定如何使用光照将影响到移动游戏的性能。因此,请务必高效使用光照来确保游戏尽可能顺畅运行。

本文中的部分内容基于由 Arm Limited 贡献并拥有版权的作品。

渲染管线选项

Unity 的传统渲染管线包括以下渲染路径:

  • 前向渲染
  • 延迟着色

前向渲染

采用前向渲染时,实时光源开销非常高。如果能减少落在每像素上的光源数量,那么就可以弥补这种开销。

延迟着色

延迟着色需要 GPU 支持。在兼容的硬件上,延迟着色可以渲染大量实时光源并提供很高的光照保真水平。遗憾的是,延迟着色在移动 GPU 上效果不佳,因为它们的带宽较低。

在开发移动游戏时,让游戏能够在尽可能多的设备上流畅运行很重要。

通用渲染管线

Unity 开发了通用渲染管线 (URP)。我们强烈建议您在开发移动游戏时使用 URP。

光照模式

根据光源在场景中的移动方式或使用方式使用不同的光照模式。不同类型的光照模式具有不同的性能特征。在实现光源时,请考虑以下几点:

  • 对静态光照使用烘焙。对于在运行时光照不会改变的对象而言,烘焙是最理想的选择。烘焙光照是指预先计算光照数据并将其存储在称为光照贴图的纹理贴图中的过程。
    • 烘焙出的光照不能在运行时进行修改。光照贴图中的光源和阴影是静态的。由于所有光照均在 Unity 中预处理,因此不存在任何影响性能的运行时光照计算。
    • 动态阴影无法使用烘焙光照创建。对于动态或移动对象而言,烘焙光照的效果看起来可能比较违和。
  • 对于打算与移动对象相互作用的静止光源,请使用混合光照。例如,火炬将光线投射到玩家身上,并随着玩家经过而产生阴影。
    • 混合光照会产生动态直射光和阴影。
    • 您可以在静态对象的光照贴图计算中包含混合光照。
    • 您可以在运行时更改强度。仅直射光会更新。
    • 开销高。
  • 对于动态或可移动光源(例如从地面升起然后爆炸的火球所投射的光线),请使用实时光照。
    • 动态光源和阴影属性可以在运行时进行修改。
    • 实时光源不会被烘焙到光照贴图中。
    • 开销非常高。

如需了解详情,请参阅 Unity 的光照管线

尽可能使用静态光源而避免使用动态光源

动态或实时光照需要在每一帧进行计算和更新。这非常适合移动对象,也适用于互动和表现情绪。

与之相反,静态光源信息被烘焙到光照贴图中。使用光照贴图纹理可以让对象避免开销极高的逐顶点或逐像素光照计算。光照贴图纹理的渲染开销始终远远低于动态光照的渲染开销。因此,我们建议在移动游戏中优先实现烘焙光照。

光照贴图烘焙

预先计算光照效果称为光照贴图烘焙。光照效果存储在名为光照贴图的单独纹理中。光照贴图可用于增强对象的外观。每次场景迭代只需要进行一次光照贴图烘焙。如果更改了场景的几何体或更改了烘焙光照的参数,需要重新烘焙光照贴图。除了光照贴图纹理的开销之外,在运行时不会产生任何额外的性能开销。这是最适用于移动平台的初步光照方法。

烘焙光照不受场景中任何动态元素或移动元素的影响。烘焙光照包括所有静态元素的烘焙全局光照。这意味着光照贴图计算包括由其他静态对象反射的间接光以及直接照射对象的烘焙光。

图 1. Armies 技术演示中使用的全烘焙光照设置。

如需烘焙光照,请执行以下三个步骤。

第 1 步:将光照模式设为混合或烘焙模式

将光照的 Mode 设为“Mixed”或“Baked”。对于移动游戏,最好优先使用烘焙光照,混合模式次之。烘焙是开销最低的光照渲染方式。

图 2. Unity 中的光照 Mode 设置。

第 2 步:将对象设为静态

将任何受烘焙光照影响的对象设为 Static。虽然有许多针对被标记为静态的对象的优化,但一般情况下最好在 Static 下拉列表中选择 Everything。将对象标记为 Static 之后,Unity 就知道应将其包含在光照烘焙中。

图 3. 静态菜单示例。

第 3 步:烘焙光照

您可以依次选择 Window > Rendering > Lighting Settings,使用其中的 Lighting 菜单来烘焙光照。

烘烤光照时,保存的数据根据启动烘焙时处于活跃状态的场景而定。系统会生成一个与烘焙的场景同名的文件夹。此文件夹用于存储光照数据的所有组成部分。如果您的项目一次加载多个场景,您必须为每个场景烘焙光照。在调整场景后,您也必须重新烘焙光照。

图 4. 烘焙出的光照贴图示例。

优化光照贴图

设置好要烘焙的光照后,请确保烘焙出的贴图经过优化。光照贴图的大小因其烘焙时的设置而异。在移动设备上,您必须尽量减少内存用量,因此必须监控光照贴图大小。

在摘自 Armies 演示的以下示例中,共有七个 1024x1024 像素的光照贴图。在贴图预览中,可以看到光照贴图上的网格。选中的网格会突出显示。

图 5. 这是一个光照贴图示例。蓝色部分为选中的网格。

Lightmapping Settings 中有许多设置,它们连同贴图大小一起,决定了每个贴图使用多少内存和存储空间。以下几部分将重点介绍几项重要设置。

光照贴图程序

Unity 提供以下三种方法在场景中烘焙光照:

  • Enlighten:仅在 2020 年长期支持 (LTS) 版本之前的版本中受到支持。请勿在新项目中使用此方法。
  • Progressive CPU:可以节省大量时间,因为此方法逐步创建光照贴图。如果选择了 Prioritize View,就会优先处理“Scene”视图中的区域。这可以缩短用于设置场景光照的迭代时间。
  • Progressive GPU:与 Progressive CPU 的工作方式相同,但在 GPU 上而不是 CPU 上生成光照贴图。在受支持的硬件上,与使用 CPU 相比,此方法可大幅缩短烘焙时间。设置 Progressive GPU 还有其他要求。如需详细了解相关要求,请参阅渐进式 GPU 光照贴图程序页面。

图 6. 您可以通过 Lightmapper Settings 更改场景的烘焙方法。

纹素

纹素即纹理像素,是纹理贴图中的单个像素。纹素将照射到对象上的每个光点的光照信息存储在光照贴图中。如果每单位空间使用的纹素较多,就会影响光照质量、烘焙计算时间、磁盘存储开销以及光照贴图的 VRAM 开销。

如需减少所需的光照贴图数据量,请在 Lightmapping Settings 中调整每个烘焙单位的纹素数量。

图 7. 光照贴图的可用设置。

Lightmapping Settings 中,Lightmap Resolution 参数用于控制光照贴图中的每个单位使用多少纹素。以下示例展示了一个立方体采用不同 Lightmap Resolution 设置的情况。可以看出,分辨率提高会迅速增加所需的工作量。

图 8. 第一个立方体的 Lightmap Resolution1。第二个立方体的 Lightmap Resolution2。第三个立方体的 Lightmap Resolution5

如需查看纹素在场景中的布局情况,请选中“Scene”视图中的 Draw Mode下拉列表,然后选择 Baked Lightmap

烘焙对象覆盖着棋盘格叠加层。棋盘格图案显示了烘焙光照时纹素的分布方式。

在以下示例中,通过将 Armies 演示中的 Lightmap Resolution15 降至 12,可将所需的光照贴图数量从七减至四。

图 9. Lightmap Resolution12 的 Armies 演示。

纹素的使用

尽管您可以统一设置整个场景中每个单位的纹素数量,但有些对象通常不需要那么多纹素。

借助 Unity,您可以控制每个对象使用的纹素数量。在相应对象的 Inspector > Mesh Renderer 中,可以使用 Scale In Lightmap 参数值控制对象在光照贴图中使用的纹素数量。

在以下示例中,左侧立方体的每个烘焙单位包含五个纹素的光照信息。右侧立方体的“Scale In Lightmap”被设为 0.5。此设置使光照纹素减至 2.5 个,所需的光照贴图空间比左侧立方体少。

图 10. 光照贴图分辨率不同的两个立方体。

图 11. 您可以更改 Scale In Lightmap 设置来减少对象的纹素数量。

请尽量避免将纹素用于以下对象:

  • 玩家看不到的表面和对象。这可以防止在光照贴图上为屏幕上看不到的细节浪费内存。
  • 光线变化不大的表面。例如,阴影中的对象或受到单一光源照射的对象。
  • 小或薄的对象。这些对象受到的光照量对场景的最终渲染效果增色不多。

尽可能多使用假光照

为减少需要处理的对象,您可以伪造某些元素。这会使您的内容看似使用了光照,其实使用的是更高效的方法。

假阴影

实时阴影开销很高。它们通过一种称为阴影贴图的技术生成。将场景的几何体渲染到阴影贴图的开销与启用阴影的情况下绘制的顶点数量成正比。我们建议您限制投射阴影的几何体数量和实时投射阴影的光源数量。

针对没有动态光源的动态对象的阴影,您可以实现假阴影。这种做法既可以降低渲染开销,又能实现与动态阴影类似的效果。以下是实现假阴影的一些方式:

  • 将 3D 网格(例如平面或四边形)放置在角色之下,并对其应用模糊纹理。
  • 您可以自行编写自定义着色器,实现更复杂的模糊阴影。

以下示例显示了使用 3D 网格实现阴影的效果:

图 12. Armies 技术演示中的阴影实现。

直接在纹理上绘制光照信息

如果将部分阴影绘制到纹理中,就可以减少额外的光照所需的计算量。这种做法可以在烘焙场景的光照时节约内存,因为它需要的光照贴图数据更少。

光照探针

当您使用的动态对象采用烘焙光照时,这些对象不会受光照贴图影响。这可能会使人觉得它们不太像场景的一部分。

您可以用光照探针来解决这个问题。光照探针具有与光照贴图类似的优势。它们也能存储可提前计算和保存的光照数据,以便在运行时使用。这样就可以将大量计算开销转移到编辑时。

光照贴图对表面纹素中接收到的光线进行编码,而光照探针则存储穿过空白空间的光线。您可以使用此数据对移动对象提供光照。光照探针可将整个场景中的动态对象与光照贴图对象在视觉上融为一体。

光照探针最适用于为场景中移动的对象提供光照。探针利用的是烘焙出的光照,这能使移动的对象与场景具有相同的光照。使用光照探针为动态对象提供光照的成本要低于使用实时光照。

如需了解详细信息,请参阅使用光照探针的静态光照光照探针页面。

图 13. Armies 技术演示中放置了光照探针为动态人群提供光照。

Mesh Renderer 设置

无论您的场景使用哪种类型的光源,Mesh Renderer 设置都必须正确无误。

请关闭所有不使用的功能。在渲染场景时,即使不为对象提供光照,Cast Shadows 等设置也会增加开销。以下示例是图 13 中显示的某个角色的 Mesh Renderer 设置。该角色使用了光照探针数据,但未使用反射探针。

光照探针的 Blend Probes 设置可将距离最近的光照探针的光照信息混合到角色中。当角色在场景中移动时,影响该角色的光照探针会随之改变。由于渲染程序使用了模糊阴影方法,因此关闭了 Cast Shadows。此外,由于场景是烘焙的,没有实时阴影,因此也关闭了 Receive Shadows

图 14. 图 13 中渲染程序的 Mesh Renderer 设置。

实时光源与光源类型

我们建议您使用烘焙光照光照探针和假光照技术(例如绘制的光照纹理或着色器材质效果)来处理光源。但是,如果您需要实时光源,那么您必须考虑要使用的光源类型。

每种光源类型的光照计算开销各不相同。下面列出了每种光源类型的详情:

  • 方向光:这种光源具有一致的方向,且不会减弱。方向光是开销最低的实时光源。每个场景通常只需要一个方向光。采用前向渲染(建议针对移动平台采用的渲染路径)时,如果场景中没有方向光,Unity 会添加默认的方向光。
  • 聚光灯:聚光灯会剔除锥体以外的对象,不会为其提供光照。因此,聚光灯的计算开销低于球面点光源的计算开销。为了获得最佳性能,请严格限制锥体宽度,仅照射预定对象。
  • 点光源:这种光源在所有方向上投射光线。在所有方向上投射光线很有用,但开销非常高。在广阔区域内大面积使用点光源的开销极高。此外,阴影计算可能会成为光照计算中开销最高的部分。如果在所有方向上投射光线,阴影就会增加,进而增加计算量。