纹理压缩技术详解
纹理压缩技术游戏中重要且常用的技术,最近在做纹理压缩相关的工作,正好深入的学习下纹理压缩技术的底层原理。关于纹理压缩技术有一篇非常全面的综述论文《TEXTURE COMPRESSION TECHNIQUES》, 对应有一篇中文的翻译文章。本文是上述文章的学习总结。
纹理压缩背景
游戏中使用纹理是把二维图像映射到三维表面,图像中单个像素叫做Texel。游戏中使用的贴图不仅可以存储颜色,还可以存储法线,高度等信息,贴图需要占用大量的内存,游戏中超过一半的内存被纹理占用,而且纹理大小也会对带宽造成影响,直接影响耗电,因此需要使用纹理压缩,对内存,带宽和耗电同时进行优化,尤其在移动端设备上尤为重要。
通常情况下纹理是一张二维的图像,但是传统的压缩算法(RLE,LZW等)却和流行的贴图压缩格式(JEPG,PNG等)并不适合纹理压缩,主要原因是贴图需要随机访问Texel,即只需要访问用到的纹理部分。而传统的图片压缩是需要针对整个纹理进行解压的。

因此大多数的压缩方案都会将原始的图片分割为固定大小的块,称做Tile,然后针对每个Tile进行独立的压缩。在评估一个纹理压缩方案的时候主要考虑以下几点:
- 随机访问:GPU需要能够高效的随机访问任何的Texel。
- 压缩速度:需要低成本的硬件解码,同时无间接的内存访问,理想情况下只需要单次访问内存来获取单个Tile的全部信息。
- 高压缩率:压缩率通常使用bpp(bit per pixel)来表示,也就是平均到一个pixel使用的比特数量。
- 图像划分为Tile: 常用的Tile大小是4x4像素,因为比44小的块比较难以压缩,比4x4大的块会影响*缓存命中率,同时为了减少延迟,我们需要压缩块的bit数能够小于内存的总线位宽,如果压缩块的比特数能够与总线的宽度相同的位数,那么可以避免流水线停顿。
- 人眼可接受的视觉质量:这点其实很重要,我们希望在相同的压缩率下,压缩算法导致的失真能够尽可能的小。
纹理压缩算法
目前游戏中常用的纹理压缩格式包括:
- S3TC系列,也称BC系列,在PC平台使用。
- ETC和PVRTC系列,在移动设备使用。
- ASTC系列,跨平台方案。
早期的编码采用调色板索引的方式,就是每个像素记录的是调色板中的颜色的索引值,而不是真实的颜色值,这样做的好处是可以减少每个像素记录的数据。但是由于调色板颜色的数量限制,压缩的图像质量不会很好。而且读取内存的次数增加(从直接读取颜色变成先读取索引,再读取调色板),增大了带宽。
于是使用了一种新的块压缩的算法,就是将原始的图片分为一个个的块(Tile),每个压缩块包含解码所需要的所有信息,因此单次读取即可进行解压。
最早的块压缩方案是块截断编码(BTC),用于压缩灰度图像。该算法的核心思想是对于每个块,保持块内所有像素的均值和方差不变,计算出两个值“a”和“b”,然后每个像素则使用0和1编码来表示“a”或者“b”。如下图所示,A表示原始的块的数据,计算出“a”和“b”的值分别为114和177,B为编码的结果,C为通过解码的结果。

这里的“a”和“b”的计算方式如下,然后每个原始的pixel编码为0或者1则是根据像素值大于均值还是小于均值,m是texel的数量,q是“1”的数量。

简单分析下,以上的每个Tile,需要保存“a”,“b”和编码值,“a”和“b”均为8bit,编码为1bit,总共8 + 8 + 16 = 32bit, 所以每个pixel占用2bit,也就是2bpp。对于彩色图像,则RGB每个通道单独计算,则是6bpp。由于每个block只有两种颜色可选,解压之后的效果会受到比较大的影响。具体效果图如下:

