纹理

请遵循以下最佳实践,以优化 Android 游戏中纹理的外观和性能。

纹理是 3D 美术的核心元素。在尽可能多的设备上都能流畅运行的 3D 游戏,其起点是经过精心设计的 3D 美术,这些美术能够充分利用图形处理器。本指南重点介绍了移动设备上纹理的优化和最佳实践,以提高游戏性能,并在保持高视觉质量的同时最大程度地减少功耗。

本文的部分内容基于 Arm Limited 贡献和版权的作品。

创建纹理图集

纹理图集是一种旨在包含多个图形对象(例如 3D 网格或 2D 精灵)图像数据的纹理。图集纹理用于组合每个对象的图像,而不是每个对象都有自己的纹理。

Meshes that share a texture atlas
图 1. 渲染场景(左)中的黄色高亮显示了共享纹理图集(右)的网格。

最大程度减少游戏帧的绘制调用次数是实现最佳渲染性能的重要因素。对不同对象使用相同的纹理是将它们组合到单个绘制调用中的一个因素。减少绘制调用对于 CPU 受限的游戏尤其重要,因为每个绘制调用在图形驱动程序处理时都会产生 CPU 开销。纹理图集还可以减少游戏运行时数据中的纹理资产文件数量。数百甚至数千个纹理可以合并成数量少得多的纹理图集文件。

在创建 3D 网格时,应计划纹理图集布局。如果在制作网格资产之前创建了图集,则必须根据纹理图集对网格资产进行 UV 展开。如果在创作后创建图集,使用绘画软件中的合并或图集创建工具,则需要根据纹理重新排列 UV 岛。

引擎特定的绘制调用批处理

Unity 游戏引擎具有 绘制调用批处理 功能,可以自动组合对象。要符合自动批处理的条件,对象必须共享一个通用材质(包括纹理),并且必须标记为静态。

虚幻引擎 4 需要手动设置才能进行批处理。您可以在导入到虚幻引擎之前在 3D 软件中合并对象。虚幻引擎还包括 UE4 Actor 合并 工具,该工具可以合并网格并创建纹理图集文件。

生成 Mipmap

Mipmap 是纹理的较低分辨率版本。给定纹理的 Mipmap 集合称为 Mipmap 链。链中的每个后续 Mipmap 级别都比前一个级别具有较低的分辨率。Mipmap 用于在渲染期间实现纹理 LOD(细节级别)。当将具有 Mipmap 的纹理绑定到纹理阶段时,图形硬件会使用片段占用的纹理空间从 Mipmap 链中选择一个级别。在渲染 3D 场景时,远离摄像机的对象将使用比靠近摄像机的相同对象更低分辨率的 Mipmap。

与不具有 Mipmap 的纹理相比,具有 Mipmap 的纹理使用更多内存。额外的 Mipmap 级别会使纹理的内存占用量增加 33%。如果纹理在距摄像机固定距离处绘制,则生成 Mipmap 是不必要的内存使用。

A mipmap chain from a base texture resolution of 512x512 pixels
图 2. Mipmap 链,纹理的基本分辨率为 512x512 像素。

正确使用 Mipmap 可以提高 GPU 性能。较低分辨率的 Mipmap 级别可用性减少了内存带宽使用量并提高了纹理缓存驻留率。

Mipmap 还可以通过减少纹理混叠来提高视觉质量。纹理混叠可以观察到在远离摄像机的区域出现闪烁效果。

An example of texture aliasing
图 3. 纹理混叠示例。一个图像在没有 Mipmap 的情况下渲染(左),另一个图像在有 Mipmap 的情况下渲染(右)。在左侧图像的红色矩形内可以观察到纹理混叠。

引擎特定的 Mipmap 详细信息

虚幻引擎 4 需要纹理尺寸为 2 的幂(例如 512x1024、128x128)才能使用 Mipmap。如果一个或两个纹理尺寸不是 2 的幂,则不会生成 Mipmap 链。

Unity 引擎将 自动缩放 尺寸不是 2 的幂的纹理以创建 Mipmap。确保您的源纹理文件为 2 的幂尺寸,以避免此缩放。

选择合适的纹理过滤模式

