一、什么是纹理压缩

纹理压缩是为了解决内存、带宽问题,专为在计算机图形渲染系统中存储纹理而使用的图像压缩技术。

1.图片格式和纹理格式的区别

(1)图片格式

图片格式是图片文件的存储格式,通常在磁盘、内存中储存和传输文件时使用;例如:JPG、PNG、GIF、BMP等。

(2)纹理格式

纹理格式是显卡能够直接进行采样的纹理数据格式,通常在向显卡中加载纹理时使用。

2.纹理管线

纹理压缩格式基于块压缩,能够更快读取像素所属字节块进行解压缩以支持随机访问;图片压缩格式基于整张图片进行压缩,无法直接实现单个像素的解析;图片压缩格式无法被GPU识别,还需要经CPU解压缩成非压缩纹理格式才能被识别。

根据老师所讲的内容,我个人倾向于把图片格式理解为顺序表,读取不随机,也就是说使用的时候要进行查找,而纹理格式则不同,它类似于哈希表,GPU可以随机访问,这样大大提高了效率。

二、常见的纹理的格式

1.非压缩格式

2.压缩格式

(1)DXTC

DXTC纹理压缩格式来源于S3公司提出的S3TC算法,基本思想是把4×4的像素块压缩成一个64或128位的数据块,优点为创建了一个固定大小且独立的编码片段,没有共享查找表或其他依赖关系,简化了解码过程。

DXT1格式主要适用于不具透明度的贴图或仅具一位Alpha的贴图(非完全透明则即完全不透明)。

DXT1将每4×4个像素块视为一个压缩单位,压缩后的4×4个像素块占用64位,其中有2个16位的RGB颜色和16个2位索引,如

DXT1中的两个RGB颜色负责表示所在压缩的4×4像素块中颜色的两个极端值(极端值颜色为RGB565格式),然后通过线性插值我们可以再计算出两个中间颜色值,而16个2位索引则表明了这4×4个像素块所在像素的颜色值,(00,01,10,11)可以表示4种状态,刚好可以完整表示color_0color_1以及我们通过插值计算出的中间颜色值color_2和color_3。而对于具有一位Alpha的贴图,则只计算一个中间颜色值color2,而color_3则用来表示完全透明。
对于如何判断DXT1格式是表示不透明还是具有1位alpha的贴图,则是通过两个颜色值color_0和color_1来实现的,如果color_0的数值大于color_1则表示贴图是完全不透明的,反之则表示具有一位透明信息。

而对比RGB24的格式,DXT1的压缩比达到了 6:1(24*16 / 64 = 6)


DXT2DXT3可以表示具有更复杂的透明信息的贴图,这两种格式采用的是显式的Alpha表示,我们知道了在DXT1中,我们使用64位数据来描述4*4的像素块的颜色信息,在DXT2DXT3中,这部分颜色信息是不变的,而是通过另附加64位数据也就是每个像素4位来表示他们的Alpha透明信息,而这4位的Alpha的信息通常情况下我们可以采用直接编码的方式来表示即可。
这样每个4×4像素块占用128位也就是8个字,0~3字表示透明信息;4~7表示前面描述的颜色的信息。
DXT2DXT3不同之处在于,

DXT2中颜色是已经完成了Premultiplied by alpha操作(已完成颜色与alpha的混合,当透明度发生改变时,直接改变整体颜色值,不必再单独复合)

DXT3的Alpha信息则是相对独立的,之所以要区分开了则是为了适应不同的需要,因为有些场合需要独立的Alpha信息。


