压缩纹理的必要性
首先要说一下图像文件格式和纹理格式的区别。
常用的图像文件格式有BMP,TGA,JPG,GIF,PNG等;
常用的纹理格式有R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等。
文件格式是图像为了存储信息而使用的对信息的特殊编码方式,它存储在磁盘中,或者内存中,但是并不能被GPU所识别,因为以向量计算见长的GPU对于这些复杂的计算无能为力。这些文件格式当被游戏读入后,还是需要经过CPU解压成R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8, A8R8G8B8等像素格式,再传送到GPU端进行使用。
纹理格式是能被GPU所识别的像素格式,能被快速寻址并采样。
举个例子,DDS文件是游戏开发中常用的文件格式,它内部可以包含A4R4G4B4的纹理格式,也可以包含A8R8G8B8的纹理格式,甚至可以包含DXT1的纹理格式。在这里DDS文件有点容器的意味。
OpenGL ES 2.0支持以上提到的R5G6B5,A4R4G4B4,A1R5G5B5,R8G8B8,A8R8G8B8等纹理格式,其中R5G6B5,A4R4G4B4,A1R5G5B5每个像素占用2个字节(BYTE),R8G8B8每个像素占用3个字节,A8R8G8B8每个像素占用4个字节。
对于一张512512的纹理的话,R5G6B5格式的文件需要占用512KB的容量,A8R8G8B8格式的文件需要占用1MB的容量;如果是10241024的纹理,则各需要2M和4M的容量,这对于动辄需要几十、几百张甚至更多纹理的游戏,上G容量的游戏在移动平台上是不容易被接受的(当然,还是有1、2G的大作的,里面包含了几千张的纹理)。
聪明的设计师们在想,有没有其他办法,既能表现丰富的色彩和细节,又能是最小失真的情况下,达到更小的纹理容量呢。压缩纹理格式应运而生(当然,并不是在移动平台后才有的产物)。
S3Tc:
也被称为DXTC,在PC上广泛被使用,但是在移动设备上还是属于新鲜事物。支持的GPU为NVIDIA Tegra系列。
S3TC(S3 TextureCompression)/DXTC/FXT1:S3TC是S3公司提出的一种纹理压缩格式,其目的是通过对纹理的压缩,以达到节约系统带宽并提高效能的目的。S3TC就是通过压缩方式,利用有限的纹理缓存空间来存储更多的纹理,因为它支持6:1的压缩比例,所以6M的纹理可以被压缩为1M存放在材质缓存中,从而在节约了缓存的同时也提高了显示性能。 DXTC和FXT1都是与S3TC类似的技术,它们分别是微软和3dfx开发的纹理压缩标准,DXTC从Direct6开始就提供了支持,如今被广泛用于基于DirectX的各类游戏中,而FXT1能提供比S3TC更高的压缩比,达到8:1,同时它也在3dfx的Glide版本中得到支持。
S3TC简要描述 [1-2]
S3TC纹理压缩方法基于索引的思想。把纹理按4x4个单元(像素点)大小划分为块。每个块对应一张四色查找表,表中存有两个标准RGB565格式表示的16位颜色,另外使用标准插入算法在插入两个新的颜色值,由此构成四色查找表。4x4大小的纹理块中每个单元(像素点)用两个bit(00 01 10 11)表示,每一个都代表四色查找表中的一种颜色。可以看出,实质上是利用每个单元(像素点)中的两个bit来索引四色查找表中的颜色值。
对于不透明纹理贴图,每个单元(像素点)用2个bit表示,共32bit,加上每个块需要表示两个16位的颜色,总共,每个块被编码为64个bit。简单透明纹理(单色透明)保留四色查找表中的第四个颜色来表示该单元透明,第三个颜色是给出的两个颜色的平均值。区分不透明纹理和简单透明纹理的方法是看给出两个颜色的存放次序。
S3TC也可以压缩更复杂的透明纹理贴图,每块需使用总计128bits来表示。算法可参考微软DirectX 6.0文档
S3TC简单的解码器对用S3TC压缩的块解码是非常容易的。每个单元有自己的2bit索引,使用这个索引在四色查找表中找到对应该单元的颜色。解码器所需的逻辑电路非常少,并且可以使用并行解码器以获得更高的速度。
Etc / ETC1:
ETC1格式是OpenGL ES图形标准的一部分,并且被所有的Android设备所支持。
扩展名为: GL_OES_compressed_ETC1_RGB8_texture,不支持透明通道,所以仅能用于不透明纹理。
当加载压缩纹理时,<internal format>参数支持如下格式:
GL_ETC1_RGB8_OES(RGB,每个像素0.5个字节)
ETC1的编码流程如下:
1、将图像划分为一系列 4X4 的子块。
2、对每个子块,尝试所有的编码可能性,取解码后和原block像素差值和最小的一种编码。
(1)是否flip,这个决定subblock如何划分
(2)每个subblock用哪一行差值表
(3)每个像素取哪一列的差值
注:决定好flip之后,颜色均值和是否能用diff方式已经确定,这个不用遍历。
3、合并所有子块编码。
不难看出,编码过程需要遍历所有可能性,其复杂度远大于解码,每一个 RGB 像素变成了一个精度较低的RGB均值和一个2位差值号,因此产生压缩。这种预测式表述自然本身就存在偏差,压缩损失亦来自于此。
Etc2:
OpenGL ES2.0 支持的压缩ETC1; 从opengl es3.0 开始支持ETC2纹理压缩,而这种压缩可以支持透明通道。
参考
https://baike.baidu.com/item/S3TC%2FDXTC%2FFXT1
https://blog.csdn.net/jxt1234and2010/article/details/49110345
https://blog.csdn.net/wanglang3081/article/details/8869589
https://forum.unity.com/threads/etc1-vs-etc2-texture-compression.219842/