S3TC系列算法
S3TC(S3 Texture Compression)最初由S3公司开发并获得专利。微软在Direct6.0中采用了S3TC,并命名为DXT1,对Alpha通道的修改方案被称作DXT2-DXT5,总的统称为DXTC。从DX10开始,这些格式被称作BC1-BC3(Block Compression,块压缩),后续还推出了BC4和BC5,随着DX11的发布,出现了两种额外的格式BC6H和BC7,BC6H是第一个针对HDR纹理的压缩格式,而BC7则是专为高质量压缩而设计的格式。以上所有的S3TC系列的压缩都使用的4x4大小的Tile。接下来针对每种格式进行专门介绍。
BC1 (S3TC/DXT1)
BC1的块压缩由两个基色c0和c1和一个索引表构成,如下图所示,索引表中每个pixel由两个bit,因此可以索引4种基色,c0,c1,c2,c3可以被视作Tile内部的调色板,基色的格式为RGB565,每个颜色16bit,每个块总共16 + 16 + 16 * 2 = 64bits,因此压缩率为4bpp。

BC1的Tile有两种类型:支持Alpha和不支持Alpha。
对于不支持Alpha的类型,则有以下的两种计算基色c2和c3的公式,大多数情况下是使用的公式2.1计算的,如果假设Tile内是正态分布,则2.2的误差更小。

对于支持Alpha的类型,每个Texel只能是完全透明或者完全不透明,这种模式被称作穿透Alpha模式,对于这种类型的块,则c3的计算公式如下,c2则直接表示完全透明。

在实践中,需要一个额外的位来区分第一种和第二种类型的块,这个是使用数据冗余来完成的。因为块中的c0和c1的顺序对于计算是无关的,因此规定,如果c0的值大于c1,则使用第一种类型解码,否则使用第二种类型。
BC1压缩中,如何寻找Tile的基色c0和c1是减少视觉损失的关键。
BC2(DXT2/DXT3)
BC1的压缩格式只适用于24bit的RGB纹理,但是不适用于RGBA8888的32bit纹理,这些纹理的alpha通道记录着不透明度或者其他信息。BC2和BC3格式被用于这类的纹理,BC2的块占用128bit,是BC1的两倍,因此BC2的压缩率为8bpp。BC2中一半的数据是预留给4bit精度的Alpha值,另一半的数据就是用于存储RGB的BC1的数据格式,颜色通道的解码方式也与BC1相同,唯一的区别是它总是以第一种类型来进行处理,因为不需要记录Alpha了。

半透明的纹理,其颜色值需要乘上Alpha通道的透明度因子。DXT2格式中的颜色值已经预乘上了Alpha,DXT3块的布局与DXT2相同,但是颜色没有预乘。DXT2和DXT3的解码过程相同,格式名字仅用于区分颜色预乘Alpha的情况。
执行纹理过滤之前,颜色值总是预乘Alpha的,否则过滤结果不正确,预乘Alpha的格式更加的方便。
BC3(DXT4/DXT5)
BC3的压缩格式和BC4相同,都是两块64bit组成:第一块存放alpha,第二块存放颜色。其中颜色块的数据格式与BC1相同,但是存放透明度数据的块采用的是压缩格式,如下图所示,Alpha通道存储两个8bit的Alpha值端点,然后每个pixel有一个3bit的调色板索引,用于查找8个Alpha调色板的值。

BC3与BC1一样,也是使用数据冗余的方式来指定编码模式,如何Alpha0 > Alpha1,则通过线性插值计算调色板中其他的6个值,否则,只有4个值被插值,剩下的两个值记录被允许的最大值和最小值。其中BC1的块跟BC2一样,总是被视作第一种类型。

与BC2中的DXT2/DXT3的区别一样,BC3中的DXT4和DXT5的区别也在于颜色值是否预乘了Alpha,DXT4存储的颜色值预乘了Alpha,而DXT5则没有。
BC4(ATI1/3Dc+)
BC4是BC3的Alpha部分,主要用于编码单通道纹理,例如高度图等。

BC4的解码与BC3的Alpha部分相同,但是单通道的数据通常使用[0,1]或者[-1,1]的范围数据表示,因此在BC4中的端点颜色便按照这种格式来被设置为浮点数。
BC5(ATI2/3Dc)
BC5的格式是专门用于压缩法线贴图的,法线贴图记录了每个Texel的法线信息,是以颜色的RGB通道来表示XYZ,这些值的范围在[-1,1]。BC1中压缩颜色时,RGB通道是共用索引的,耦合到了一起。但是法线的各个通道是不相关的,而且本地调色板的大小较小,BC1压缩来压缩法线的话会限制梯度,压缩的效果很差。如下图所示,BC5和BC1的压缩效果对比。

