为毛要写这个
本来觉得像这样的问题,是无法归类的,因为不同的项目有不同的需求,但今天因为quick论坛中的一个技术疑问贴,钩起了我整理这篇文章的兴趣 http://www.cocoachina.com/bbs/read.php?tid=214811
于是,我决定尽力描述一下纹理格式选择方面的问题,一是起到一个科普的作用,因为目前没有发现十分完整的讲这方面的文章。二是整理一下自己的思路。
当然,这些东西肯定不是我自己凭空YY出来的,我也是参考了不少文章,也从项目中总结了一些问题。在此先列出一些链接,先睹为快
iOS和android游戏纹理优化和内存优化(cocos2d-x)
嗯 ,我觉得已经足够多了,并且,可能你也没有耐心把所有链接看完。 至少我没有……。
GPU纹理与非GPU纹理
很多小伙伴其实一直不明白GPU纹理与非GPU纹理的区别,以致于我打了将近1000字,他也没有懂为什么JPG不能省内存。
常见的JPG格式,是一种文件压缩格式,即它能够把图片像素值,使用一种有损压缩的方式来存储,这样图片的体积会变得非常小。
而我们经常提到的GPU纹理格式,如pvrtc,etc等,是一种GPU压缩格式,这种压缩格式,文件大小会比JPG大,但是它读入到内存后,不会被解压,而是直接送到显卡。 显卡也不会对齐解压,而是在执行如tex2D纹理采样指令的时候,取访问并索取对应像素值的内容。 这样才能够节省显存和内存。 pvrtc、etc等,既能够进行压缩,同时又提供随机访问(即在不全部解压的情况下,访问指定UV坐标的像素值)能力,因此会比jpg大许多
图片格式简介
->pvrtc
pvrtc有两种格式,一种是pvrtc 2bp,一种是pvrtc4bp。 二者没有本质区别,最主要的区别就是2bp表示每个像素使用2 bit来存储,4bp使用4bit来存储。当然这是一种理论值,由于pvrtc还需要储存额外的信息以保证能够恢复像素值,以及提供随机访问能力。 所以,实际的pvrtc格式的文件,不会是1/8和1/16这么小。pvrtc格式读入内存后,不会进行解压,而是直接送给GPU。 GPU也不会进行解压,从而保证了内存和显存的节约。 而tex2D能够对这种格式的纹理直接采样。
另外,pvrtc目前有两个大版本,pvrtc1和pvrtc2,pvrtc2对压缩算法做了明显的改进。 但需要新款的GPU才支持,所以,如果要使用pvrtc2版本的PVRTC纹理,记得检查OPENGL ES的扩展标记。
pvrtc建议只对IOS平台使用,因为这是power vr显卡专用的纹理格式。
->etc
etc目前有两种格式,etc1/etc2。 etc1是opengl es 1.x和opengl es 2.0提供的必须支持的硬件格式,其唯一的缺点就是不支持ALPHA通道。 而etc2是opengl es 3.0才支持的,且增加了ALPHA通道能力。
由于目前opengles 3.0设备的普及率还不高,目前(写这文章的时候是2014年7月18日)建议使用etc1格式
建议etc1格式使用在ANDROID平台上。
->png24/32
png24和32其实是一样的,24表示ALPHA通道不使用,因此,这是一种RGBA8888的纹理格式,这种格式不进行任何压缩,完全保持原始像素值。可以提供较高的质量,但这种纹理占用的磁盘空间较大。(与其内存占用等同),可以简单地通过 长x宽x4来计算大小。 比如1024x1024的PNG24/32纹理,占用磁盘空间和内存为 4MB。
->png8
png8十分复杂,因为png8的意义比较多。
仅包含ALPHA通道的8位PNG:这种通常拿来提供单独的ALPHA通道能力,opengl d3d等均支持这种8位ALPHA纹理。
256色调色板的PNG:这种通常见于网站图标等,它只有一位表示ALPHA,即镂空效果。且不适合用在需要表现丰富和高饱和度的场合。
通过一些压缩方式得到的压缩PNG:如grunt-png8,tinypng等, 这些工具,使用了较高的压缩技巧,使PNG24/32的RGBA可以保存在8位通道中。 且能够提供较高的还原度。 这种和PNG一样,读入内存后,每个像素依然占4BYTES。
->jpg
jpg是一种通过有损压缩算法,使原始图片可以以很小的文件来存储的格式。 这种和上面提到的png8十分类似,但JPG不支持ALPHA通道。 虽然不支持ALPHA通道,但它读进内存后,内存占用依然是每个像素4BYTES。
另外还有一些dds,tga等,由于我个人在手机游戏开发方面涉及这方面较少(端游使用tga和dds较多),在此不作论述。
如何选择纹理格式
人生之所以纠结,在于许多事情你可以选择
---纠结帝·麒麟子·Alex
上面的纹理格式,同一种情况下,可能多种都适合,那如何选择呢。 我们还是根据具体情况而定。
1、场景、背景、全屏图片
2D手机游戏中,多半都有这样的图片,以作为背景,特别在一些SLG,横版过关游戏中。这种图片对ALPHA没有要求,并且,在同一时间,只会出现一张(如果是多张拼接,也不会超过屏幕尺寸太多),内存不会成为关键点。所以,在这种情况下,我们大胆选择JPG就可以了。
2、场景的前景,装饰物,可移动对象
这种要看规模,如果规模较小,类型不多。 或者类型虽然多,但同一时间出现在场景中的类型不多,那我们可以选择压缩PNG8的方式,它支持ALPHA通道,文件又小。
如果同屏可能出现多种这种,则需要考虑在IOS上使用PVRTC,在ANDROID上使用ETC1+ALPHA_MASK
3、UI
UI的背景图,可以优先考虑使用压缩PNG8,如果达不到精度要求,则使用PNG32。而对于UI的小元素,可以考虑使用压缩PNG8.
对于UI的图标,一般是不带ALPHA的PVRTC/ETC + 一张公共的ALPHA掩码图,通过双层混合来实现圆边效果。 因为图标同屏出现可能较大。 如果图标能够控制在一定范围内,由于图标是48X48等大小,一张1024x1024的大图,可以放400个图标。 换用JPG,也有4MB的开销,如果这个是可以接受的,也可以使用JPG+ALPHA_MASK的方式。
写到这里才发现,其实只需要下面一句话就可以搞定。
这类图片会不会同时出现多个,同时出现时,内存开销是否无法接受, 如果确实无法接受,则使用GPU纹理,否则,优先考虑JPG,JPG+ALPHA,或者PNG8
就是说,首先要减小安装包大小,如果内存有无法接受的情况,才需要用GPU纹理进行优化。而我们在优化的时候,最好是对某一类图片进行统一处理。 比如场景图片,如果决定了使用JPG,那就清一色的JPG。
纹理的出图和处理
这么多格式,如果让美术来出图,不是要折腾死么。
因此,只需要规定,美术出图只给PNG(有些可能会是TGA)即可,剩下的事情,程序自己写脚本进行解决。 这样可以保证美术出图的规范性,同时也避免了程序在优化过程中,切换纹理格式带来的重新出图问题。
PS:pvrtc和etc都可以使用pvrtex tool进行转换, etc的转换也可以使用mali的工具,看各人爱好了。 pvr的工具不管是下载,还是查看文档,都需要注册,就不给链接了……。
结束
或许你会说,JJYY半天,没有带点实际的。
这句话真的挺嘲讽的,但我真的打算结束了,一是我想要说的,就是这些,二是,我没有打算写一个如何在移动平台上使用各种纹理的文章。
希望这篇文章能够给迷茫的兄弟带来灵感。