深入探讨游戏开发中的纹理压缩
#define CC_GL_ATC_RGB_AMD 0x8C92
#define CC_GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93
#define CC_GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
CCImage的开始是这个3个宏定义,这3个宏定义其实是对于ATITC格式纹理的类型:
1、 ATC_RGB_AMD (RGB textures)
2、 ATC_RGBA_EXPLICIT_ALPHA_AMD (RGB textures using explicit alphaencoding)
3、 ATC_RGBA_INTERPOLATED_ALPHA_AMD (RGBA textures using interpolatedalpha encoding)
1、BMP: Windows标准图像文件格式,位映射存储格式,图像深度可选(lbit、4bit、8bit及24bit),不采用其他任何压缩;
2、TGA: 数字化图像,以及运用光线跟踪算法所产生的高质量图像的常用格式,支持压缩,使用不失真的压缩算法,可以带通道图,另外还支持行程编码压缩。特点是可以做出不规则形状的图形、图像文件,兼顾了BMP的图象质量的同时又兼顾了JPEG的体积优势;
3、JPG: 24位颜色存储单个位图,与平台无关的格式,支持最高级别的有损压缩,可以很好地压缩类似的色调,但不能很好地处理亮度的强烈差异或处理纯色区域,只支持YUV颜色模式的数据结构;
4、GIF: 基于LZW算法的连续色调的无损压缩格式,可变长度等压缩算法;
5、PNG: 位图文件(bitmap file)存储格式,8位、24位、32位三种形式。
要显示一张JPG格式的图片需要先解码加载(手持设备上还是比较耗电的),然后再解压缩成原始像素格式传递给显卡,在没有显卡硬件支持的前提下,使用压缩格式保存纹理是值得商榷的。正是因为现在游戏对贴图的依赖导致对显示总线造成巨大的压力,所以很多厂家都对硬件提供了实时解压缩的功能,不过可惜的是没有一个格式能够得到多个厂家的支持。OpenGL ES定义了一个标准的接口:
_API void GL_APIENTRY glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizeiheight, GLint border, GLsizei imageSize, const GLvoid* data);
不过纹理数据的格式还是没有统一的标准,导致一旦使用了压缩纹理就无法实现跨平台。
之前的纹理优化已经介绍了常见的纹理格式RGB565,RGBA4444,RGBA5551,RGB888,RGBA8888等。这里我们应该能很好地明白文件格式和纹理格式的区别了, 文件格式是图像为了存储信息而使用的对信息的特殊编码方式,它存储在磁盘中,或者内存中,但是并不能被GPU所识别,因为以向量计算见长的GPU对于这些复杂的计算无能为力。这些文件格式当被游戏读入后,还是需要经过CPU解压成像素格式,再传送到GPU端进行使用。而纹理格式就是能被GPU所识别的像素格式,能被快速寻址并采样。举个例子,DDS文件是游戏开发中常用的文件格式,它内部可以包含RGBA4444的纹理格式,也可以包含RGBA8888的纹理格式,甚至可以包含DXT1的纹理格式。在这里DDS文件有点容器的意味。
这里我们可能会疑问我们为什么不能把类似图片压缩格式的方法应用在纹理贴图上,其实这是因为显示芯片在存取贴图时,是一种「随机存取」的动作。也就是说,显示芯片通常会需要以任意的顺序存取贴图里的数据。一般的压缩方式如JPEG,都利用了行程长度的编码方法,简单的说,它们必需以一定的顺序才能解开。因此,不能用这种方式来压缩贴图。
1、改变颜色空间:例如[3dfx公司]的YAB格式,利用YAB,每个像点只需要8bits,就可以达到接近16bits的效果。不过,无论如何,这样都使颜色的数目减少。因此,整个贴图的色彩变化就受到了限制。
2、调色盘:就是类似OpenGL的索引,利用一个256种颜色的调色盘,就可以把贴图以 8 bits 的方式储存。不过,虽然它的色彩空间较大(可以是24bits或32bits),但是总颜色数目还是不能超过256种。所以,它的应用范围仍然有限。
现在常用的贴图压缩方式,则是利用以区块为基础的方式。通常的做法是,把贴图切割成许多小区块,再对各个区块进行压缩。例如,S3TC就是把贴图切成4×4的小区块。利用这种做法,就可以对区块进行某种处理(通常就是vector quantization或是其变形),显示芯片也可以区块为单位,进行随机的存取动作。因此,这是适合用在贴图的方式。
不过,区块的大小会影响到压缩的效果。一般来说,区块愈大,就能有愈高的压缩比。不过,愈大的区块也会使额外的负担增加。因为显示芯片只能以区块为单位来读取贴图数据,如果区块愈大,则每个区块中就可能会有愈多的数据是不需要的。所以,也不能任意把区块的大小加大。
在[基于已压缩纹理的渲染]论文中,列举了纹理压缩四项的特点,使其不同于其他图像压缩技术。
1、解压速度:由于最好能直接从已压缩的纹理直接渲染,为了尽可能地不影响性能,解压缩要尽可能快。
2、随机访问:由于几乎不可能预测纹素被访问的顺序,任何纹理压缩算法必须允许对其中纹素的随机访问。所以几乎所有的纹理压缩算法都以块为单位压缩和存储纹素,当某一纹素被访问时,只有同一块中若干纹素被读取和解压缩。这项需求也排除了很多压缩率较高的图像压缩方式,例如JPEG和行程长度编码。
3、压缩率和图像质量:由于人眼的不精确性,相比于其他应用领域,图像渲染更适宜使用有损数据压缩。
4、编码速度:纹理压缩对压缩速度要求不高,因为绝大多数情况下,纹理只需要进行一次压缩。
由于其数据访问模式是事先知道的,纹理压缩常作为整个绘图管线的一部分,在绘制时对动态地已压缩数据进行解压缩。而反过来绘制管线也可以通过纹理压缩技术来降低对于带宽和存储的需求。在纹理贴图中,已压缩纹理和没有经过压缩的纹理使用起来基本没有区别,都可以被用来存储颜色数据或其他数据,例如[凹凸贴图]或法线贴图,也都可以和Mipmapping或各向异性过滤等共同使用。
CPU | GPU |
德州仪器 | Power VR系列 |
三星猎户座 | Mail系列 |
高通 | Adreno系列 |
NVIDIA | GeForce系列 |
海思K3V2 | Vivante GC |
1、Imagination的PowerVR:PVRTC,ETC1
* 代表机型:AppleiPhone、iPad,三星I9000、P3100
2、Qualcomm的Adreno系列:
*Adreno 2xx系列:3Dc和ATITC(基于ATI)
*Adreno 320:ETC,3Dc和ATITC(基于ATI), ETC2
* 代表机型:HTC G10、G14,小米1、小米2
3、ARM的Mali系列:ETC
*300/400系列:
*T600系列:ASTC
* 代表机型:三星Galaxy SII、Galaxy SIII、Galaxy Note1、Galaxy Note2(亚版)
4、NVIDIA的Geforce系列:ETC,S3TC(DXT1、DXT3和DXT5)
*Tegra 2:ATITC
*Tegra K1:ASTC
* 代表机型:Google Nexus 7,HTC One X
5、Vivante的GC系列:ETC,S3TC
现在主流的纹理压缩标准:
1、ETC1:OpenGL ES2.0基本的纹理压缩标准,大部分移动GPU都会支持的纹理标准,不支持Alpha通道,只能用于压缩不透明的材质,几乎所有的安卓设备都可以支持ETC压缩的GPU加速;
2、ETC2:OpenGL ES 3.0引入的纹理压缩格式,还在改进中,除了高通的Adreno 320之外还没有移动GPU支持,补全了ETC1不支持Alpha通道的缺陷,支持更高质量的RGBA(RGB+Alpha)压缩;
3、PVRTC:PowerVR架构的纹理压缩技术,iOS端官方推荐的纹理格式,可以得到较好的显示效果但节省大量的内存空间,并且PowerVR架构中纹理数据只有在最终渲染过程中有需要才会被解压缩,可以大幅的降低芯片的带宽和处理功耗;
4、S3TC:也被称为DXTn或者DXTC,就是常见的DDS压缩纹理,无论压缩速度还是压缩比都不错,也支持GPU加速,而且是桌面显卡通用的压缩格式;
5、EAC:主要用于1-2信道数据的情况;
6、ASTC:压缩速度和质量上比S3TC要好。
这里先暂时介绍到这里,可以看出纹理压缩格式和图片文件格式有非常多的类型,每种类型都有其应用领域,后面会针对每种纹理压缩格式来做详细的分析。在实际应用过程中,我们要根据项目的实际情况,在图片质量和内存占用上找到平衡点,还要考虑兼容性,选择恰当的纹理压缩格式,制定合理的图片加载方案,从而得到理想的显示效果。