遵循以下最佳实践,优化 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(细节级别)。当将 Mipmap 纹理绑定到纹理阶段时,图形硬件会使用片段占用的纹理空间从 Mipmap 链中选择一个级别。渲染 3D 场景时,远离相机的对象将使用比靠近相机的相同对象更低分辨率的 Mipmap。
与非 Mipmap 纹理相比,Mipmap 纹理使用更多内存。额外的 Mipmap 级别使纹理的内存占用量增加了 33%。如果纹理以固定距离从相机绘制,则生成 Mipmap 是不必要的内存使用。
正确使用 Mipmap 可以提高 GPU 性能。较低分辨率 Mipmap 级别可用性降低了内存带宽使用率并提高了纹理缓存驻留率。
Mipmap 还可以通过减少纹理混叠来提高视觉质量。纹理混叠可以观察到为远离相机的区域的闪烁效果。
引擎特定的 Mipmap 详细信息
虚幻引擎 4 需要纹理尺寸为 2 的幂(例如 512x1024、128x128)才能使用 Mipmap。如果一个或两个纹理尺寸不是 2 的幂,则不会生成 Mipmap 链。
Unity 引擎将自动缩放尺寸不是 2 的幂的纹理以创建 Mipmap。确保你的源纹理文件是 2 的幂尺寸以避免这种缩放。
选择合适的纹理过滤模式
纹理过滤是影响渲染三角形视觉外观的硬件渲染功能。正确使用纹理过滤可以提高场景的视觉质量。有多种纹理过滤模式,每种模式在渲染改进和成本之间具有不同的平衡。成本包括计算时间和内存带宽。三种常用的纹理过滤模式是:最近邻(或点)、双线性和平面三线性。各向异性是另一种纹理过滤方法,可以与双线性或平面三线性过滤相结合。
最近邻
最近邻是最简单且成本最低的纹理过滤模式。最近邻使用源纹理中指定的坐标采样单个纹素。使用最近邻渲染的三角形将呈现出块状或像素化的外观,尤其是在靠近相机渲染时。
双线性
双线性过滤对源纹理中指定坐标周围的四个纹素进行采样。这四个纹素取平均值以确定片段的纹理颜色。双线性过滤导致像素之间出现更平滑的渐变,避免了最近邻过滤的块状外观。靠近相机渲染的三角形将出现模糊而不是像素化。由于额外的纹素采样和平均,双线性的成本高于最近邻。
平面三线性
渲染顶点到相机的距离不同的网格时,在渲染过程中可能会选择多个 Mipmap 级别。两个 Mipmap 级别之间的变化可能会导致在过渡点出现明显的锐利切割。平面三线性过滤通过对两个不同的 Mipmap 级别执行双线性过滤并对结果进行插值来软化这些过渡。使用多个 Mip 级别和插值使平面三线性比双线性在计算上更昂贵。
各向异性
各向异性过滤提高了相对于相机的极端角度渲染的纹理网格的视觉质量。地面平面是此类网格的常见示例。各向异性过滤需要 Mipmap 纹理才能发挥作用。渲染期间应用的各向异性过滤的比率或级别可以进行配置。各向异性过滤的成本随着级别的增加而增加。
模式选择策略
双线性过滤通常是性能和视觉质量之间的最佳平衡。平面三线性过滤需要更多的内存带宽,应有选择地使用。在许多情况下,双线性过滤结合 2x 各向异性过滤的外观和性能将优于 1x 各向异性过滤的平面三线性过滤。将各向异性级别提高到 2x 以上的成本非常高,应针对关键游戏资源有选择地进行。
纹理过滤可能占 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
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 更小的内存大小下实现类似的质量。
纹理压缩速度
如果您的游戏有很多纹理,纹理压缩可能需要很长时间。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 接缝需要在三角形上具有不同的平滑组。