DXT4、DXT5也是用于表示具有复杂的透明信息的贴图,与2和3不同的是:4和5的Alpha信息是通过线性插值计算所得,类似于DXT1的颜色信息。同样的,每4×4的像素块的透明信息占用64位,所不同的是,64位中采用了2个8位的alpha值和16个3位的索引值,既然每个像素的索引占3位,那么可以表示8种不同的透明状态
在这里插值的方法有两种,一种用于表示具有完全透明和完全不透明的状态,另一种则是仅在给出的极端值alpha_0和alpha_1中进行插值。区分的方法也是通过比较alpha_0和alpha_1的大小来实现的,如果alpha_0大于alpha_1,则通过插值计算剩下的6个中间alpha值;否则,只通过插值计算4个中间alpha值,alpha_6直接赋值0,alpha_7直接赋值255。
DXT4和DXT5的区别同DXT2和DXT3的区别相同,DXT4的颜色值是理解为已经完成Premultiplied by alpha操作的
另外需要注意的是,所有的压缩纹理格式都是2的幂,因为纹理压缩的单位是4×4像素,所以如果贴图的大小位16×2或者8×1这样的比例,系统会同样采用4×4的单位进行压缩,会造成一定的空间浪费,同样的大小会被占用,只是不会参与使用而已。

在Unity内贴图类型选为法线后会采用DXTnm压缩格式,它基于DXT5,该格式会把法线贴图R通道存入A通道,然后RB通道清除为1,这样可以将法线XY信息分别存入到RGB/A中分别压缩,以获得更高的精度,然后再根据XY构建出Z通道数据。

那么对于RGBA32格式,DXT2345的压缩率则为 4 :1(32/128*16)

参考:DXT纹理压缩_lhc717的博客-CSDN博客

(2)ATI1/2(BC4/5)

ATI1(BC4),主要用于压缩高度图、法线图等具有单一灰度通道的纹理贴图,采用固定32字节块的压缩方式,每个块可以压缩4×4像素纹理数据。在每个压缩块中,只需要2个红色分量存储压缩后的数据,可以用两个8位整数、一个16位的整数、或者一个12位整数和一个4位整数存储这些数据。因此,在使用ATI1压缩算法时,搭配的纹理比原始纹理小约75%,这大大减少了存储空间的需求。

ATI1尤其适用于包含高度信息的纹理(如Displacement Maps),但对最终图像制品的视觉影响很小。不过,因为它不能进行色彩太多变化的压缩,所以它不适合用于HDR纹理(高动态范围纹理)。

总体而言,ATI1能够提供比较小的文件尺寸和较低的显存占用,同时还能满足设计师对细节的需求。

ATI1的压缩率:单通道8位:(64位/16像素) = 2 : 1


ATI2(BC5),主要用于压缩法线图和切线空间贴图等具有两个灰度通道的纹理贴图,采用固定块大小的压缩方式,每个块可以压缩4×4像素纹理数据。在每个压缩块中,采用两个分离的压缩算法(RG存储分别经过压缩的正半球和负半球法向量)。然后再对编码后的R通道和G通道进行解压合成Z轴信息得到完整的归一化法线向量。每个R和G通道都可以用3个8位整数、或者一个10位整数和一个6位整数存储,使得最终的压缩结果无需外部计算即可直接读取。

相比于ATI1,ATI2增加了红色和绿色Two-Step交替方法进行压缩,获得了更高的压缩率和更好的细节表现。但由于使用了两个分离的压缩算法,所以在解压的时候还需要进行额外的操作。另外,ATI2作为一种压缩纹理格式,对于最终的图像质量还需要在算法上进行精度控制和优化平衡。

ATI2的压缩率:双通道8位(16位):(2*64位/16像素) = 2 : 1

双通道16位(32位) :(2*64位/16像素) = 4 : 1


(3)BC6/7

BC6和BC7的官方原理说明:

BC6H 格式 – UWP applications | Microsoft Learn

BC7 格式 – UWP applications | Microsoft Learn


(4)ETC

上述我们说的DirectX选择了DXTC作为标准压缩格式,那对于OpenGL则选择了爱立信研发的ETC格式,几乎所有的安卓设备都可以支持ETC压缩,所以其在移动平台上被广泛应用;