纹理过滤是影响渲染三角形视觉外观的硬件渲染功能。正确使用纹理过滤可以提高场景的视觉质量。有多种纹理过滤模式,每种模式在渲染改进和成本之间取得不同的平衡。成本包括计算时间和内存带宽。三种常用的纹理过滤模式是:最近邻(或点)、双线性,和三线性。各向异性是一种附加的纹理过滤方法,可以与双线性或三线性过滤相结合。

最近邻

最近邻是最简单且成本最低的纹理过滤模式。最近邻使用源纹理中指定的坐标采样单个纹理元素。使用最近邻渲染的三角形将呈现出块状或像素化的外观,尤其是在靠近摄像机时渲染时。

双线性

双线性过滤采样源纹理中指定坐标周围的四个纹理元素。对这四个纹理元素进行平均以确定片段的纹理颜色。双线性过滤导致像素之间出现更平滑的渐变,避免了最近邻过滤的块状外观。靠近摄像机渲染的三角形将显得模糊而不是像素化。由于额外的纹理元素采样和平均,双线性的成本高于最近邻。

A comparison between nearest and bilinear filtering
图 4. 最近邻(左)和双线性(右)纹理过滤的比较。

三线性

当渲染网格时,如果顶点到摄像机的距离变化很大,渲染过程中可能会选择多个mipmap级别。两个mipmap级别之间的变化可能会导致在过渡点出现明显的锐利切割。三线性过滤通过对两个不同的mipmap级别执行双线性过滤并插值结果来软化这些过渡。使用多个mipmap级别和插值使得三线性过滤比双线性过滤计算量更大。

A comparison between bilinear and trilinear filtering
图 5. 双线性(左)和三线性(右)纹理过滤的比较。放大区域对比了沿mipmap过渡渲染的差异。

各向异性

各向异性过滤提高了相对于摄像机以极端角度渲染的纹理网格的视觉质量。地面平面是这种网格的一个常见示例。各向异性过滤需要mipmap纹理才能发挥作用。渲染过程中应用的各向异性过滤的比率或级别可以配置。各向异性过滤的成本随着级别的增加而增加。

A comparison between 1x and 2x anisotropic filtering
图 6. 双线性/1x 各向异性过滤(左)和双线性/2x 各向异性过滤(右)的比较

模式选择策略

双线性过滤通常是在性能和视觉质量之间取得最佳平衡的方法。三线性过滤需要更多的内存带宽,应选择性地使用。在许多情况下,双线性过滤结合 2x 各向异性过滤的效果和性能都优于三线性过滤结合 1x 各向异性过滤。将各向异性级别提高到 2x 以上的成本非常高,应非常有选择地针对关键游戏资源进行。

纹理过滤可能占 GPU 总功耗的一半,因此尽可能选择更简单的纹理过滤器是降低游戏功耗的绝佳方法。

优化纹理尺寸

确保纹理尺寸尽可能小,同时仍能达到所需的图像质量。检查纹理资源以查找错误的大尺寸纹理。此原则适用于离散纹理和图集纹理。如果您的游戏支持许多包含各种分辨率和性能能力的设备,请考虑为相应的设备类别创建纹理资源的低分辨率和高分辨率版本。

在渲染使用材质中多个纹理的网格时,请考虑选择性地降低某些纹理的分辨率。例如,当使用 1024x1024 的漫反射纹理时,可以将粗糙度或金属贴图纹理降低到 512x512,而对图像质量的影响最小。验证所有此类调整大小实验的影响,以确保它们不会影响所需的质量级别。

使用合适的颜色空间

许多用于纹理创作的软件包都在 sRGB 颜色空间中运行并使用 sRGB 颜色空间导出。作为颜色处理的漫反射纹理可以使用 sRGB 颜色空间。不作为颜色处理的纹理,例如金属、粗糙度或法线贴图,不应在 sRGB 颜色空间中导出。

游戏引擎纹理设置包括一个参数,用于指示纹理是否使用 sRGB 颜色空间。

sRGB texture settings in Unity and Unreal Engine 4
图 7. Unity(左)和虚幻引擎 4(右)中的 sRGB 纹理设置。

由于此类纹理的像素数据不用作颜色数据,因此使用 sRGB 颜色空间会导致不正确的视觉效果。

Rendering a roughness metallic man in linear versus sRGB color space
图 8. 线性(非 sRGB)粗糙度金属贴图(左)和 sRGB 粗糙度金属贴图(右)。右侧的反射看起来不正确。

