在游戏中,纹理不仅占据大量的包体,也占据了大量的内存。传统的图片压缩格式(如 JPEG、PNG 等)虽能减少资源大小,但是不能被 GPU 直接识别,还是需要先加载到内存通过 CPU 解码,转换成 RGB/RGBA 等能被 GPU 识别的格式,才能传送到 GPU 进行渲染。
为避免这些问题,社区 Cocos Star Writer 楚游香带来针对 GPU 的纹理压缩方案,使纹理能够直接被 GPU 识别并进行渲染,它具有以下优点:
无需 CPU 解码,节省了 CPU 运算,减少耗电量;
纹理直接被传送到 GPU,避免了内存占用,提高渲染性能;
高效的压缩算法,减少了包体大小。
以下是具体操作讲解,欢迎阅读。
01
压缩纹理的原理
传统的图片压缩主要目的是存储和传输,为了尽可能的高效压缩,使用了可变的压缩比率,因此在解压时需要解压更多的像素位才能读取某个像素的位置,不适合随机和快速读取,也发挥不了 GPU 的并行处理优势。
而压缩纹理使用一个固定的压缩比率,将纹理划分成多个像素块,每个像素块包含 2*2 或 4*4 个像素,然后对每个像素块进行压缩,被压缩的像素信息存储在一个像素集合中,每个像素块的索引位置存储在一个块索引图中。
读取时,首先将纹理坐标转化为块索引值,然后在像素集合中查找对应的像素块,最后在这个像素块中找到纹理颜色值。
因为采用了固定的压缩比率,GPU 内部可以并行处理,从而快速的解压缩。与之相对的是,纹理的压缩过程发生在程序运行之前,并不在意编码速度,因此在压缩时会遍历所有可能性,找到和原始像素差值最小的编码,这也是纹理压缩耗时较久的原因。
顺便说一下,普通图片格式中,PNG 是无损压缩,JPEG 是有损压缩。而压缩纹理都是有损压缩,只是在绝大部分情况下,手机上看不出来而已。
02
常用的压缩纹理格式
手机上使用压缩纹理依赖于 OpenGL ES 的支持,OpenGL ES 2.0 本身并没有定义任何纹理压缩格式,它仅提供 glCompressTexImage2D() 方法供应用程序上传压缩纹理,压缩纹理的格式由各个 GPU 厂商定义和实现。
OpenGL ES 3.0 提供了压缩纹理标准,使各个平台都可以使用同一种压缩纹理,但市面上的设备还需要很长时间才会全部过渡到 OpenGL ES 3.0。因此,仍然需要对不同的平台和设备使用不同的压缩纹理格式。
手机游戏中常用的有以下格式。
2.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 的幂次方。
2.2 ETC2
ETC2 是 ETC1 的扩展,压缩比率一样,但压缩质量更高,而且支持透明通道,能完整存储 RGBA 信息。
ETC2 需要 OpenGL ES 3.0(对应 WebGL 2.0)环境,目前还有不少低端 Android 手机不兼容,iOS 方面从 iPhone5S 开始都支持 OpenGL ES 3.0。
ETC2 和 ETC1 一样,长宽可以不相等,但要求是 2 的幂次方。
2.3 PVRTC
Creator 中常用的是 PVRTC4+A,压缩比和 ETC 一样,iOS 全系列支持,但是 Android 不支持。另外 PVR 要求纹理长宽相等(正方形)且是 2 的幂次方,例如 1280*720 的 PNG 图片,转换后变成 2048*2048,这一点会大大增加内存消耗。
在实测中还发现转换后的图片质量不如 ETC1,存在模糊、毛边现象,对画面要求高的游戏不适合。
03
压缩纹理的使用
压缩纹理的使用非常简单,根据构建平台添加需要的格式即可,具体参见 Creator 官方文档,本文不再重复。
Creator 编辑器还提供了转换压缩纹理的选项,根据转换速度分为 Fast、Slow 等好几档,速度越慢则画面质量越好。但不管选哪个,只影响显示效果和转换时长,显存占用都是一样的。
一般情况下,显存占用就是压缩纹理的文件大小,例如文件大小是 1.5M,则它占用的显存也是 1.5M。
在设置压缩纹理格式时,目前 Creator 2.x 版本还需手动一个一个设置。如果想一次性设置所有或部分资源,记得有大佬提供过插件,当然自己写个脚本遍历修改对应的 .meta 文件也比较方便。
这里是一个我写好的脚本:
https://www.chuyouxiang.com/archives/760(一键自动化设置压缩纹理格式)
04
总结
在实际项目中的测试结果是,单图、自动图集、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 好。
OpenGL ES 3.0 开始新推出一种 ASTC 格式,Android 和 iOS 都支持,画面质量比 PVR 好,且不要求纹理长宽相等和 2 的幂次方。ASTC 可能会是未来的统一格式,但目前很多 Android 低端设备还不支持。
以上是来自社区 Cocos Star Writer 楚游香的个人经验分享,大家可根据个人项目实际情况酌情使用。
至于压缩纹理在 Creator 中的配置方法、使用方法,以及对不同平台的支持情况等,可参考【官方文档】:
https://docs.cocos.com/creator/3.0/manual/zh/asset/compress-texture.html
社区有许多大佬除了技术过硬外,独立做游戏好多年,不仅前端、后端自己搞定,还各种外服一起搞,感谢大佬们一直不吝分享自己的经验,普惠广大开发者,也希望更多的同学可以一起加入到我们的行列中。
05
直播预告
最后,本周五(3 月 19 日)晚 19:30,Cocos 引擎组放空老师将携神秘嘉宾在 B 站开展直播,带来关于《Cocos Creator 3.0 轻松玩转 npm 海量资源》、《FrontJS SDK,你的线上黑猫警长》等内容。
欢迎各位开发者准时围观,也欢迎大家提前准备关于 Cocos Creator 2.x 升级 3.0 所遇到的问题,引擎组会在直播间里为大家解答噢!期待您的参与!
最后,欢迎大家常在社区交流玩耍!