由于法线是归一化的,因此实际上我们存储法线的时候,只需要存储其X和Y分量即可,Z分量是可以通过$z = \sqrt{1 - x^2 + y^2}$来计算得到,因此BC5的压缩算法实际上是两块BC4的叠加,每块分别表示X和Y通道的值,如下图所示:

每个子块的解码过程与BC4的解码过程完全相同,默认情况下是不计算Z值的,因为BC5可以压缩存储任意的双通道贴图,Z值的计算是在着色器中完成。
BC6H
导致BC1压缩质量差的原因有几点:
- 颜色顶点精度太低(使用的RGB565),而且RGB三色不均会引起颜色偏移。
- 局部调色板的颜色太少,只有四种。
- 所有的四种颜色在颜色空间是在一条线性映射上,如果原始的块中的颜色分布不均的话,则压缩质量会很差,如下图所示:

在BC6H压缩编码中通过提高端点精度和存储多达三对颜色端点来解决,该格式每个块使用128bit,也就是8bpp。根据块类型不同,每个块有一组不同的字段,每个字段的大小也不同。BC6H的块类型增加到14个,块类型在压缩块的第一位设置,块类型被称作块模式。两分区的块和三分区的块都有64个不同的子集,为了指定块属于哪一种分区,需要在块中存储子集的ID。以下显示了2个区域和3个区域的前八个分区的子集。

和之前的压缩格式相同,pixel的索引表指定端点的混合比例,分区ID则表示Texel使用那个端点对,如上图所示,A0-A1的端点对用于子集0,B0-B1的端点对用于子集1。每个pixel的索引可以是2-4位,也就是可以索引的颜色数量从4到16(包括端点)。
BC6H中的插值权重是严格定义的,如下图所示,indexprecision是单个索引的大小,可以是2,3或4位。

在DXT1中,数据冗余用于编码块类型。在BC6H / BC7中,相同的技巧用于减少索引表的大小;每个端点对编码一位。例如,考虑texel 0的索引,它是texel的左上角。如果它的最高位是1,那么可以交换相应的端点,这个位变成0。因此,总是可以重新排列端点,使得该子集中的某个索引的最高位为0。这种索引称为锚索引,并且其最高位永远不会存储在压缩块中。

BC6H旨在压缩具有高动态范围或者HDR的纹理,只支持没有Alpha的RGB图像。BC6H格式的数值可以是带符号或者无符号的16bit浮点数,因此解码器能够在带符号和无符号模式工作,然而解码后的值总是带有符号的半浮点数,解码后的值作为32位浮点数返回给着色器。

共有14种块类型如下图。BC6H只支持一区或两区块。PB字段(分区ID)始终为5位。因此,只有第32个分区集可用。在大多数情况下(对于模式10和11除外),使用Delta编码存储色彩端点:直接存储一个完整精度的端点,存储所有剩余的端点相对于完整端点的偏移值。

BC7
BC7 是一种用于 RGB 和 RGBA 数据的高质量纹理压缩格式,每个块的大小为 16 字节(128 位),对应 4x4 的纹素区域。其压缩原理和适用场景如下:
压缩原理:
- 模式选择:BC7 可以指定 8 种模式之一,模式在 16 字节块的最低有效位中指定。模式值确定每个块内插终结点对的数目。
- 终结点编码:BC7 块可以包含多个终结点对,终结点表示形式可能采用 “RGBP” 形式,其中 “P” 位表示终结点颜色组件共享的最小有效位。例如,“RGB 5.5.5.1” 表示终结点被解释为 RGB 6.6.6 值,P 位的状态定义每个组件的最小有效位。
- 索引存储:对于每个纹素,BC7 存储一个调色板索引,该索引指向由终结点定义的颜色调色板中的颜色。编码器会通过选择合适的终结点顺序,使得指定 “修复” 索引的索引将其最有效位设置为 0,从而为每个子集保存一位。
- alpha 通道处理:对于不显式编码 alpha 组件的 BC7 块,alpha 组件被解码为 1.0;对于具有组合颜色和 alpha 组件的 BC7 块,alpha 组件值与颜色分量值一起内插;对于具有单独的颜色和 alpha 组件的 BC7 块,RGB 矢量和标量 alpha 通道单独编码。
适用场景: - 高质量纹理:BC7 是专门针对 LDR(低动态范围)图像设计的压缩算法,适用于需要高质量压缩的 RGB 或 RGBA 纹理,例如游戏中的角色纹理、场景纹理等,可以在压缩后保持较好的视觉效果。
- 法线贴图:该格式用于高质量的 RGBA 压缩,可以显著减少由于压缩法线带来的错误效果,因此非常适合法线贴图的压缩。
- 带透明通道的纹理:BC7 能够很好地处理 RGBA 数据,对于带有透明通道的纹理,如玻璃、树叶等半透明物体的纹理,BC7 压缩格式可以在保留透明信息的同时实现有效的压缩