使用纹理压缩

纹理压缩是一种应用于未压缩像素数据的图像压缩算法,它会生成可在渲染过程中由图形硬件快速解压缩的纹理。有效使用纹理压缩可以减少内存使用量并提高性能,同时对视觉质量的影响最小。Android 上最常见的纹理压缩算法有三种:ETC1、ETC2 和 ASTC。对于现代游戏,ASTC 通常是最佳的主要选择,如果您的游戏针对不支持 ASTC 的设备,则 ETC2 作为备选方案。

ETC1

所有 Android 设备都支持 ETC1。ETC1 仅支持单一的每像素四位 RGB 颜色数据模式。ETC1 不支持 alpha 通道。许多支持 ETC1 的游戏引擎允许指定第二个 ETC1 纹理来表示 alpha 通道数据。

ETC2

ETC2 受超过 90% 的活跃 Android 设备支持。不支持 OpenGL ES 3.0 API 的非常旧的设备无法使用 ETC2。与 ETC1 相比,ETC2 添加了

  • Alpha 通道支持,包括八位和一位“穿透”
  • RGB 和 RGBA 纹理的 sRGB 版本
  • 单通道和双通道,R11 和 RG11 纹理

ASTC

ASTC 受超过 75% 的活跃 Android 设备支持。ASTC 具有可配置的压缩块大小,这使您可以对压缩率与给定纹理的图像质量之间的平衡进行细粒度控制。ASTC 通常能够在与 ETC2 相同的内存大小下实现更高的质量,或者在比 ETC2 小的内存大小下实现类似的质量。

A visual comparison of texture compression formats using the same source image
图 9. 未压缩(左,大小为 17MB)、使用 ETC1 压缩(中,大小为 3MB)和使用 ASTC 压缩(右,大小为 2.5MB)的图像之间的比较。

纹理压缩速度

如果您的游戏有很多纹理,则纹理压缩可能需要很长时间。ETC 和 ASTC 都有可选的压缩质量设置。质量设置越高,压缩所需的时间越长。在开发过程中,您可能希望降低质量级别以减少压缩时间,并在创建重要版本之前提高质量级别。

游戏引擎中的纹理压缩

如果您使用的是游戏引擎,则可能需要在项目级别选择纹理压缩格式(ETC 或 ASTC)。为了支持多种压缩格式以实现最大兼容性,可能需要执行其他工作。Google Play Asset Delivery 的纹理压缩格式定位功能可以帮助在游戏中包含多种格式,并且仅在安装时将最优格式传递给单个设备。

展开 UV

使 UV 岛尽可能笔直。这将以以下方式帮助您的纹理

  • 打包 UV 岛更容易,从而减少浪费的空间。
  • 笔直的 UV 减少了纹理上的“阶梯效应”。
  • 良好的 UV 打包可确保从纹理获得最佳分辨率。
  • 即使 UV 略微偏离笔直,也能获得更好的纹理质量。
An unoptimized UV island versus an optimized UV island
图 10. 未优化的 UV 岛(左)和拉直/展开的 UV 岛(右)。

模型上的可见纹理接缝看起来很糟糕。尝试将任何 UV 接缝放置在不太显眼的地方。为了帮助创建更好的法线贴图,在边缘锐利的地方分割您的 UV 岛,并在岛屿周围留出一些空间。

避免难以察觉的细节

在创建艺术作品时,不要添加不会看到的细节,尤其是在为配备较小屏幕的设备设计的游戏中。制作一个复杂的 4096x4096 纹理浪费在房间角落里几乎看不到的小椅子模型上。在某些情况下,您可能需要夸大边缘(添加额外的亮点)和阴影以提高形状感知。

A small texture is used on a model rendered at a distance
图 11. 在远处渲染的士兵模型上使用了缺乏过多细节的小型 256x256 纹理。

烘焙细节

移动设备的屏幕比个人电脑或游戏机小,图形硬件也较不强大。不要在运行时计算环境光遮蔽或镜面高光等效果,如果可能,请考虑将它们“烘焙”到漫反射纹理中。这有助于提高性能并确保细节的可见性。

Baking highlights and ambient occulusion into a diffuse texture
图 12. 烘焙到漫反射纹理(左)中的高光和环境光遮蔽以及在游戏中渲染(右)。