ETC方案与DXTC具有相同特点,将4×4的像素单元压缩成64位数据块,并将像素单元水平或竖直朝向分为两个区块,每个像素颜色等于基础颜色加上索引指向的亮度范围。

第一行:24位 颜色信息(RGB444 * 2 或 RGB333 + RGB555)

第二行:32位 像素索引信息

第三行:8位 亮度索引信息

ETC1针对RGB24格式进行压缩,压缩后的结果是64位数据块的16个像素。

所以ETC1的压缩比为 6 : 1

因为ETC压缩在安卓上的良好兼容性和不错的效果,目前大多数安卓平台手游都采用了ETC压缩方案,但为了解决ETC1不支持Alpha的问题,我们可以采用两张纹理混合的方式来实现带Alpha通道的压缩。

ETC1和ETC2的差别

ETC1要求长宽为2的次幂的贴图,它适用于所有安卓设备,压缩率比较高,但不适用于带Alpha通道的贴图。ETC2是ETC1的扩展,它要求长宽都能被4整除的贴图,且硬件要求OpenglES3.0和Opengl4.3以上,支持Alpha通道的压缩,但它的内存占用大于ETC1。

(5)ASTC

ASTC128位数据块中存储的信息:

11位:权重、高度信息,特殊块标识;

2位:Part数量

4位:16种插值端点模式(如LDR/HDR,RGB/RGBA)

111位:插值端点信息,纹素权重值,配置信息

详情参考:

ASTC纹理压缩格式详解 – 知乎 (zhihu.com)


(6)PVRTC

PVRTC旨在减少纹理数据的存储空间和带宽需求,从而提高性能和降低功耗。

PVRTC压缩算法包括两种版本:PVRTC1和PVRTC2。这里我们主要介绍PVRTC1的工作原理。

PVRTC1支持两种压缩比,4bpp(每像素4位)和 2bpp(每像素2位)。我们以4bpp为例:

压缩后的64位数据块中包含A,B两张图像,A和B都是在原本的基础上长和宽分别压缩了4倍后得到的低分辨率图像,A占了14位(RGB:554,RGBA:4433),B占了15位(RGB:555,RGBA:4443),同时对于16个像素,每个像素存储了2位调制数据(00,01,10,11),可以表示4个状态。同时还有一位的调制模式0/1,在不同调制模式下,4个状态可以得到不同的混合值,最终通过得到的混合值和A,B的颜色值可以得到最终的混合值,还有2位分别在A,B上表示不透明标值,用来判断是RGB存储还是RGBA存储,至于采用哪一个,会选用A和B中位数更高上面的不透明标值。

PVRTC2相较于PVRTC1有所改进,主要表现在颜色空间处理、边缘处理等方面,以获得更高的图像质量。总之,PVRTC通过将纹理划分为小块并利用颜色插值以及模拟像素技术,实现了较高的压缩比,从而降低了存储和带宽需求。这对于移动设备来说尤为重要,因为它们通常具有有限的硬件资源。

PVRTC1-4bpp的压缩率:对于RGB24,压缩率为24/4也就是 6 : 1

对于RGBA32,压缩率为32/4也就是 8 : 1

三、总结

1.画质比较

RGBA > ASTC 4×4 > ASTC 6×6 > ETC2 ≈ ETC1

2.压缩比

DXT16:1DXT2/34:1

DXT4/54:1ATI1 4:1

ATI2 4:1 BC6 6:1

BC73:1 PVRTC 6:1

ASTC 4:1~35.95:1

3.适配

英伟达和Unity官方对于不同类型贴图给出了不同的压缩方案建议,感兴趣的同学可以看下:

Using ASTC Texture Compression for Game Assets | NVIDIA Developer

Unity – Manual: Recommended, default, and supported texture compression formats, by platform (unity3d.com)

参考:

3600_纹理压缩_哔哩哔哩_bilibili

PPT:3600百人计划-纹理压缩 (qq.com)