ETC系列算法
ETC(爱立信纹理压缩)格式最开始是在移动设备中使用,现在是基于Android设备的标准压缩方案。ETC系列的第一个版本PACKMAN被称作ETC1。
ETC压缩的主要思想是基于人眼对亮度比色度更敏感。因此ETC中每个子块只记录一个基色,每个pixel记录亮度偏移信息,然后结合这两者获取最终的颜色。

PACKMAN
PACKMAN使用的是2x4的块,而不是4x4,这是因为一个压缩块占用的比特数与总线的宽度相同时,可以避免流水线停顿,简化了硬件的实现。因此PACKMAN的块使用32bit,压缩率位4bpp,等于BC1的压缩率。
PACKMAN中每个块只存储一个RGB444的基色,其他的颜色是通过改变Texel的亮度来获得。解码过程首先回复四个颜色的调色板:首先通过亮度的索引(4bit)从亮度查找表中找到四个颜色的亮度变化,然后应用于基色。恢复出调色盘之后,通过pixel的索引来指定每个pixel编码的颜色。亮度查找表的前半部分如下图,后半部分是前半部分的两倍缩放。

ETC1(iPACKMAN)
单个低精度的颜色RGB444是限制PACKMAN编码效果的主要因素。ETC1编码中使用的块大小为4x4,由两个子块构成(2x4或者4x2)。其中第一个子块的基色以RGB555精度存储,而第二个基色则存储3为的插值,如果两个基色都是RGB444的块称为Individual模式。亮度码则使用3bit来索引8种亮度偏移。最后的两位:diff用来指定块类型,flip指定块的垂直或水平排列的方式。

ETC2
虽然ETC1差分和旋转模式提高了质量,但是由于每个块中只有一个基色可用,在色度变化较大的块里,质量损失也比较大。ETC2使用额外的块模式来解决这个问题,这些新的模式是用ETC1中无效的块编码实现,使得ETC2完全兼容ETC1。
当基色R0和偏移量dR1的和溢出了有效的5bit的范围[0,31]的时候,无效的组合出现在差分块中,这些数据块使用一种新的模式进行解码,导致溢出的R0和dR1的所有组合如下:

这种块中64位的1位用于diff位,8位用于R0和dR1的值,剩下55位,可以使用R0和dR1的两个低位来编码附加信息,总共有效载荷是59位。绿色通道中的溢出可以来编码另一种模式,但是有效负载将更少,因为需要保证在红色通道中没有溢出,否则被视作前一种,只需要花1bit,因为R0高位如果不相等,则保证没有溢出。这种模式下共58位,蓝色通道溢出则提供了57位。以下是所有的块的模式:

与单独和差分模式类似,T块和H块中的最低32位用于索引表。 但是本地调色板以完全不同的方式恢复。两种颜色A (r0, g0, b0)和B (r1, g1, b1)以RGB444格式存储在压缩的T块中。 其余3位对d值进行编码。A和B颜色解量化为RGB888。然后,其他两种局部调色板颜色计算为 C0 = (A - (d, d, d)) 和 C1 = (A + (d, d, d))。因此,本地调色板颜色在RGB空间中形成T形如下图。这种模式对块很有用,其中大部分点位于一条线上,但一些纹素有不同的颜色。需要说明的是,距离d是间接存储的:3位字段用作查找表(LUT) {3,6,11,16,23,32,41,64} 的索引,T块和H块相同。

