遵循以下最佳实践,以优化 Android 游戏中纹理的外观和性能。
纹理是 3D 艺术的核心元素。在最多设备上运行良好的 3D 游戏始于经过精心设计的 3D 艺术,以充分利用图形处理器。本指南重点介绍移动设备纹理的优化和最佳实践,旨在让您的游戏表现更好,最大限度地降低功耗,同时保持高视觉质量。
本文部分内容基于 Arm Limited 贡献并拥有版权的作品。
创建纹理图集
纹理图集是一种纹理,旨在包含多个图形对象(例如 3D 网格或 2D 精灵)的图像数据。图集纹理用于组合每个对象的图像,而不是每个对象都有自己的纹理。

最小化游戏帧的绘制调用次数是实现最佳渲染性能的重要因素。为不同对象使用相同的纹理是将其组合成单个绘制调用的一个因素。减少绘制调用对于受 CPU 限制的游戏尤为重要,因为每个绘制调用在图形驱动程序处理时都会产生 CPU 开销。纹理图集还可以减少游戏运行时数据中纹理资源文件的数量。数百甚至数千个纹理可以整合到数量少得多的纹理图集文件中。
创建 3D 网格时,您应该规划纹理图集布局。如果在制作网格资源之前创作了图集,则网格资源必须按照纹理图集进行 UV 展开。如果在创作后使用绘画软件中的合并或图集创建工具创建图集,则需要根据纹理重新排列 UV 孤岛。
引擎特定的绘制调用批处理
Unity 游戏引擎有一个 绘制调用批处理 功能,可以自动组合对象。要符合自动批处理的条件,对象必须共享一个通用材质(包括纹理)并被标记为静态。
虚幻引擎 4 需要手动设置批处理。您可以在将对象导入虚幻引擎之前,在 3D 软件中合并对象。虚幻引擎还包含 UE4 Actor 合并 工具,该工具可以组合网格并创建纹理图集文件。
生成 Mipmap
Mipmap 是纹理的低分辨率版本。给定纹理的一组 mipmap 称为 mipmap 链。链中每个后续的 mipmap 级别都比前一个级别具有更低的分辨率。Mipmap 用于在渲染期间实现纹理 LOD(细节级别)。当 mipmapped 纹理绑定到纹理阶段时,图形硬件会使用片段占用的纹理空间从 mipmap 链中选择一个级别。渲染 3D 场景时,离相机较远的对象将使用比靠近相机的相同对象更低分辨率的 mipmap。
Mipmapped 纹理比非 mipmapped 纹理使用更多的内存。额外的 mipmap 级别会使纹理的内存占用增加 33%。如果纹理以固定距离从相机绘制,则生成 mipmap 是不必要的内存使用。

正确使用 mipmap 可以提高 GPU 性能。低分辨率 mipmap 级别的可用性减少了内存带宽使用并提高了纹理缓存驻留。
Mipmapping 还可以通过减少纹理锯齿来提高视觉质量。纹理锯齿可以观察为远离相机的区域上的闪烁效果。

引擎特定的 mipmap 细节
虚幻引擎 4 要求纹理尺寸为 2 的幂(例如 512x1024, 128x128)才能使用 mipmapping。如果一个或两个纹理尺寸不是 2 的幂,则不会生成 mipmap 链。
Unity 引擎将 自动缩放 尺寸不是 2 的幂的纹理以创建 mipmap。请确保您的源纹理文件尺寸为 2 的幂,以避免这种缩放。
选择合适的纹理过滤模式
纹理过滤是一种硬件渲染功能,它影响渲染三角形的视觉外观。正确使用纹理过滤可以提高场景的视觉质量。有多种纹理过滤模式,每种模式在渲染改进和成本之间有不同的平衡。成本包括计算时间和内存带宽。三种常见的纹理过滤模式是:最近邻(或点)、双线性、三线性。各向异性过滤是一种额外的纹理过滤方法,可以与双线性或三线性过滤结合使用。
最近邻
最近邻是最简单、开销最低的纹理过滤模式。最近邻使用源纹理中指定的坐标采样单个纹素。使用最近邻渲染的三角形将呈现块状或像素化外观,尤其是在靠近相机渲染时。
双线性
双线性过滤采样源纹理中指定坐标周围的四个纹素。这四个纹素被平均以确定片段的纹理颜色。双线性过滤在像素之间产生更平滑的渐变,避免了最近邻过滤的块状外观。靠近相机渲染的三角形将显得模糊而不是像素化。由于额外的纹素采样和平均,双线性过滤的成本高于最近邻。

