概述
2D项目使用Sprite和其他图形来创建其场景的视觉效果。这意味着单个项目可能包含许多纹理文件。Unity 通常会为场景中的每个纹理发出一个DrawCall;但是,在具有许多纹理的项目中,多个绘制调用会占用大量资源,并会对项目的性能产生负面影响。
精灵图集 (Sprite Atlas) 是一种将多个纹理合并为一个组合纹理的资源。Unity 可以调用此单个纹理来发出单个绘制调用而不是发出多个绘制调用,能够以较小的性能开销一次性访问压缩的纹理。
为什么图集可以减少DrawCall
简单点说就是:使用图集,我们就可以归类不同种类的图片,之后就可以一次进行多张图片的绘制处理,只需调用一次DC,提高了效率。
什么是DrawCall
DrawCall就是一个命令(也是CPU调用图形编程接口,比如DirectX或OpenGL),一个由CPU发起,命令GPU执行的命令,就是CPU告诉GPU可以对某个模型进行渲染处理的传话人。一般来说一个独立的模型会产生一个DrawCall,但是如果经过一些特殊的处理,比如动态合批,静态合批等等操作,就会降低DrawCall。
为什么DrawCall会影响CPU性能
CPU将DrawCall发往GPU过程中并不是很直接的就交给GPU,而是先将这些数据存到一个命令缓冲区内,然后再后面的几何阶段由GPU进行数据的读取。GPU的渲染能力是很强的,渲染300个和3000个三角网格通常没有什么区别,因此渲染速度往往快于CPU提交命令的速度。DrawCall的数量太多,CPU就会把大量时间花费在提交DrawCall命令上,造成CPU的过载。
为什么图集的大小要设成2的N次方
因为大部分游戏引擎底层的渲染方式都是基于OpenGL的,OpenGL载入纹理图片时,所用内存会自动扩张到2的N次方(500 x 500 → 512 x 512)。
- 因为转化过程比较慢,由运行程序转换十分耗时,所以Unity3D提前将资源转化为符合标准的图片,这样可以提升转化速度;
- 节省内存。一张图片的大小为10*10像素,OpenGL会按照16*16的规格将图片载入到内存中;如果图片大小为64*65,那么就会按照64*128载入了,这就造成了内存的无必要开销;
- 减少包体。打成图集后的合成的大图会比之前所有的散图所占用的物理存储更小。这样从通过减小图片资源物理存储大小起到压缩游戏安装包的作用。
图集的压缩格式
为什么要压缩
- 我们平时看到的图片一般都是png、jpg、bmp等格式的。在实际开发中,我们一般是不会直接用这些格式,因为加载速度慢,占用内存高;
- 为什么这么说呢?因为这些压缩格式不是硬件支持的,所以需要在内存中解压后再传给GPU用,那解压后的内存有多大呢?假设一张有4通道的1024*1024的图片,每通道8位的话,那这一张图就是4M,如果是RGBA4444的话也有2M;
- 那还有什么压缩格式可用呢?很多,比如安卓支持ETC格式、IOS支持PVRTC格式,还有ASTC格式。我们以ETC2 8bits为例,同样一张有4通道的1024*1024的图片,一个像素占1个字节,所以就占1M。 比前面省了4倍;
- PVRTC 4bits压缩更高,最后大小也就0.5M;
- 如果用PVRTC 2bits,还能更小,但压缩质量会有所下降;
- 请注意,PVRTC 格式要求纹理为正方形(即宽度等于高度);
- 上述格式除了压缩率高,还是硬件直接支持的,不需要解压。无论加载速度还是内存占用上表现都非常不错。
压缩格式该怎么选择
- 压缩格式之所以这么多就是因为硬件太多了,大家都为了解决性能的问题,所以整出了这么多格式;
- 至于怎么选择,我们粗略的给出了答案。安卓选择ETC格式,不带透明度的可以用ETC1,带透明度的用ETC2。IOS选择PVRTC格式;
- 如果要用ASTC的话,需要达到一定的条件,如下:Android:需要 GL_KHR_texture_compression_astc_hdr 扩展。iOS:需要 A13 或更高版本的芯片 (2019)。如果不支持,则纹理将解压缩为 RGB9E5 格式,从而失去了 Alpha 通道。
图集的缺点
在使用图集时,会将整张图集加载进内存,因此应当将经常需要显示的图片素材放到同一张图集中,如果不经常使用的也放到同一张图集,即使这张图片不需要显示,也会被加载进内存汇总。同时图集的大小固定为POT(Power of Two),如果图集中的元素大小差距过大,也会导致空间浪费。