H型与T型非常相似。有两种颜色A和B,以及存储在块中的d值的索引。但是C0、C1、C2、C3颜色在本地调色板中使用,而不是A、B、C0、C1(参见图24)。这些颜色在RGB空间中形成H字母形状。此模式适用于编码块,其中颜色位于两条线上。 但是,H块的有效载荷比T块少一位,同时必须存储相同数量的数据(两种RGB444颜色和一个用于d的 LUT 索引)。 这个问题可以通过BC1中使用的相同技巧来解决;由于H形是对称的,因此可以交换A和B颜色并对丢失的位进行编码。

PVRTC系列算法
PVRTC(PowerVR Texture Compression)是为PowerVR图形核心设计的,被用于苹果的移动设备。
PVRTC算法将整个图像分为高频和低频信号,低频信号由两个低分辨率的图像A和B来表示,在两个维度上分别缩小了4倍。高频信号是一个全分辨率但是低精度的调制信号M。解码的过程是:首先按照比例放大图像A和B,然后使用调制信号M进行混合,该调制信号制定了每个纹理像素的混合权重。与其他的压缩方案不同,PVRTC受益于跨块的颜色的相似性,因此PVRTC原生支持平滑的渐变。

PVRTC 4bpp
为了不分别访问三个不同的图像,PVRTC将所有的数据以64bit存储在一个块中,每个块由图像A的一个像素和图像B的一个像素以及对应的4x4的调制系数组成,如下图:

A和B颜色任何一种都可以使用RGB或者RGBA存储,两个颜色字段中的最高位决定使用哪种格式。具体可以表示的格式如上图所示。由于A和B贴图在解码的时候需要放大,因此PVRC块解码需要访问相邻的四个块的信息,然后使用双线性滤波器放大,最后每个像素的索引值对A和B图像以权重来混合。

模式位(如下图)用于穿透alpha模式。这允许在不损失RGB通道精度的情况下对1位alpha通道进行编码。当Mode-bit为“1”时,索引“10”为完全透明值保留。

注意到了,PVRTC中解码每个块的时候需要访问到相邻的四个块,似乎会影响性能,但是这种负面的影响可以被纹理缓存所减少,如果命中缓存的话,访问相邻的块只需要很少的内存访问时间。实际上,访问相邻的块在纹理的滤波中也会被需要,所以最坏情况下任何的压缩算法的解压都需要访问相邻的纹理块。
PVRTC 2bpp
PVRTC格式具有2bpp的非常高压缩级别的模式。它类似于4bpp模式并使用相同的块布局。然而,图像A和B在水平维度上进一步缩小了两倍。因此,32位调制字段必须保存8x4纹素的调制信息。模式位现在指定调制数据编码。如果它是“0”,那么每个纹素的1位索引存储在调制字段中。否则,将存储2位索引,但仅存储以棋盘图案排列的一半纹素。剩余纹素的调制值是通过平均相邻的两个或四个调制值来计算的。

ASTC系列算法
ASTC(Adaptive Scalable Texture Compression)由ARM和AMD联合开发,所有的ARM图形内核都支持ASTC的硬件。ASTC的开发基于以下的一些要求:
- 从1到4分量纹理支持。虽然可以使用BC7、PVRTC2或ETC2存储单通道纹理,但对于空通道会浪费大量位。
- 在不相关通道的情况下可接受的质量。 这对于法线贴图和 RGBA 图像很重要。
- LDR和HDR支持。BC6H可用于HDR纹理压缩,但它不支持alpha通道。
- 跨平台支持。PVRTC仅仅支持iOS平台,BC6H/BC7不支持移动设备,ETC不支持桌面GPU。对于跨平台开发人员来说,这是相当不方便的。
- 比特率/质量比的灵活性。根据纹理类型,不同级别的压缩伪影是可以接受的,因为可压缩性因图像而异。前面提到的格式提供不超过两个比特率/质量选项(BC1/BC7或PVRTC4bpp/2bpp)。由于不能使用5bpp压缩级别(如果4bpp提供的质量略微不足),则必须使用8bpp选项。它在没有显着质量改进的情况下使带宽翻倍。
- 支持2D和3D纹理。
ASTC的格式由一个固定大小的128bit的块,但是对于2D纹理,编码的图块的大小从4x4到12x12纹理像素,对于3D纹理从3x3x3到6x6x6不等。下图表示所有的ASTC的压缩格式及压缩率。

