纹理压缩简介 DXT PVR ETC
在软件开发,特别是三维应用中,纹理随处可见,但受限于网络环境和硬件能力,纹理也是一大瓶颈。而且在一般的三维应用中,纹理所占大小基本都会在1/2以上,模型中往往超过2/3。或许你会说,纹理不就是一张图吗,有那么重要吗?如下两张对比图,可能你会认为前者逼格高,但对于正常人而言,后者显然要好很多。正是有了纹理,如同在骨架上赋予了皮肤,让我们的应用更加的逼真,贴近现实。
而你能想象到吗?如上的模型一共有三张纹理,其中之一效果如下:
看上去怪怪的。其实在纹理的压缩中,人们先想到了如何去除冗余信息,对称的部分只保留一份,尽可能让不同的部分紧凑,充分利用好每一个像素来保存有效数据。得益于对称在大自然中的普遍性,这种方式确实极大的减少了纹理像素。
纹理的拼接是纹理压缩的开始,采用不同的压缩方式对纹理最终的大小影响也是显著的。比如上面的这张纹理在不同压缩格式下的大小差别也是非常显著的(原始文件为tga格式,通过Photoshop转换为其他格式,默认选项):
如果按照上面表格的逻辑,用jpg或png格式就好了,无损压缩,而且也是最小的。(注:原作者在评论中承认笔误,jpg实际上是有损压缩)
确实在很多情况下这是一个比较好的选择,比如网络带宽有限,这样可以很好的节约带宽和下载时间,而Bmp,tga,png以及jpg都是无损格式。但这类压缩存在一个致命缺陷,他们都是基于整幅图片下进行的压缩,比如霍夫曼编码等,这样像素和像素之间在解码的过程中存在依赖关系,无法直接实现单个像素级别的解析,这就发挥不了显卡的并发能力,更重要的是问题在于无论是png还是jpeg最终在显存中解码后都是RGBA的纹理格式,因此并无法减少显存的占用率。比如一张256256的RGBA纹理,无论是png还是jpg格式,虽然文件大小不一样,在显卡中的大小仍然是256256*4的显存空间。
不同于png、jgp这种硬盘压缩方式而言,DXT,ETC等纹理压缩方式可以在游戏运行中无需CPU解压就被GPU直接采样,可以极大的减少内存和带宽的占用,提升运行效率,对移动游戏而言更是如此。
1.DXT
DXT是一种有损纹理压缩算法,微软的Direct中支持,DXT的格式包括DXT1~DXT5,其中DXT1和DXT5较为多见,后面会做详细讨论。可以说DXT是目前应用最广泛的纹理压缩格式,可以认为所有的PC端显卡都支持DXT压缩,*记录,该专利有效期到2017年10月2号。
DXT算法非常容易理解,而且整体看上去效果不错,但如果对局部特写,会发现在细节上会有很多丢失,这也是算法本身导致的,毕竟每个块只有两个颜色,而其他颜色都是在这两个颜色区间的差值,如果当前区域内还有其他显著颜色则必然会有丢失。
这种信息的丢失主要集中在比较细的边界中,但DXT1在压缩率上是RGB的6倍,这种问题可以通过提高纹理分辨率的方式来解决,高宽放大41%(1.41*1.41=1.9881),这样整个纹理是以前的2倍,但压缩率还能保持为3倍,也是可以接受的。在DXT中还有一个主要的损失,就是RGB的24位转为了16位颜色,16位中R&B各占5位,但是G占了6位,这是因为人眼对绿色最为敏感。
另外一个问题就是DXT3和DXT5之间的对比,相比DXT1不支持透明度(但支持是否透明),DXT5要大一倍(多了64bit),和之前颜色保存方案一样对透明度也保存了两个16位的颜色和对应的调色板,对RGBA的效果也得到了保证,但DXT3思路不一样,它是对每一个像素保存了4bit的透明度,同样也是多了64bit,但此时毕竟只有16个透明度选项,相比DXT5,在压缩率上相当,但对透明色的处理不够细腻,因此在实用性上并不推荐DXT3。
尽管DXT在细节上有明显硬伤,在总体效果不错,而且确实是一种强大的压缩方式,所以在多数纹理压缩选择中都是最佳方案,几乎可以认为是PC下的标准压缩格式。
2.PVR&ETC
也许是出于专利和商业角度,也许确实DXT在移动端确实无法满足要求,DXT并没有在移动端得到很大的支持,相反,在iOS设备中支持的是PVR压缩,在Android中支持的是ETC压缩。
DXT在细节上缺陷明显,最重要的原因是当把纹理分为4*4像素的区域块后,每个块之间都是独立的,尽管这极大的简化了压缩算法,但却丢失了相邻块之间这种普遍的相似性。这是算法本身导致的,而PVR则会考虑该区域块对应的右侧,下侧和右下侧的三个区域块的关联性。
从现实的角度来看,受制于专利和硬件厂商,我们并没太多选择的余地,Android下就要用ETC,iOS下只能PVR,而在PC上不用DXT估计就要被嘲讽了。但这也是一个很棘手的问题,比如在WebGL下,特别是Android下差异化很大,是否支持纹理压缩,甚至在同一个设备不同的浏览器,因为驱动的不一致,可能系统自带的会支持ETC压缩,而微信等QQ浏览器下并不支持。而且华为的手机貌似在浏览器级别下都不支持ETC(硬件支持,还是驱动的问题)。而如果在移动设备上不用压缩,显存是有限的,除非你在数据量上做出牺牲,怎么解决都很矛盾,相比而言,iOS下则要舒服很多。
格式 | 压缩比 | GPU支持 | 描述 | 图片要求 |
---|---|---|---|---|
DXT | DXT1:0.3/DXT5:0.6 | Windows\Android(Nvidia Tegra and Intel Bay Trail) | 分为DXT1-DXT5这五个级别,DXT1 适用于不具有透明度或者仅具有一位Alpha的贴图,DXT3和DX5支持包含4位alpha通道的RGB纹理 | 无 |
ATC RGBA/RGB | RGBA:0.25/RGB:0.125 | Qualcomm -Adreno | 高通GPU支持格式,支持带有Alpha的RGB纹理压缩。 | 无 |
PVRTC RGBA/RGB | 2bit:0.125/4bit:0.25 | PowerVR | IOS平台都支持,支持每个像素2位或者4位的纹理,包含或者不包含alpha通道都可以;PVRTC 2-bpp把一个8×4的像素单元组压成一个64位的数据块,压缩效果比较差;PVRTC 4-bpp把一个4×4的像素单元组压成一个64位的数据块。游戏中使用4位压缩更多。 | 尺寸为2的N次幂,并且宽高相同。 |
ETC1 RGB 4Bit | 0.125 | 支持Opnegl ES2.0的GPU | OpenGL ES2.0版本支持,移动GPU均支持的一个格式,遗憾的是不支持Alpha通道。ETC1把一个4x4的像素单元组压成一个64位的数据块。游戏开发中采用最多的格式,不过麻烦的是需要对Alpha通道进行单独存储,Unity5.4.3版本之后提供了官方支持 | 尺寸为2的N次幂,长宽可不同 |
ETC2 ARGB/RGB 4bit | RGBA:0.25/RGB:0.125 | 支持Opnegl ES3.0的GPU | OpenGL ES 3.0以上才支持,补全了ETC1不支持Alpha通道,支持更高质量的压缩。虽然如此,从Android官方数据来看,还有相当大的设备是采用Opengl ES2.0;使用需要谨慎,不过随着设备更新换代,开发时间周期比较长的游戏可以考虑直接使用 | 尺寸为4的倍数 |
Unity官网对每个平台默认的纹理压缩格式以及使用建议给出了详细描述,需要注意的是:在不同移动GPU平台下选择GPU支持的压缩纹理,就可以在不需要CPU解压的情况下直接被GPU采样,节省CPU内存和带宽,也可以节省存储的体积。
如果目标平台不支持设置的压缩格式,纹理将解压为RGBA32或者RGB24,浪费CPU时间和内存。