三线性
当您渲染一个顶点与相机距离不同的网格时,在渲染过程中可能会选择多个 mipmap 级别。两个 mipmap 级别之间的变化可能导致过渡点出现明显的锐利切口。三线性过滤通过对两个不同的 mipmap 级别执行双线性过滤并内插结果来软化这些过渡。使用多个 mip 级别和内插使三线性过滤比双线性过滤的计算成本更高。

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

模式选择策略
双线性过滤通常在性能和视觉质量之间取得最佳平衡。三线性过滤需要显着更多的内存带宽,应选择性使用。在许多情况下,双线性过滤与 2 倍各向异性过滤相结合将比 1 倍各向异性过滤的三线性过滤看起来更好,性能也更好。将各向异性级别提高到 2 倍以上成本极高,应仅对关键游戏资源进行非常选择性的操作。
纹理过滤可能占 GPU 总能耗的一半,因此尽可能选择更简单的纹理过滤器是降低游戏功耗的绝佳方法。
优化纹理尺寸
确保您的纹理尺寸尽可能小,同时仍能达到所需的图像质量。检查您的纹理资源,以查找错误过大的纹理。此原则适用于独立纹理和图集纹理。如果您的游戏支持涵盖广泛分辨率和性能能力的许多设备,请考虑为适当的设备类别创建低分辨率和高分辨率版本的纹理资源。
当渲染使用多种纹理的材质的网格时,请考虑选择性地降低某些纹理的分辨率。例如,当使用 1024x1024 的漫反射纹理时,将粗糙度或金属贴图纹理减小到 512x512 可能是可行的,并且对图像质量的影响最小。验证所有此类大小调整实验的影响,以确保它们不会损害所需的质量水平。
使用合适的色彩空间
许多用于纹理创作的软件包都在 sRGB 色彩空间中操作和导出。作为颜色处理的漫反射纹理可以使用 sRGB 色彩空间。不作为颜色处理的纹理,例如金属贴图、粗糙度贴图或法线贴图,不应以 sRGB 色彩空间导出。
游戏引擎纹理设置包含一个参数,用于指示纹理是否使用 sRGB 色彩空间。

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

使用纹理压缩
纹理压缩是一种应用于未压缩像素数据的图像压缩算法,它生成可在渲染期间由图形硬件快速解压缩的纹理。有效使用纹理压缩可以减少内存使用并提高性能,同时对视觉质量的影响最小。Android 上最常见的三种纹理压缩算法是:ETC1、ETC2 和 ASTC。对于现代游戏,ASTC 通常是最佳首选,如果您的游戏目标设备不支持 ASTC,则 ETC2 是备用选项。
ETC1
所有 Android 设备都支持 ETC1。ETC1 仅支持 RGB 颜色数据每像素四位的单一模式。ETC1 不支持 alpha 通道。许多支持 ETC1 的游戏引擎允许指定第二个 ETC1 纹理来表示 alpha 通道数据。
ETC2
超过 90% 的活跃 Android 设备支持 ETC2。不支持 OpenGL ES 3.0 API 的非常旧的设备无法使用 ETC2。与 ETC1 相比,ETC2 增加了:
- Alpha 通道支持,包括八位和单比特“穿孔”
- RGB 和 RGBA 纹理的 sRGB 版本
- 单通道和双通道 R11 和 RG11 纹理
ASTC
超过 75% 的活跃 Android 设备支持 ASTC。ASTC 具有可配置的压缩块大小,这使您可以精细控制给定纹理的压缩比与图像质量之间的平衡。ASTC 通常能够在与 ETC2 相同的内存大小下实现卓越的质量,或者在比 ETC2 更小的内存大小下实现相似的质量。