使用颜色着色

如果您能够创建自定义着色器,并且拥有具有相似或统一颜色方案的网格,请考虑对适用的网格使用颜色着色。使用颜色着色时,会使用灰度纹理,这比 RGB 纹理占用更少的纹理内存。顶点颜色数据由着色器应用以对网格进行着色。另一种着色方法是使用 RGB 遮罩并根据遮罩的颜色范围应用纹理。

A grayscale texture tinted at runtime
图 13. 在运行时为柱体模型着色的灰度纹理(左)(右)。

打包纹理通道

在渲染具有多个纹理的材质时,寻找将仅使用单个颜色通道的纹理组合到使用所有三个颜色通道的单个纹理中的机会。这减少了内存使用量并减少了片段着色器执行的纹理采样器操作次数。

Three single-channel textures combined into one multi-channel texture
图 14. 三个单通道纹理(左)组合成一个多通道纹理(右)。环境光遮蔽数据分配给红色,粗糙度/平滑度贴图分配给绿色,金属贴图分配给蓝色。

打包时,将细节最多的数据分配给绿色通道。由于人眼对绿色更敏感,因此图形硬件通常会为绿色通道分配更多位。例如,粗糙度/平滑度贴图通常比金属贴图具有更多细节,并且是分配给绿色通道的更好选择。

对于使用 alpha 通道的材质,如果您的打包纹理中仅使用两个通道,请考虑将 alpha 通道数据放入打包纹理而不是漫反射纹理中。根据漫反射纹理的格式,这可以帮助您减小其尺寸,或通过省略 alpha 通道数据来提高其视觉质量。

An alpha channel packed into another texture
图 15. alpha 通道不透明度贴图与粗糙度/平滑度贴图和金属贴图一起打包到纹理中。

确保您的打包纹理设置为线性 RGB 颜色空间而不是 sRGB。

创建法线贴图

法线贴图是一种可以让3D模型呈现细节外观的技术,而无需使用额外的几何体。例如皱纹或螺栓等可能需要许多三角形建模的特征,可以使用法线贴图来模拟。根据游戏的艺术风格和方向,法线贴图可能合适也可能不合适。

A model rendered with and without a normal map
图 16. 未应用法线贴图的模型渲染(左),以及应用了法线贴图的相同模型渲染(中),以及法线贴图纹理(右)。

法线贴图会带来一些性能开销,因此应在低端设备上谨慎使用。法线贴图需要额外的纹理,导致额外的纹理采样和片段着色器计算。

法线贴图最佳实践

以下是法线贴图创建的一些最佳实践

使用包围盒

包围盒是低多边形模型的放大版或外推版。它需要包含高多边形模型才能在法线贴图烘焙过程中正常工作。包围盒用于限制法线贴图烘焙期间的射线投射距离,并有助于避免法线贴图上出现分割法线接缝的问题。

A cage surrounding the low polygon mesh
图 17. 包围低多边形网格的包围盒。
A model rendered using a normal map with and without a cage
图 18. 使用带有包围盒生成的法线贴图渲染的模型(左)与使用未带包围盒生成的法线贴图渲染的模型(右)进行比较。

按网格名称烘焙匹配

如果您的烘焙软件支持,请按网格名称烘焙匹配。此功能可以减轻法线贴图投影错误的问题。当物体彼此过于靠近时,它们可能会意外地将其法线贴图投影到错误的面。按网格名称匹配可确保烘焙仅在正确的表面上进行。有关 Substance Painter 中此功能的更多信息,请参阅此页面。有关 Marmoset Toolbag 中此功能的更多信息,请参阅此页面

分离网格

如果在烘焙时无法按网格名称匹配,请考虑分离网格。分离网格会将网格的各个部分移开,以防止法线贴图投影到错误的表面。如果您也正在烘焙环境光遮蔽,则可能需要使用未分离的网格单独执行烘焙。

An exploded mesh for normal map baking
图 19. 用于法线贴图烘焙的分离网格

最小化接缝

硬边上的连续 UV 会导致可见的接缝,在硬边上分割 UV 以最大程度地减少这种影响。设置平滑组时,经验法则是将角度保持在 90 度以下。UV 接缝需要在三角形上具有不同的平滑组。