关于ASTC有一篇比较好的参考文章。
ASTC与普通Block压缩算法的区别
- ASTC的块的大小是可变的,可以是44,66等,但是数据量是固定的,一块的大小固定128bit,也就是块越大,压缩率就越高,质量就越差。
- 块是可以分成更细的partition,这个防止在一块中出现不连续的更小的部分。
- 通道的角度上也可以分planes,比如RBG可以和A分开,这样是为了分别计算用于插值的Color Endpoint。
- 对Color Endpoint和Weight的数据使用了BISE( bounded integer sequence encoding)。
2,3,4使得Color Endpoint和weight占用的位数是可变的,这让ASTC的压缩率可变,有可变的块大小,而且可以兼容1~4个通道。
ASTC压缩算法基本概念
Color Endpoint
ASTC是用两个端点颜色来框定当前block的颜色范围。然后用插值的方式得到当前texel的数值,所以,这里的Color Endpoint就是用于插值的两个端点颜色。
Plane
贴图中各个通道之间并不一定是相关的,比如我们可以在Alpha通道里面存一些和颜色不相关的数据,这个时候如果把四个通道看成一个整体去做插值的话,每个texel只有一个weight去插值,那么就会出现比较大的误差,为了解决这个问题,提出了Dual-Plane的概念,也就是Color Endpoint的某一个单独的通道可以单独计算,每个texel可以有两个weight去用来插值两个Plane的值。
Partition
虽然,我们已经把整个图片分割成了4*4这样的Block,但是仍然可能会出现在block内部值相差很大的情况,这个时候就需要把原来的block细分到更小的partition上去了,然后每个partition分别计算Color Endpoint,再来插值,这样会更合理。当Dual-Plane开启的时候,partition最多只能是3,因为大于3数据位就不够了。
BISE(Bounded Integer Sequence Encoding)
ASTC中主要使用到了BISE(Bounded Integer Sequence Encoding)。比如现在有三个数字需要编码{40,50,70},对应的二进制为{101000,110010,1000110},一般情况下,我们可能想到的使用二进制来编码这三个值,这三个值在128以内,所以,可以每个数用7bit来编码,那么也就是需要21bit位。那这里我们可以用BISE来减少编码的位数,以此来压缩数据量。BISE会把数分为高位和地位,高位使用Trits(三进制)或者Quints(五进制)来表示,多少值范围内的数如何分割表示是有个最佳实践的,这里可以查表来确定,为了简单,这里直接给出例子中的结论,它们是在80(查找表中的固定值)以内的数字,所以使用4bit的Bits表示低位,高位用Quints来表示。那它们的低位就是{1000,0010,0110},这个直接编码就可以了,高位的值为{2,3,4},然后把这三个值拼到一个大数值上,因为高位用Quints表示的,三位的Quints就能表示125(5 * 5 * 5)以内的数字,我们可以用7bit(128)来表示,这样原本要24(3 * 8)bit的三个数字就用了4 * 3 + 7 = 19bit来表示了。下图是BISE和二进制存储效率的对比图。

