Unity 纹理压缩与相关优化知识汇总
一、纹理格式的基础知识
1. 图片格式
图片格式一般指我们从美术工具中导出的文件格式,用于在磁盘中存储的格式;如 jpg,png,tga 等;
2. 纹理格式
文件格式是图像为了存储信息而使用的编码方式,但是他不能被 CPU 所识别,GPU 的特性是做向量运算;因此这些文件被游戏读入时还需要 CPU 解压成 R8G8B8 等像素格式,再传送到 GPU 端使用;而在运行时做解压无疑是非常耗的,所以一般在图片源文件导入时就会为其生成纹理格式的文件。
纹理格式是可以被 GPU 所识别的像素格式,可以被快速寻址采样。
但是直接使用这种纹理格式会导致资源的加载时间长、内存占用大的问题,尤其在移动平台上更加明显,因此,为了解决这个问题,就出现了压缩纹理格式;
3. 纹理压缩格式
Unity 支持许多图片格式的源文件,但是 3D 图形的实时渲染中不会用这些格式,3D 图形硬件要求纹理以专门格式进行压缩,这些格式对快速纹理采样进行了优化,DXT,ETC 等压缩纹理格式可以在游戏运行中无需 CPU 解压就被 GPU 直接采样。每个不同的平台和设备都有自己的专有格式,因此在选择压缩方式时需要考虑兼容性。
Unity 文档中给出了所有支持的纹理压缩格式(https://docs.unity3d.com/cn/2018.4/Manual/class-TextureImporterOverride.html):
这里简单归纳一下,不做一一阐述:
ETC1 和 ETC2
分别对应着 RGB 和 RGBA 纹理,适用于 android 平台;
PVRTC
主要是 PowerVR 平台下的硬件使用,适用于 IOS 平台;
ASTC 格式
近年来使用频率越来越高的一种方式,安卓和 iOS 都开始支持 ASTC,它具有多种压缩比例,而且使用于 pot 和 npot,从 4x4 每个像素占用 8bit 到 12x12 每个像素仅占用 0.89 个;ASTC 压缩的不同设置决定了压缩的大小,和纹理是否带有 alpha 通道没有关系;
无 Alpha 通道的贴图建议压缩格式为 ASTC 8x8。如果贴图为法线贴图,建议压缩格式为 ASTC 5x5。有更高要求的贴图(比如面部、场景地面),可以设置压缩格式为 ASTC 6x6,法线贴图为 ASTC 4x4。
有 Alpha 通道的贴图建议压缩格式为 ASTC 5x5。有更高要求的贴图(比如特效、UI),可以设置压缩格式为 ASTC 4x4。
RGB24 位 & RGBA24 位 & RGBA16 位
真实色彩的压缩格式,其中 RGBA16 位是低质量的真实色彩;
4. 各平台下的格式选择参考
在 Unity 文档中还给出了每个平台下的默认格式,如下所示,它给出了不同平台、不同颜色模型(有无 Alpha 通道)下的压缩格式,包括了高质量、正常质量(默认设置)、低质量;

其中高质量一般是 8 位 / 像素,基本无压缩;正常质量一般在 4-8 位 / 像素;低质量一般在 4 位 / 像素以下;
注意兼容性:** 使用目标平台不支持的纹理压缩格式时,纹理将解压缩 RGBA32(因为 RGBA32 是真彩色,并有 alpha 通道,它是最高质量的压缩格式,并使用于所有平台)并与压缩纹理一起存储在内存中,解压缩纹理以及存储两次浪费了时间和内存,因此需要针对不同的平台选择兼容的格式 **。
二、纹理相关的性能优化
纹理过大会导致资源的加载、卸载耗时以及内存的占用增加(纹理资源的加载和内存占用一般是正相关的),纹理过大也会导致渲染时的带宽问题;而质量越高、文件越大,因此这是在品质和性能找到一个平衡;
1. 纹理属性的设置
mipmap 属性也会导致内存增加,也需要注意,一般 UI 资源都不需要勾选这两项的;
读写属性会导致内存加倍,而且一般不会用到;
NPOT 和 POT:主要和 mipmap,纹理的存储优化,以及纹理采样的地址计算等有关。如果是 p2 的话,做起上边这些就很简单了,基本上就是移位移位再移位,硬件做起来很简单,P2 对硬件更加友好;这里一般要求美术资源在制作时就是满足 pot 的,如果美术资源不是,那么使用 pot 则会出现效果问题;
如果美术资源已经有 alpha 通道,也可以在导入设置中设置 Alpha Source 为 None;
2. 纹理分辨率设置
没有什么比直接减少分辨率更直接有效的工作,就像我们都知道的道理:80% 的优化来源于 20% 的工作,那剩下 20% 的优化才是真正耗费时间的。
要去限制美术出图的大小,他们不关心性能只关心效果,可能每一张图都想要 2048x2048 甚至更高,所以有的时候必须要在效果可以接受的基础上来降低分辨率,降低一个级别就是四倍的优化。
3. 选择纹理压缩格式的策略
我们的思路是让需要展示出高品质的纹理占用更多的资源,让那些没必要展示高品质的纹理占用更少的资源;
- 尽量避免 RGBA32 和 ARGB32 纹理的使用,因为这种格式一般属于高清晰的,但也比较耗;
- RGBA16 格式的加载效率也很高,接近于 ETC1/PVRTC,设备越好,差距越小,因此在 ETC1/PVRTC 的效果不够好时,可以尝试用 RGBA16;
- UI 贴图要注意 Mipmap 和 read&write 属性的设置;
- 粒子贴图大小一般要在 256x256 一下;
- 对于模型、场景贴图,我们需要根据不同用途的贴图做不同的压缩处理,如果使用 ASTC 格式,可以参考 NVIDIA 官网给出的测试数据来选择;

- 在支持 OpenGLES3.0 的安卓设备上,ETC2 是一个很好的处理带透明度的纹理格式,但是游戏需要在 OpenGLES2.0 设备上,则不要使用 ETC2,因为该版本不支持 ETC2,这样就会导致 ETC2 会被转成 RGBA32 导致双倍的内存占用;
4. 实际应用
纹理资源一般可以做分档处理,最简单的可分为高中低三档,也可以根据需要加入不同的档次,依据档次来选择压缩格式;
在执行上,一般需要 hook 和 check 工具,在导入时对纹理进行一些 hook 判断和处理,同时 check 工具要能够获取到项目中所有纹理的大小、压缩格式等信息统计。
其它策略:
- 在玩家焦点区域的纹理可以选择高清,其它区域设置为低清晰,通过动态的 LOD 来做优化;
三、参考
Unity Manual:特定于平台的覆盖的压缩纹理格式;https://docs.unity3d.com/cn/2018.4/Manual/class-TextureImporterOverride.html
Unity Connect:说说 Unity 纹理压缩技术与策略;https://connect.unity.com/p/shuo-shuo-unitywen-li-ya-suo-ji-zhu-yu-you-hua-yi
各种移动 GPU 压缩纹理的使用方法,介绍了一些纹理格式、图片格式、压缩格式的联系:https://huailiang.github.io/blog/2019/texture/
UWA:Unity 加载模块深度解析(纹理篇):https://blog.uwa4d.com/archives/LoadingPerformance\_Texture.html
NVIDIA 官网:ASTC 压缩纹理格式详细分析数据:https://developer.nvidia.com/astc-texture-compression-for-game-asset
to be continued…