文章大部分摘自: http://kirenenko-tw.blogspot.com/2013/06/opengltexture-compression.html
一、前言
游戏场景里,贴图是影响真实性的重要因素。通常贴图越大,也就越精细,但其占用的内存空间也就更大。
貼圖大小 |
16 bits |
16 bits mipmap |
24 bits |
24 bits mipmap |
32 bits |
32 bits mipmap |
64x64 |
8 KB |
10.6 KB |
12 KB |
16 KB |
16 KB |
21 KB |
256x256 |
128 KB |
170 KB |
192 KB |
256 KB |
256 KB |
340 KB |
1024x1024 |
2 MB |
2.6 MB |
3 MB |
4 MB |
4 MB |
5.3 MB |
常用的图片文件格式有:BMP, TGA, JPG, GIF, PNG等。
不过,像JPG这种常见的图片压缩格式,对于多数应用的内存占用和显示总线带宽占用并没有带来直接的好处,因为还得对JPG进行解压缩成原始的像素,再传给显卡,而且还有加载时的解码计算负担。这是因为显卡的纹理解码硬件不理解JPG格式。所以,在没有显卡硬件支持的情况下,用压缩格式保存纹理没有什么意义,特别是对于移动设备来说,解码像JPG这种复杂格式是很浪费电的。
常用的纹理格式有:RGB_565, ARGB_4444, ARGB_1555, RGB_888, ARGB_8888等。
不论何种图片文件格式,它们都是为了存储像素信息而是用的对信息的特殊编码方式,它存储在磁盘中,或者内存中,但是并不能被GPU所识别,因为以向量计算见长的GPU对这些复杂的计算无能为力。当这些文件格式被游戏读入后,需要经过CPU解压成RGB_565,ARGB_4444, ARGB_1555, RGB_888, ARGB_8888等像素格式,才能传送到GPU里使用。纹理格式是能被GPU所识别的像素格式,能被快速的寻址并采样。
纹理格式如:RGB_565,每个像素占用:5+6+5=16 (bits),共 2 个字节。RGB_888,每个像素占 24 位,3 个字节。ARGB_8888占 32 位,4 个字节。
关于纹理格式的更多资料:
http://en.wikipedia.org/wiki/High_color
二、贴图压缩方式
对于一张 512*512 的纹理,RGB_565格式的文件占用 512 KB的容量。
计算公式为:
numBytes = width * height * bitsPerPixel / 8
|
ARGB_8888格式的文件需要占用1M的容量;如果是1024*1024,则需要更多。
(以下内容直接摘抄:)
現在一般的显卡上通常有 32MB 的显存容量。如果每个贴图都要 2MB 的話,即使不计 frame buffer 所占用的空間,也只能使用 16 张贴图。这显然是不可接受的。所以,现在的游戏通常无法使用很大的贴图。
然而,在储存一般的影像的時候,通常会使用某些压缩方式。现在常见的 JPEG 压缩,可以达到 1:6 甚至 1:12 的压缩比。如果把类似的压缩方式应用在贴图上,不就可以大量减少贴图所用的空间了吗?
不幸的是,一般的影像压缩方式,是沒有办法用在贴图上面的。因为,显示芯片在存取贴图时,是一种「随机存取」的动作。也就是说,显示芯片通常会需要以任意 的顺序存取贴图里的资料。一般的压缩方式如 JPEG,都利用了 variable length 的 coding,简单的说,它们必需以一定的顺序才能解开。因此,不能用这种方式来压缩贴图。
一种压缩方式,是改变颜色空间。例如,3dfx 的 YAB 格式,就是一种不同的颜色空间。利用 YAB,每个像素只需要 8 bits,就可以达到接近 16 bits 的效果。不过,无论如何,这样都使颜色的数目减少。因此,整个贴图的色彩变化就受到了限制。
另一种方式,就是用传统的「调色盘」结构。利用一个 256 种颜色的调色盘,就可以把贴图以 8 bits 的方式储存。不过,虽然它的色彩空间较大(可以是 24 bits 或 32 bits),但是总颜色数目还是不能超过 256 种。所以,它的应用范围仍然有限。
现在常用的贴图压缩方式,则是利用以区块为基础的方式。通常的做法是,把贴图切割成许多小区块,再对各个区块进行压缩。例如,S3TC 就是把贴图切成 4x4 的小区块。利用这种做法,就可以对区块进行某种处理(通常就是 vector quantization 或是其变形),显示芯片也可以以区块为单位,进行随机的存取动作。因此,这是适合用在贴图的方式。
不过,区块的大小会影响到压缩的效果。一般来说,区块越大,就能有越高的压缩比。不过,越大的区块也会使额外的负担增加。因为显示芯片只能以区块为单位来读取贴图,如果区块越大,则每个区块中就可能会有越多的资料是不需要的。所以,也不能任意把区块的大小加大。
|
在Beers,Agrawala和Chaddha于1996年发表的一篇影响深远的论文 基於已壓縮紋理的渲染
[1]
中,他们列举四项纹理压缩的特点,使其不同于其他图片压缩技术。
- 解压速度:为了尽可能不影响性能,解压缩要尽可能快,最好能直接从已压缩的纹理直接渲染。(所谓解压,就是把贴图转换成GPU能识别的纹理格式:RGB_565等。)
- 随机访问:由于几乎不可能预测纹理像素被访问的顺序,任何纹理压缩算反必须允许对其中的纹理的随机访问。所以几乎所有的纹理压缩算法都已块为单位压缩和存储纹理像素,当某一个纹理像素被访问时,只有同一块中的若干纹理像素被读取和解压缩。这项需求也排除了很多压缩率较高的图片压缩格式,例如:JPEG和行程長度編碼。
- 压缩率和图像质量:由于人眼的不精确性,相比于其他应用领域,图像渲染更适宜使用有损压缩。
- 编码速度:纹理压缩对压缩速度的要求不高,因为绝大多数情况下,纹理只需要进行一次压缩。(但是对解压速度要求较高。)
由于其数据访问模式是事先知道的,纹理压缩常作为整个渲染管线的一部分,在绘制时动态的对已压缩数据进行解压缩(可以把解压缩放在shader里处理)。而反过来渲染管线也可以通过纹理压缩技术来降低对显卡位宽和存储的需求。在纹理贴图中,已经压缩的纹理和没有经过压缩的纹理使用起来基本没有区别,都可以被用来存储颜色数据或其他数据,例如凹凸贴图或法线贴图,也都可以和Mipmapping或各项异性过滤等共同使用。
主流的纹理压缩标准:ETC, PVRTC, S3TC。
(此处略去一段关于三种纹理的历史介绍)
下面介绍一些贴图压缩方式的细节:
三、VQTC (Vector Quantization Texture Compression)
先看一种简单的贴图压缩方式:VQTC。
VQTC是由 NEC/Videologic 的 PowerVR 系列所使用的一种贴图压缩方式,但是因为 PowerVR 系列一直没有取得很大的成功,所以 VQTC 一直未受到重视。而现在 Videologic 最新的 Kyro 显卡芯片业不在支持 VQTC。不过,因为它非常简单,因此不妨了解一下。
VQTC 使用的是 vector quantization 的方式,所以叫做 VQTC。
压缩原理:
把贴图切成许多大小相同的区块(例如,2*2, 4*4),对这些区块做 vector quantization。例如,可以从这些区块中,找出4096个最具代表性的区块。这样一来,每个区块就只需要存放 index,也就是 12bits 的空间。
显卡芯片在读取贴图资料时,要将整个向量(即 4096 个区块)读入显示芯片中的一个向量表。这个动作对每个贴图只需要做一次。在读取各个 texel 时,则是先判断出所需的 texel 所在的区块(以2*2区块来说,就是把位置坐标分别除以 2),读取 index,再从向量表中查出区块的内容。基本上,这些动作都非常简单。
在压缩比方面,以上面的例子来说,如果压缩 256*256 的贴图,并采用 2*2 的区块大小,那么贴图就有 128*128=16384 个区块。找出 4096 个最具代表性的区块后,整个贴图需要的控件就是 4096 个区块再加上 16384 个index的空间,也就是 4096*16+16384*1.5 = 88 KB。和原来未压缩所需要的 256 KB 相比,压缩比为 1:2.9。
当然可以调整区块的大小为 4*4 或者更大,不过,这样会使的压缩后贴图的精度变低。通过调整区块的大小,和数目,就可以调整出不同精度的压缩贴图,这是 VQTC 的优点之一。
VQTC 的缺点是,不能做部分更新。简单的说,如果想要更新一个 VQTC 贴图的一小部分,几乎说是不可能的,总是需要将贴图重新压缩,或是牺牲一些品质,使用原有的向量来压缩。另外,VQTC 也不太适合处理有透明通道的贴图。
VQTC 现在已经不常用了。目前更流行的的贴图压缩,则是由 S3 所开发的 S3TC。
四、S3TC(S3 graphics Texture Compression)
S3TC 是由 S3 公司所开发出来,最早用在它的 Savage 系列芯片中。虽然 Savage 显示芯片并不算成功,但是 S3TC 却因为微软公司将其加入 DirectX 而变得相当成功。微软向 S3 授权 S3TC 技术后,其他公司在 DirectX 中支援 S3TC(在 DirectX 中称为 DXTC),则不需要再付给 S3 费用,因此得到了相当程度的支持。
S3TC 包括五中贴图压缩格式,在 DirectX 中,分别称为 DXT1 ~ DXT5。这些不同的贴图压缩格式,其实就是为了透明度(Alpha Channel)所设计的。
DXT1 是 S3TC 中,最基本的压缩格式。它是用来处理 没有 Alpha Channel 或者是 Alpha Channel 为 1bit 的贴图。其他的压缩方式都只是 DXT1 的变化而已。
压缩原理:
DXT1 所用的方法,和 VQTC 类似,也是将贴图切成许多小区块,然后再利用 vector quantization 的方式来压缩。不过,和 VQTC 不同的是,DXT1 的区块总是 4*4 的大小,而且它将 vector quantization 用在区块内,而不是像 VQTC 对所有的区块做 vector quantization。
对于每个 4*4 的区块,储存了两个 16bits 的颜色,颜色的格式是 RGB_565。从这两个颜色,可以用线性插值的方式得到另外两个颜色。简单的说,如果这两个颜色分别是 RGB-0 和 RGB-1,那 RGB-2 和 RGB-3 可以由下面的式子求出:
RGB-2 = (2*RGB-0 + 1*RGB-3)/3;
RGB-3 = (1*RGB-0 + 2*RGB-3)/2;
这样就得到了四个颜色。区块中的每个 texel 则是一个 2bits 的 index 表示。如果 index = 0,则表示这个 texel 的颜色是 RGB-0;index = 1,则是 RGB-1;index = 3 则是 RGB-3。以这样的方式存储,每个区块只需要 16*2 + 2*4*4 = 64bits。因此,在压缩 16bits 的贴图时,压缩比是 1:4.
DXT1 同时也可以处理 具有 1bit Alpha Channel 的贴图。一般来说,在贴图中使用 1bit 的 Alpha Channel,就是用来注明透明的地方。因此,DXT1 的处理方式,是牺牲了1个颜色,用它来表示透明的 texel。
如果一个区块需要存放透明度的话,那么,颜色的产生方式就变成:
RGB-2 = (RGB-0 + RGB-1)/2
和前面一样,texel 的 index 仍是 2bits。index = 0~2 时,分别是表示 RGB-0~RGB-2。当 index = 3 时,就表示这个 texel 是透明的,它的颜色是黑色,且 alpha = 0。
不过,要怎么区分 有 Alpha 和 没有 Alpha 的情形呢?DXT1 使用一个简单的方法:如果 RGB-0 比 RGB-1 要大的话,那就表示这个区块是没有 Alpha 的。相反,则表示有 Alpha Channel 了。
当 Alpha Channel 不只是 1bit 的时候,就需要其他的方式了。DXT2~DXT5 就是在处理这种情况。因为它们的原理和 DXT1 是相同的,所以就不再多做介绍了。不过,DXT2~DXT5 每个 4*4 区块需要 128 bits,也就是说,他们的压缩比只有 DXT1 的一半。
S3TC 的优点:因为它是针对每个 4*4 的区块做压缩,所以可以对贴图做 部分更新 的操作。要更新贴图中的某个部分,只需要更新这部分的区块即可,其他区块完全不会被影响。
五、FXT1(3dfx Texture Compression)
基本上,FXT1 和 S3TC 是非常类似的。只不过,FXT1 使用 8*4 的区块,而非 S3TC 的 4*4 区块。而且相对于 S3TC 只有一种压缩方式(即DXT1,DXT2~DXT5 只是将 DXT1 加上 Alpha Channel 而已),FXT1 有四种不同方式。这使得压缩的时候,可以针对不同的情形,选择最适当的压缩方式。
FXT1 有四种压缩方式,都是使用 8*4 的区块。第一种方式称为 CC_HI,用来压缩使用 1bit Alpha Channel 来表示透明度的贴图。
压缩原理:
CC_HI 和 DXT1 非常类似,它也是存放两个颜色,不过,和 DXT1 不同的是,CC_HI 用的是 15 bits 5-5-5,而非 DXT1 的 16 bits 5-6-5。
利用线性插值的方式,CC_HI 的两个颜色会插值出 5 个颜色,合起来是 7 个颜色,再加上一个颜色表示 透明texel。每个 texel 则使用 3bits 的 index 来指向这 8 个颜色。另外,CC_HI 使用 2 bits 的 mode bits 来表示这个区块是 CC_HI 格式。所以,一个 8*4 的区块需要 2+2*15+32*3 = 128 bits。因此,如果贴图是 16bits 的话,压缩比就是 1:4。
第二种方式称为
CC_CHROMA,
它是专门用来存放 没有 Alpha Channel 的贴图。基本上,CC_CHROMA 的方式很简单:它存放 4 个 15bits 的颜色,每个 texel 则用一个 2bits 的 index 指向这 4 个颜色。它的 mode bits 是 3bits,还有一个未使用的 bit,所以它需要的空间是 3+1+4*15+32*2=128bits。
第三种方式称为
CC_MIXED,
这个模式非常的复杂。它将 8*4 个区块分成两个 4*4 的小区块,每个小区块有两个 16bits 5-6-5 RGB的颜色。每个小区块中的压缩方式和 DXT1 几乎相同,但是它不是用颜色的大小来区分 是否有 Alpha Channel,而是用一个 Alpha Bit 来区分。这个模式需要的空间也是 128bits。
第四种方式称为
CC_ALPHA
,
它是用来处理具有多位 Alpha Channel 的模式(类似 DXT2~DXT5)。它使用3个 20bits 5-5-5-5 ARGB格式的颜色,并有两种模式:第一种模式,每个 texel 的 index(2bits)指向这三个颜色,而第四个颜色则是表示 透明texel。第二种模式,则是将区块分成两个小区块,并使用类似 DXT1 的方式(第一个小区块用 COLOR0 和 COLOR1 为基准色,而第二个小区块则用 COLOR1 和 COLOR2 作为基准色),只不过有加上 Alpha Channel。这个格式的 mode bit 是 3bits,再加上一个 模式 bit,所以需要的空间是 3+1+20*3+32*2=128bits。
FXT1 与 S3TC 并没有很大差别,一般来说,在压缩 24bits 或 32bits 的贴图时,FXT1效果会好一点。但是由于其区块较大,是显卡芯片在处理时的效率较差。
FXT1 和 S3TC 都有相对应的 OpenGL extension 来提供支持:
S3TC:GL_EXT_texture_compression_s3tc
FXT1:GL_3DFX_texture_compression_FXT1
六、PVRTC(PowerVR Texture Compression)
PowerVR texture Compression 最早是用于 IOS 系统,用于显示器芯片支持 PowerVR 的。
优点:相对png来说,pvr 能更节省一张图片加载后占用的内存。
缺点:缺点是压缩方式是有损的压缩方式,因此对于某些图片不适宜用pvr压缩,通常pvr格式贴图会被用于2D的动画,特效等。
下面介绍文件格式与压缩原理。
1. 文件格式
PVRTC在OpenGL ES的扩展名为:GL_IMG_texture_compression_pvrtc,支持预处理压缩。当加载PVRTC压缩纹理是,参数支持如下格式:
COMPRESSED_RGB_PVRTC_4BPPV1_IMG (RGB 4 bit per pixel)
COMPRESSED_RGB_PVRTC_2BPPV1_IMG (RGB 2 bit per pixel)
COMPRESSED_RGBA_PVRTC_4BPPV1_IMG (RGB 4 bit per pixel with alpha channel)
COMPRESSED_RGBA_PVRTC_2BPPV1_IMG (RGB 2 bit per pixel with alpha channel)
|
(敬请期待...)
七、ETC
(敬请期待...)