寻找主轴
我们在编码贴图的时候,Color Endpoint只会有两个值,也就是说我们在做压缩的时候是一个二维的插值,我们的贴图中有四维的数据,我们需要尽量的保留原来block中的值。也就是说需要把数据降维,这个就是PCA(主成分分析法)来处理的问题了。但是在实际的实现中,我们并不会真的用PCA来计算,因为PCA需要计算数据的协方差矩阵,还要计算矩阵的特征值,这些计算过于繁琐,我们需要一个近似的方法来替代PCA,当然如果你不需要实时,为了更好的效果,PCA仍然是一个可选的方案。
Bounding Box
这个比较好理解,就是找所有数据在四个维度上的最大最小值,就像我们计算Mesh的AABB bound一样,这样我们不仅找到了主轴,就是最小和最大值组成的那条轴,而且还直接找到了Endpoints,也就是最大最小的值。但是,这个方案明显太过粗略了,这个轴有可能完全在大多数值的分布之外,但是由于计算量很少,这个方案在实时要求很高的时候是可取的。
最大笛卡尔坐标轴累加
这个方法是ARM提供的开源代码中的实现,做法是这样的,先求出block中所有颜色的值的平均值。然后计算每个颜色值跟平均值的差,然后根据这个差是否在x,y,z,w上的正方向上,比如如果在x的正方向上,就将这个差的值加到一个累加的向量上,也就是说sum_x这样的值会是所有差值在x正方向上的差值的累加,还有其他sum_y,sum_z,sum_w,然后比较这几个sum的向量的模,最长的向量就是我们想要找的向量。感性上,我能理解它在先确定一个笛卡尔坐标轴方向下,然后找到这个方向下所有点的贡献,然后得到在这几个方向上选择一个最优的。但是,我没有找到这背后的数学支撑,如果有同学能找到线索,请千万告诉我(在ARM开源代码中的compute_avgs_and_dirs方法中)。
计算 EndPoint
对于Bounding Box的方法,这个比较简单,就是算出来的最大最小值,对于单纯找主轴的方法,我们需要把所有的颜色值投影到轴上,找到投影完最大和最小的值,就是我们要找的Endpoint的值。
计算Weights
这个需要计算Endpoint之间的距离,然后把Block中每个投影到主轴上的值减去最小值之后去除以Endpoint之间的距离。
ASTC压缩流程
上面介绍了一些ASTC压缩算法用到的一些算法细节,接下来就开始介绍如何使用上面的算法,把一个4*4的block中的颜色值压缩写入到128bit的数据块中的。首先看一下这128bit的数据分布是怎样的。

可以看到有些位的数据是固定大小的,是一些配置还有一些数据则是动态大小的,如Texel weight data,不同的配置它的数据大小是不同的。
BlockMode
在ASTC的block中低11位是Block mode,这个是ASTC block中最重要的配置项,包含以下一些内容。

其中$D_P$是说要不要开启dual weight planes,P是表示是否开启range的高清模式,W,H是用来说weight grid的长宽,这个可以和block size不一样,缺失的用插值来获得,$\rho^0$,$\rho^1$,$\rho^2$是用来表示range的,这个需要结合P来确定。

可以看到,当确定完weights的取值范围,也就确定了如何用BISE来编码了,这个BISE的编码的最佳实现在代码实现的时候会录入到一个表里面。
Part
用来描述partition的个数。所以最多支持4(3+1)个partitions。

如果Partition大于1的话,会有一个Partition index的配置来从上面的编码好的分块去选择。

CEM(Color endpoint mode)
这个配置是在说如何编码Color endpoint的,有下面这么多种编码方式。

上面的图是single-partition的时候CEM有4bits,如果是multi-partition,那么CEM就会有6-14bits了。multi-partition会略复杂,这里就不详细说明了。虽然压缩的数据可以有各种编码,通道数也可以配置,但是解压之后就都是RGBA的基本格式了。
Color Endpoint Data
当block的上面的配置确定之后,我们就已经决定每个类型的数据位数了。我们来简单计算一下,首先blockmode的位数是固定的是11bits,然后是part 2bits,然后根据part的数量得出有P位的partition index。根据block mode中的weight grid size 和 weight range,就可以计算出一个plane的weight data的位数了,那整个weight data的数据位数就可以计算出来了就是$W_{width} * W_{height} * NumOfPlane * WeightBits$。CEM有4bit或者6-14bits。如果开了dual-plane,还会有2bits的CCS(Color Component Selector),这个是用来标明哪个通道会被认为是单独的一个plane。

CCS是写在Weight Data的低位后面的。到了这里,我们就把数据填的只剩Color Endpoint Data了,这个时候可能会疑惑,Color Endpoint Data用什么BISE编码格式呢,这个是算出来的,因为只剩下Color Endpoint Data了,我们需要用完整个128bit,这里会有一个表格,根据还剩多少bits,Color Endpoint的编码方式(CEM),几个partitions,就能得出需要多少个数字来表达所有的Color Endpoint Data,然后直接查表(quant_mode_table)能得出BISE的编码规则。到此所有128bits的数据就被填满了。
压缩算法流程
下面就是整个ASTC的压缩流程。


