一、图片
1.1 纹理压缩
虽然我们可以将JPG、PNG
之类的格式导入Unity作为纹理的源文件,但实际上,在导入Unity后,会自动对其进行纹理压缩。
为什么要进行纹理压缩?
每像素位数 (bpp) 表示单个纹理像素所需的存储量。bpp 值越低的纹理在磁盘和内存中也越小。较低的 bpp 值也意味着 GPU 使用较少的内存带宽来读取纹理像素。GPU 内存带宽通常是帧率的瓶颈,因此纹理压缩有助于避免这一问题。
既然要进行纹理压缩,JPG、PNG
这类格式也拥有较高的压缩率,为什么不直接作为纹理呢?
这是因为这类格式不支持像素随机访问。GPU在进行渲染时,只使用需要的纹理部分,如果采用这种格式,在访问某个像素之前,就需要解码整张纹理。这就会造成严重的性能问题。
- 安卓平台压缩格式
在安卓平台下,官方建议设置压缩格式为ETC2或ETC。前者支持Alpha通道,且拥有更高的质量;后者不支持Alpha通道,要使用Alpha通道需要将纹理拆分为两个,一个用于RGB,一个用于Alpha。但是ETC支持比较旧的设备,且占用空间要小很多。
上图中带有“Crunched”字样的是Unity的二次压缩格式,即在ETC的基础上再次进行压缩。它可以使压缩后的图片占用体积更小,但相应的在运行时也需要进行二次解压。
- IOS平台压缩格式
IOS上的压缩格式官方推荐格式为ASTC或PVRTC。前者适用于较新的设备,后者则拥有更广泛的兼容性。
1.2 精灵的多边形模式
在图片的精灵模式中,有一个「Polygon」也就是多边形模式。
在多边形模式下,打开精灵的编辑窗口,选择「Custom Outline」模式,即可自定义精灵的网格。
最后需要在Image组件中开启「Use Sprite Mesh」选项
它的作用是允许我们自定义精灵的网格。对于一个透明背景的图片来说,如果采用默认的模式,其透明背景仍然会参与渲染,而且同样会造成Overdraw。
但如果我们自定义它的顶点,让网格更贴合非透明的部分,就可以减少可能产生Overdraw的面积。但代价是增加了顶点和边的数量。这就需要结合CPU和GPU的占用情况进行权衡。
1.3 关闭图片的读写和Mip Map
对于在UI中使用的图片,如果没有需求,建议关闭下面这两个选项
开启读写会导致在运行时将图片的一份拷贝加载到内存,从而造成资源的浪费。「Generate Mip Map」是用于在3D场景下,降低远处图片的渲染质量以提高性能,但对于UI来说并没有用处。而且开启这个选项会在运行时生成多个质量不一的图片加载到内存,同样会造成资源的浪费。
二、图集
2.1 为什么需要打图集
图集就是一种将多个纹理合并为一个组合纹理的资源。在之前学习优化的过程中我们了解到,决定UI元素能否合批的其中一个关键因素就是材质ID是否相同。假如每个UI元素都使用一张独立的材质,那么它们的材质ID是不同的,也就无法进行合批,最终导致UI产生大量的Draw Call。通过将这些材质打包成一张图集就可以很好地解决这个问题。
2.2 打包图集的方式
在「Edit -> Project Settings -> Editor -> Sprite Packer -> Mode」选项中,选择启用「Sprite Atlas V1」即可开启图集打包。
主图集
接下来在资源目录里创建一个图集「Create -> 2D -> Sprite Atlas」
这里的Type选择Master,表示这是一个主图集。然后将需要打包的图片添加到「Objects for Packing」中。然后点击「Pack Preview」按钮即可开始打包。效果如下
子图集
我们也可以将图集的Type设置为「Variant」,即子图集。它需要指定一个主图集,然后会继承主图集的所有属性。不过子图集可以设置「Scale」,可以用于不同分辨率下加载不同精度的图片。
我们可以在代码中通过如下方式加载图集中的图片
public SpriteAtlas atlas;
private void Start()
{
// 参数填图集中对应图片的名称
GetComponent<Image>().sprite = atlas.GetSprite("技能1");
}
2.3 图集的打包原则
- 同一UI下的图片打进同一个图集
因为在运行时加载图集中的图片时需要将整个图集载入内存,因此对于同一UI中的图片不应该跨多个图集。 - 对于通用的图片可以单独打一个图集
如果某些图片需要在多个UI中复用,可以单独打包到一个图集中。在运行时常驻内存,避免频繁加载。 - 只将较小的图片打入图集
2.4 动态图集
下面来设想一个场景。场景中是一个装备商店,商店中有数十件装备,对应有数十张图片。按照我们打图集的策略,只要将这些图片打到一个图集中,即可大幅减少Draw Call。但假如玩家要在这些装备中选择几样带在身上,那么这张由几十张图片构成的大图集就需要驻留在内存中。显然,这会造成资源的浪费。如果不将这些图片打成一个图集,又会造成大量的Draw Call。一个比较合理的解决方法是使用动态图集。
所谓的动态图集就是在运行时动态打包图集。我们可以将玩家选择的几件装备的图片动态打成一个图集。这样既可以避免大量的Draw Call,又可以防止无意义的内存占用。
具体的动态图集使用方法可以参考:https://zhuanlan.zhihu.com/p/367679754
三、参考资料
[1]. https://zhuanlan.zhihu.com/p/391885850
[2]. https://zhuanlan.zhihu.com/p/237940807
[3]. https://blog.csdn.net/linxinfa/article/details/108827197
[4]. https://zhuanlan.zhihu.com/p/399537508
[5]. https://docs.unity3d.com/cn/2021.3/Manual/class-TextureImporterOverride.html