纹理压缩速度
如果您的游戏有大量纹理,纹理压缩可能需要很长时间。ETC 和 ASTC 都有可选的压缩质量设置。更高的质量设置需要更长的压缩时间。在开发过程中,您可能希望降低质量级别以缩短压缩时间,并在创建重要构建之前提高质量级别。
游戏引擎中的纹理压缩
如果您正在使用游戏引擎,您可能需要在项目级别选择您的纹理压缩格式(ETC 或 ASTC)。为了支持多种压缩格式以实现最大兼容性,可能需要额外的工作。Google Play Asset Delivery 的纹理压缩格式目标功能可以帮助您在游戏中包含多种格式,并且仅在安装时将最优格式交付给单个设备。
展开 UV
使 UV 孤岛尽可能平直。这有助于您的纹理在以下方面表现更佳:
- UV 孤岛打包更容易,浪费空间更少。
- 平直的 UV 减少纹理上的“阶梯效应”。
- 良好的 UV 打包确保从纹理获得最佳分辨率。
- 更好的纹理质量,即使 UV 因拉直而略有变形。

模型上可见的纹理接缝看起来很糟糕。尽量将任何 UV 接缝放置在不太明显的位置。为了帮助创建更好的法线贴图,请在边缘锋利的地方分割您的 UV 孤岛,并在孤岛周围留出一些空间。
避免不可察觉的细节
创作艺术时,不要添加不会被看到的细节,尤其是在为小屏幕设备设计的游戏中。为房间角落几乎不可见的小椅子模型制作一个精细的 4096x4096 纹理是浪费。在某些情况下,您可能需要夸大边缘(添加额外的光)和着色以改善形状感知。

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

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

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

打包时,将细节最多的数据分配给绿色通道。由于人眼对绿色更 敏感,图形硬件通常会为绿色通道分配更多位。例如,粗糙度/平滑度贴图通常比金属贴图具有更多细节,是分配给绿色通道的更好选择。
对于使用 alpha 通道的材质,如果您的打包纹理中只使用了两个通道,请考虑将 alpha 通道数据放入打包纹理而不是漫反射纹理中。根据漫反射纹理的格式,这可以帮助您减小其大小,或者通过省略 alpha 通道数据来提高其视觉质量。

请确保您的打包纹理设置为线性 RGB 色彩空间,而不是 sRGB。
创建法线贴图
法线贴图是一种技术,它赋予 3D 模型细节外观,而无需使用额外的几何体。诸如皱纹或螺栓等可能需要许多三角形才能建模的特征可以使用法线贴图进行模拟。法线贴图是否适用取决于游戏的艺术风格和方向。

法线贴图确实会产生一定的性能开销,在低端设备上应谨慎使用。法线贴图需要额外的纹理,从而导致额外的纹理采样和片段着色器计算。
法线贴图最佳实践
以下是法线贴图创建的一些最佳实践:
使用笼子
笼子是低多边形模型的更大或被推出去的版本。它需要包含高多边形模型才能在法线贴图烘焙期间正常工作。笼子用于在法线贴图烘焙期间限制射线投射距离,并有助于避免法线贴图上分割法线接缝的问题。


按网格名称烘焙匹配
如果您的烘焙软件支持,请按网格名称烘焙匹配。此功能可缓解法线贴图投影错误的问题。当物体彼此靠得太近时,它们可能会意外地将法线贴图投射到错误的面上。按网格名称匹配可确保烘焙仅在正确的表面上完成。有关 Substance Painter 中此功能的更多信息,请参阅此页面。有关 Marmoset Toolbag 中此功能的更多信息,请参阅此页面。
炸开网格
如果在烘焙时无法按网格名称匹配,请考虑炸开您的网格。炸开网格会将各个部分彼此移开,以便法线贴图不会投影到错误的表面上。如果您还在烘焙环境光遮蔽,则可能需要使用未炸开的网格单独执行该烘焙。

最小化接缝
硬边缘上的连续 UV 会导致可见的接缝,在硬边缘处分割 UV 以最大程度地减少这种效果。设置平滑组时,通常将角度保持在 90 度以下。UV 接缝需要在三角形上具有不同的平滑组。