内容简介:在一些游戏类应用或者移动应用中,图片资源所耗的内存往往是整个App内存占用的大头。在早期开发过程中,为了追求更好的体验效果,往往使用了很多高清的资源,这些高清资源往往占用了很大的内存。 而对于移动端设备来说,内存是有限的,尤其对于一些早期的设备,比如第一代的iPad,RAM才512MB,如果要兼容这些老款的设备,无疑对内存的使用要变得很节制。特别对于iOS设备来说,App的内存不能无限制的使用下去,如果内存占用过多,系统在发出内存警告以后如果还无法释放出更多的空闲内存出来,后面就意味着你的App已经送上了断
在一些游戏类应用或者移动应用中,图片资源所耗的内存往往是整个App内存占用的大头。在早期开发过程中,为了追求更好的体验效果,往往使用了很多高清的资源,这些高清资源往往占用了很大的内存。 而对于移动端设备来说,内存是有限的,尤其对于一些早期的设备,比如第一代的iPad,RAM才512MB,如果要兼容这些老款的设备,无疑对内存的使用要变得很节制。特别对于iOS设备来说,App的内存不能无限制的使用下去,如果内存占用过多,系统在发出内存警告以后如果还无法释放出更多的空闲内存出来,后面就意味着你的App已经送上了断头台,有随时被系统咔嚓掉的风险。为提升App的稳定性以及执行的效率,降低内存占用是每个开发人员一直在追求的目标。
如何降图片内存
通常设计师给的图片格式有PNG、JPG、TGA等,比如png图片,一张1024*1024分辨率的图片,文件大小才几百KB甚至更小,这些图片是经过特殊编码,减少了冗余信息,降低了存储空间的占用,易于网络传输。然而这些格式的图片读到内存以后,并不能直接交个GPU进行绘制,需要通过CPU解码成位图以后,才能给GPU绘制。以32位png格式图片,分辨率1024*1024图片为例,解码成位图以后占用的内存大小为4 * 1024 * 1024 * 8 bit,占用内存为4MB。降低图片内存占用,有下面常见方法:
常见方法
- 降低图片分辨率:根据实际情况,在不影响整体体验效果的同时,适当降低分辨率。
- 减小图片每个像素的位数:比如将32位png图片转成了24位、16位,甚至更低,根据实际效果决定。
- 使用调色板技术:每个像素只是调色板的索引,大幅降低内存占用。这个只能对颜色数少的图片使用,颜色丰富的图片,颜色失真,效果很差。
- 纹理压缩:使用标准的压缩算法对纹理进行压缩。
纹理压缩
简单来说,就是将原始位图经过标准的算法进行编码压缩,压缩后的数据能够直接被GPU读取解码渲染。减少了CPU解码的过程以及转成bitmap的内存占用。因此使用纹理压缩,可有效降低内存的占用以及提升渲染效率。
常用的纹理压缩格式
DXT
DXT纹理压缩格式来源于S3(Silicon & Software Systems)公司提出的S3TC,基本思想是把4x4的像素块压缩成一个64或128位的数据块,是有损压缩方式。 S3TC算法有五种变化DXT1-DXT5,一般在Windows设备上面使用,支持的GPU: Windows\Android(Nvidia Tegra and Intel Bay Trail)。 五种算法的格式对比如下表:
ETC
Ericsson Texture Compression,是由 Khronos 支持的开放标准,在移动平台中广泛采用。它是一种为感知质量设计的有损算法,其依据是人眼对亮度改变的反应要高于色度改变。类似于DXT,ETC也是把4x4的像素块压缩成一个64或128位的数据块,也是有损压缩。
ETC有两种压缩格式:ETC1和ETC2。
ETC1
ETC1是早期推出的纹理压缩格式,也是兼容性最好的格式。基本上上能够兼容所有支持OpenGLES2.0的Android设备,但是iOS设备都不支持。ETC1不支持Alpha通道,如果需要支持Alpha,需要经过特殊的处理才能支持:将图片的Alpha通道分离出来,跟24位RGB图合成一张原来2倍大小的图,通过自定义shadder来绘制。Cocos Creator里面的做法可以参考教程: Cocos Creator 支持ETC1 + Alpha 纹理压缩 。
ETC2
ETC2 是ETC1的扩展,向下兼容ETC1,对RGB压缩质量较好并且支持Alpha通道。ETC2相比ETC1压缩质量更高,从视觉上看也是接近了原图的效果。不过ETC2的兼容性不太好,OpenGLES3.0才把它纳入标准,而且对硬件要求也较高,iOS设备需要苹果A7以上的设备才能支持,Android大部分设备都支持,但是不同厂商差异很大,经过测试,发现有些Android 6.0的设备都不支持。两种格式对比如下:
PVRTC
PowerVR Texture Compression,PVRTC格式与基于块的压缩格式,比如S3TC、ETC的不同之处是,它使用2张双线性放大的低分辨率图,根据精度和每个像素的权重,融合到一起来呈现纹理,并且2-bpp和4-bpp都支持ARGB数据。PVRTC格式压缩比高,也是有损压缩。PVRTC有2bpp和4bpp格式,2bpp每个像素2bit,压缩率很高,但是质量较差。4bpp每个像素4bit,质量相对更好一些。具体用哪个,需要根据实际情况而定。
PVRTC压缩对图片的规格要求较高,图片的大小必需是正方形而且边长必需是2的N次幂。该纹理压缩对iOS兼容性好,支持所有的iOS设备以及部分使用PowerVR GPU的Android设备。
后来Imagenation 公司推出了PVRTC2纹理压缩格式,它是对PVRTC进行的改进,添加了NPOT(非2方)和贴图集支持,而且压缩质量也有很大的提高。但是必需有PowerVR Series5XT或Series6 GPU 才支持,苹果设备没有支持,估计这个格式也是凉凉了。
ASTC
自适应可伸缩纹理压缩(Adaptive Scalable Texture Compression,ASTC)是一种基于像素块的有损纹理压缩算法,由ARM的Jørn Nystad及其他人员共同设计。在2012年8月6日,ASTC被Khronos Group正式采纳为OpenGL和OpenGL ES的正式扩展。ASTC同样是基于block的压缩方式,但块的大小却较支持多种尺寸,比如从基本的4x4到12x12,而且块的宽高也不限于pot,比如6x5;每个块内的内容用128bits来进行存储,因而不同的块就对应着不同的压缩率。兼容性方面,OpenGLES3.0以上支持,支持多数Android 高端机器以及iPhone 6以上机型。压缩率如下表:
Block Size | Bits Per Pixel | Comp.Ratio |
---|---|---|
4x4 | 8.00 | 4:1 |
5x4 | 6.40 | 5:1 |
5x5 | 5.12 | 6.25:1 |
6x5 | 4.27 | 7.5:1 |
6x6 | 3.56 | 9:1 |
8x5 | 3.20 | 10:1 |
8x6 | 2.67 | 12:1 |
10x5 | 2.56 | 12.5:1 |
10x6 | 2.13 | 15:1 |
8x8 | 2.00 | 16:1 |
10x8 | 1.60 | 20:1 |
10x10 | 1.28 | 25:1 |
12x10 | 1.07 | 30:1 |
12x12 | 0.89 | 36:1 |
Cocos Creator 纹理压缩插件化
从上面纹理压缩的介绍可以看出,没有任何一种格式是完美的,都有它使用的场景以及局限性在里面。ABCmouse是一款类游戏的儿童英语教学App,使用的Cocos Creator进行开发并发布到全平台。由于App需要兼容老款低端的设备,因此我们采用了混合压缩的方式:
- iOS采用PVR + ETC2进行纹理压缩
- Android采用ETC1进行纹理压缩
- 根据具体情况,有些图片如果无法保证质量,将采用原图而不进行纹理压缩,以保证设计需要的效果
为什么要插件化
了解Cocos Creator的开发应该清楚,每次新开发功能,修改图片或者脚本以后,如果需要看看在真机上的效果,都需要在Cocos Creator上触发一次构建。建构完成以后,会在build目录下生成一个包含所有图片的目录。如果需要纹理压缩,需要手动触发一下纹理压缩的工具,这种流程非常频繁而且过于机械化。因此,我们考虑将纹理压缩功能以插件的形式集成到Cocos Creator,在Cocos Creator构建完成以后自动触发纹理压缩,使整个流程更加 自动化 。
原理
Cocos Creator插件能力,官方有文档可以参考。关键的一步,就是监听Cocos Creator构建完成的事件 editor:build-finished
,在监听到这个事件以后启动纹理压缩。
'editor:build-finished': function (event, target) { if (packerOpened) { process.nextTick(() => { texPacker.handleNoPack(); texPacker.generateImgResourceMap(); Editor.log('开始压缩纹理...'); texPacker.startPack(); }); } else { Editor.log('纹理压缩已关闭,不进行纹理压缩。'); } }
我们也在插件中提供了一些菜单选项,可以临时打开或者关闭纹理压缩,开启关闭纹理压缩菜单:
'packtexture:opentexturepack'() { packerOpened = true; Editor.log('纹理压缩已开启'); }, 'packtexture:closetexturepack'() { packerOpened = false; Editor.log('纹理压缩已关闭'); },
纹理压缩以后,我们发现压缩以后的纹理比原始的JPG、PNG图片更大,影响到安装包的大小。因此,最后在纹理压缩完成以后再采用了gzip压缩,最后安装包不会明显增长,甚至有所降低:
packETCAplha: function (srcPath, sfile, dfile, callBack, dfile2) { let packTemp = sfile.replace(/\.[^/.]+$/, ""); let outputPath = packTemp + '.pkm'; let shell = 'etcpack ' + sfile + ' ' + srcPath + ' -c etc -aa'; exec(shell, { encoding: 'utf8', env: process.env }, (err, stdout, stderr) => { let finish = true; if (fs.existsSync(outputPath)) { fs.createReadStream(outputPath) .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(dfile)); if (dfile2) { fs.createReadStream(outputPath) .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(dfile2)); } fs.unlinkSync(outputPath); } else { finish = false; let buffer = fs.readFileSync(sfile); fs.writeFileSync(dfile, buffer); Editor.log('压缩ETC+Alpha 失败, ' + sfile); } if (callBack) { callBack(finish, dfile); } }); },
增量压缩
有些比较大的图片,压缩一次的耗时是比较长的,需要几秒钟。对于一个稍具规模的项目来说,图片数量有很多,压缩一次耗时很长,需要几十秒甚至几分钟时间。如果每次都全量重新压缩一次的话,耗费时间太久,影响开发的效率。
因此,我们根据图片的md5值进行对比,只对新增加或者有修改的图片进行压缩,无修改的图片直接使用上次压缩的结果,大大节省了纹理压缩的时间,提升了开发效率。
全量压缩,可以看出每一项压缩完耗时都较长:
增量压缩,每项检查都很快:
我们这个插件已经开源 CocosCreator 纹理压缩插件 。
总结
纹理压缩是降低图片内存占用节省GPU带宽的有效手段,对于图片占比很多的场景还是可以考虑使用纹理压缩。本文介绍了当下常见的一些纹理压缩格式,简要分析了其特点。具体选择哪种压缩格式的时候,需要结合实际情况,采用一种或者多种格式想结合的方式,获得最终想要的结果。后面简单介绍了纹理压缩 工具 以插件形式和Cocos Creator结合得例子,希望对有遇到类似问题的人有些帮助。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- OpenGL ES 入门之旅 -- GLSL纹理单元和纹理翻转解决策略
- OpenGL ES入门: 渲染金字塔 - 颜色、纹理、纹理与颜色混合填充以及GLKit实现
- WebGL 纹理详解
- WebGL 纹理颜色原理
- Unity中纹理格式探究
- 游戏制作之路(48)地形纹理工具
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Android编程权威指南(第2版)
Bill Phillips、Chris Stewart、Brian Hardy、Kristin Marsicano / 王明发 / 人民邮电出版社 / 2016-5 / 109.00 元
Big Nerd Ranch是美国一家专业的移动开发技术培训机构。本书主要以其Android训练营教学课程为基础,融合了几位作者多年的心得体会,是一本完全面向实战的Android编程权威指南。全书共34章,详细介绍了8个Android 应用。通过这些精心设计的应用,读者可掌握很多重要的理论知识和开发技巧,获得最前沿的开发经验。 如果你熟悉Java语言,或者了解面向对象编程,那就立刻开始And......一起来看看 《Android编程权威指南(第2版)》 这本书的介绍吧!