1 什么是压缩纹理
在游戏中,纹理不仅占据大量的包体,也占据了大量的内存。传统的图片压缩格式(如JPEG、PNG等)虽能减少资源大小,但是不能被GPU直接识别,还是需要先加载到内存通过CPU解码,转换成RGB/RGBA等能被GPU识别的格式,才能传送到GPU进行渲染。
为避免这些问题,压缩纹理,指的是一种针对GPU的纹理压缩方案,使纹理能够直接被GPU识别并进行渲染,它具有以下优点。
- 无需CPU解码,节省了CPU运算,减少耗电量。
- 纹理直接被传送到GPU,避免了内存占用,提高渲染性能。
- 高效的压缩算法,减少了包体大小。
2 压缩纹理的原理
传统的图片压缩主要目的是存储
和传输
,为了尽可能的高效压缩,使用了可变的压缩比率,因此在解压时需要解压更多的像素位才能读取某个像素的位置,不适合随机和快速读取,也发挥不了GPU的并行处理优势。
而压缩纹理使用一个固定的压缩比率,将纹理划分成多个像素块,每个像素块包含2*2
或4*4
个像素,然后对每个像素块进行压缩,被压缩的像素信息存储在一个像素集合中,每个像素块的索引位置存储在一个块索引图中。读取时,首先将纹理坐标转化为块索引值,然后在像素集合中查找对应的像素块,最后在这个像素块中找到纹理颜色值。
因为采用了固定的压缩比率,GPU内部可以并行处理,从而快速的解压缩。与之相对的是,纹理的压缩过程发生在程序运行之前,并不在意编码速度,因此在压缩时会遍历所有可能性,找到和原始像素差值最小的编码,这也是纹理压缩耗时较久的原因。
顺便说一下,普通图片格式中,PNG是无损压缩,JPEG是有损压缩。而压缩纹理都是有损压缩,只是在绝大部分情况下,手机上看不出来而已。
3 常用的压缩纹理格式
手机上使用压缩纹理依赖于OpenGL ES的支持,OpenGL ES 2.0本身并没有定义任何纹理压缩格式,它仅提供 glCompressTexImage2D() 方法供应用程序上传压缩纹理,压缩纹理的格式由各个GPU厂商定义和实现。
OpenGL ES 3.0提供了压缩纹理标准,使各个平台都可以使用同一种压缩纹理,但市面上的设备还需要很长时间才会全部过渡到OpenGL ES 3.0。因此,仍然需要对不同的平台和设备使用不同的压缩纹理格式。
手机游戏中常用的有以下格式。
3.1 ETC1
ETC1把4*4
的像素块压缩成固定的64位编码(8个字节),4*4
像素块是16个像素,每个像素4字节,一共占64个字节,所以压缩比是 64/8=8。但是ETC1只能存储RGB信息,不适用带透明度的纹理,为解决这个问题,Creator在ETC1文件中额外写入了透明度信息,即ETC1+A格式,它的压缩比是 64/16=4。
ETC1/ETC1+A需要OpenGL ES 2.0(对应WebGL 1.0)环境,目前几乎所有Android手机都支持ETC1,但是iOS不支持。
ETC1/ETC1+A纹理的长宽可以不相等,但要求是2的幂次方。
3.2 ETC2
ETC2是ETC1的扩展,压缩比率一样,但压缩质量更高,而且支持透明通道,能完整存储RGBA信息。
ETC2需要OpenGL ES 3.0(对应WebGL 2.0)环境,目前还有不少低端Android手机不兼容,iOS方面从 iPhone5S 开始都支持OpenGL ES 3.0。
ETC2和ETC1一样,长宽可以不相等,但要求是2的幂次方。
3.3 PVRTC
Creator中常用的是PVRTC4+A,压缩比和ETC一样,iOS全系列支持,但是Android不支持。另外PVR要求纹理长宽相等(正方形)且是2的幂次方,例如1280*720
的PNG图片,转换后变成2048*2048
,这一点会大大增加内存消耗。在实测中还发现转换后的图片质量不如ETC1,存在模糊、毛边现象,对画面要求高的游戏不适合。
4 压缩纹理的使用
压缩纹理的使用非常简单,根据构建平台添加需要的格式即可,具体参见Creator官方文档,本文不再重复了。
Creator编辑器还提供了转换压缩纹理的选项,根据转换速度分为Fast、Slow等好几档,速度越慢则画面质量越好。但不管选哪个,只影响显示效果和转换时长,显存占用都是一样的。一般情况下,显存占用就是压缩纹理的文件大小,例如文件大小是1.5M,则它占用的显存也是1.5M。
在设置压缩纹理格式时,目前Creator 2.x版本还需手动一个一个设置。如果想一次性设置所有或部分资源,自己写个脚本遍历修改对应的.meta
文件也比较方便,这里是一个我写好的脚本 一键自动化设置压缩纹理格式
5 总结
在实际项目中的测试结果是,单图、自动图集、TexturePack合图加起来超过两千张图片的Creator工程,使用PNG时打出来的apk包大小近500M,内存占用1.3G。采用压缩纹理后,包体大小降到150M,内存占用降到600M。
- 压缩纹理的初始文件大小比PNG大1-2倍,但经过zip或打包成apk、ipa后,大小比PNG小了1/2-1/3,对减小包体有巨大好处。
- 从OpenGL ES 2.0开始,GPU纹理支持非2幂次方(例如,小于2048的图片比2048图片更节省内存),目前所有手机都已经是OpenGL ES 2.0及以上了。
- 对于Android原生平台,要想兼容尽可能多的设备,又想发挥内存和包体优势,目前最佳选择是ETC1。
- 对于iOS原生平台,PVR画面质量不佳,如果只考虑 iPhone5S 及以上设备,则使用ETC2比PVR好。
- 采用压缩纹理后,Creator的动态合图会失效,因为动态合图依赖于内存中的纹理缓存,而压缩纹理直接传送到GPU。
- 压缩纹理在压缩时速度较慢,如果是第一次压缩,整个过程可能在数个小时以上,具体取决于资源数量。
- OpenGL ES 3.0开始新推出一种ASTC格式,Android和iOS都支持,画面质量比PVR好,且不要求纹理长宽相等和2的幂次方。ASTC可能会是未来的统一格式,但目前很多Android低端设备还不支持。