1. 纹理压缩定义
纹理压缩是为了解决内存、带宽问题,专为在计算机图形渲染系统中存储纹理而使用的图像压缩技术。
2. 图片和纹理压缩格式
图片压缩格式和纹理压缩格式的区别:
- 从概念上讲
- 图片压缩格式:是图片文件的存储格式,通常在硬盘、内存中存储,传输文件时使用,例如jpg、png、gif、bmp。
- 纹理压缩格式:是显卡能直接进行采样的纹理数据格式,通常在向显卡中加载纹理时才使用。
- 从实现方式上讲
- 图片压缩格式:基于整张图片进行压缩,像素之间解码过程中存在依赖关系。它无法实现单个像素级的解析,发挥不了显卡的并行能力。并且,无论什么格式在显卡解码后都是RGBA的纹理格式。
-
- 总结:无法减少显存的占用率,且需要CPU解压后才能被GPU读取,结果就是:增加了CPU的时间和带宽。
- 纹理压缩格式:基于块压缩,能够更快的读取像素所属字节块进行解压缩,以支持快速随机访问。
-
- 随机访问指的是如果渲染一个物体时,需要在某个坐标上采样纹理,那么GPU只需要读取该像素所属固定大小字节块,对其进行解压即可。
- 例如如果拿到一张贴图,设置纹理压缩格式DXTC/ETC/ASTC,CPU会按照我们设定的格式进行压缩,然后传递给GPU读取。如果不设置纹理压缩格式,以图片格式进行,那么CPU也会进行压缩,这增加了CPU的时间和带宽,并且会压缩为RGBA32格式,而这个格式非常大,因此并没有起到压缩的作用。
- 总结:纹理压缩相对正常图片格式,能够直接被GPU采样,发挥GPU强大的并行能力,且优化了带宽问题。
3. 常见的纹理压缩格式
其中黄色为常用格式
3.1. 非压缩格式
RGBA8888(RGBA32) | R、G、B、A四个通道各占8位 一个像素消耗4 * 8 = 32位(bit)= 4字节(Byte) |
RGBA4444(RGBA16) | R、G、B、A四个通道各占4位 一个像素消耗16b,2B |
RGB888(RBG24) | R、G、B三个通道各占8位 一个像素消耗24b,3B |
RGB565(RGB16) | R、G、B三个通道各占5、6、5位 一个像素消耗16b,2B |
3.2. DirectX压缩标准-DXTC
DXTC系列的纹理压缩格式来源于S3公司提出的S3TC算法,它的基本思想是把4×4的像素块压缩成一个64或128位的数据块。优点是创建了一个固定大小且独立的编码片段,没有共享查找表或其他依赖关系,简化了解码过程。
3.2.1. DXT1(BC1)
DXT1也叫作BC1,它将4×4的像素块压缩成了一个64位的数据块,这个64位的数据块中包含两部分:
- 其中32位是两个16位RGB(RGB565)颜色,这两个RGB颜色是16个像素的每个像素的两个极端颜色值AB,然后通过线性插值计算出剩余的两个中间颜色CD。
- 剩余的32位平均分配给了16个像素作为颜色值的索引值(例如 00 01 10 11......),每个像素占2位。
此外注意:
- 存储极端颜色的格式是RGB565,也就是说绿(G)通道的精度比其他两个通道精度高一位,占6位。这就是为什么把信息放绿通道精度会更高的原因。为什么G通道精度更高?这是因为人眼对绿色更敏感。
- DXT1格式适用于不具有透明度信息或者具有一位透明度信息的贴图(完全透明or完全不透明)
-
- 对于没有Alpha信息的贴图,压缩和前面一样
- 对于有Alpha信息的贴图,极端颜色插值时,中间颜色不会插值出两个,而是只有一个C(A和B插值得到),因为要留出一个D表示是否透明or不透明。
DXT1的压缩率
- 参照对象:RGB24-每个像素占24b(DXT1主要用于没有Alpha信息的贴图)
- DXT1:总数据块为64位,16个像素共用,那么每个像素占4b
- 所以压缩率为24b / 4b = 6:1
3.2.2. DXT2/3(BC2)
DXT2/3将4×4的像素块压缩成了一个128位的数据块,每个像素占8位。128位中64位和DXT1是一样,表示颜色信息,另外64位用来增加Alpha信息。Alpha信息并没有插值,只是单纯的为每一个像素多给4位信息。即像素0~3表示透明信息,4-7表示颜色信息。
DXT2和DXT3的区别:
- DXT2是已经完成了颜色与Alpha的混合,当透明度发生改变时,直接改变整体颜色值,不再单独进行复合
- DXT3的Alpha信息相对独立(分开压缩)
DXT2、3的压缩率
- 参照对象:RGBA(32位)
- 总数据块为128位,一共16个像素,每个像素占8位
- 压缩率为32 / 8 = 4:1
3.2.3. DXT4/5(BC3)
DXT4/5将4×4的像素块压缩成了一个128位的数据块,每个像素占8位。和DXT2/3的区别是Alpha信息是通过线性插值得来的。表示Alpha信息的64位:
- 2个8位的极端值=16b
- 每个像素3位的索引值=48b
DXT4和DXT5的区别:同DXT2/3
DXT4、5的压缩率同为4:1
扩展:我们可以看到在Unity内贴图类型选为法线后会采用DXTnm压缩格式,该格式会把法线贴图R通道存入A通道,然后RB通道清除为1,这样可以将法线XY信息分别存入到RGB/A中分别压缩,以获得更高的精度,然后再根据XY构建出Z通道数据;
Default
Override
3.3. ATI系列
3.3.1. ATI1/2(BC4/5)
- ATI1,也被称为BC4
-
- 64位
- 每一个数据块中存储的是单个颜色通道的数据
- 主要用于存储:高度图、光滑度贴图等单通道信息
- ATI1的压缩方式和DXT5中,对于Alpha数据处理一样
- 压缩比:
-
-
- 参照对象:单通道 8位
- 总数据块为 64位,16个像素,所以每个像素4位
- 压缩比为:8 / 4 =2:1
-
- ATI2,也被称为BC5
-
- 128位
- 和ATI1的区别在于,它存储了两个颜色通道的数据
- ATI2的压缩方式:处理方式也是相同的,相当于存储了两个ATI1的数据块
- 如果想要节省通道只存储法线xy通道时,就可以采用该压缩格式
- 优点:因为每个通道都会有自己的索引,会单独压缩,所以法线贴图的xy信息可以比DXT1中有更多保真度
- 缺点:需要使用两倍内存,需要更多的带宽才能将纹理传递到着色器中
- 压缩比:
-
-
- 参照对象:两个通道 16位
- 总数据块为 128位,16个像素,所以每个像素8位
- 压缩比为:16 / 8 = 2:1
-
3.3.2. BC6/7
BC6和BC仅在D3D11及以上图形硬件中受到支持,他们每个块占用16字节。
- BC6
-
- 针对RGB半精度浮点数数据进行压缩
- 是专门针对HDR(高动态范围)图像设计的压缩算法
- 压缩比为6:1
- BC7
-
- 针对8位RGB或RGBA的图像进行压缩
- 是专门针对LDR(低动态范围)图像设计的压缩算法,该格式用于高质量的RGBA压缩,可以显著减少由于压缩法线带来的错误效果
- 压缩比为3:1
- 一般我们使用BC7给端游高质量图像进行压缩
- Reference:BC6和BC7的官方原理说明
3.4. ETC系列
DirectX选择DXTC作为标准压缩格式,而OpenGL选择了爱立信研发的ETC格式。几乎所有安卓设备都支持ETC格式,所以它在移动端应用广泛。
基本思想:ETC的方案同样将4×4的像素单元压缩成64位数据块,同时,将像素单元水平或竖直朝向分为两个区块,每个像素颜色等于基础颜色加上索引指向的亮度范围。
总结:每个区块中有12位用来存储颜色信息(12*2),16位存储其8个像素的索引(每个像素2位,16*2),4位存储亮度索引(4*2)
3.4.1. ETC1
ETC 压缩算法的基本思想:将图片分成 4x4 的若干个像素块,每个像素块按照一定规则编码成为一个 64 位(8字节)的数据。过程:
- 将 4x4 的像素块分为两个 4x2 的子块,有水平和竖直两种分法。使用 1位数据flipbit来表示是哪一种分法,还剩下 63 位数据。
- 分别计算两个分块中 8 个像素颜色的平均值,根据两个块颜色平均值的差值,确定使用 individual模式还是 differential 模式。使用 1位数据diffbit来表示是哪种模式,还剩下 62位数据。
- 存放两个子块的平均颜色信息
-
- individual模式用R4G4B4 的格式分别表示两个子块的平均颜色。
- differential 模式使用 R5G5B5 格式表示第一个子块的平均颜色,R3G3B3 格式表示第二个子块与第一个子块平均颜色的差值。
- 这里使用了 8 * 3 = 24 位数据,还剩下 62-24 = 38 位数据。
- 所有的图片共享一个全局的映射表数据,如下所示。这个数据是固定的全局静态数据,并不会进入到编码数据中,这个表是一个 8 x 4 的二维数组,使用 3 位数表示第一个子块在映射表中查询的第一维索引,即 0-7 的下标,第二个子块同样需要 3 位数来表示,共6位。还剩下 38-3*2 = 32位数据
- 4x4像素块中的每个像素,使用2位数来表示该像素在映射表中查询的第二维索引,需要2位数来表示0-3的下标,所以消耗了 4x4x2 = 32 位数据。
压缩比:
- 对于 RGB24 图片,每个块的数据由 4x4x3 = 48字节,压缩为 8字节,压缩比为 6:1
- 针对 Alpha 图片,每个块的数据由 4x4x1 = 16字节,压缩为 8 字节,所以对于普通的 RGBA 分离为 RGB24 和 Alpha 之后分别进行 ETC1 压缩的图片,整体压缩比为 (48 + 16) : (8 + 8) = 4:1
水平方向:亮度索引值(3位,0-7)
- 每个区块的亮度索引值会从8个亮度索引值中获取当前像素单元的亮度表
竖直方向:像素索引值(2位,0-3)
- 每个像素的像素索引值可以从亮度表的4个值中选取对应的亮度补充值
最终的颜色 = 12位基础颜色信息 + 亮度补偿值
对于ETC1不支持Alpha通道的解决方案
- 采用两张纹理混合的方式
ETC1的适用情况
- 长宽为2的幂次的贴图
- 不适用于带透明通道的贴图
- 适用于基本所有安卓设备
3.4.2. ETC2
- TEC2是ETC1的扩展,支持了Alpha通道(内存占用大于ETC1)
- 硬件要求OpenGL ES3.0和OpenGL4.3以上
3.5. ASTC
ASTC是由ARM和AMD联合开发的纹理压缩格式,ASTC同样是基于块的压缩算法,与BC7类似。它的数据块大小固定为128位,其中块中的像素数量可变,从4×4到12×12像素都有。
每个数据块中存储了两个插值端点,存储的不一定是颜色信息,也可能是Layer信息,这样可以用来对Normal或Alpha进行更好的压缩(根据贴图类型进行针对性压缩)。
块中的每个纹素,存储其对应插值点的权重值,权重值数量可以 少于纹素数量,可以通过插值得到每个纹素的权重值,再进行颜色计算。
- 128位数据块中存储的信息:
-
- 11位,权重、高度信息、特殊块标识
- 2位,Part数量
- 4位,16中插值端点模式(LDR/HDR、RGB/RGBA)
- 111位,插值端点信息、纹素权重值、配置信息
- 优点:
-
- 可以根据不同图片选择不同压缩率的算法
- 图片长宽不需要是2的次幂
- 同时支持HDR和LDR
- 缺点:
-
- 兼容性不够完善
- 解码时间较长
- 无法在iphone6以下的设备运行
3.6. PVRTC
PVRTC是由Imagination公司专为PowerVR显卡设计的压缩格式(iphone、ipad,部分安卓机)
不是基于块的算法,而是将图像分为了低频和高频信号
- 低频信号由两张低分辨率图像AB组成
- 高频信号则是一张记录了每个像素混合的权重值的全分辨率低精度的调制图像
- 解码时,AB图像经过双线性插值放大,然后根据调制图像权重进行混合
压缩原理,分为4-bpp 和 2-bpp(bpp = Bit Per Pixel,即每个像素占的位数)
- 4-bpp为例:
把4×4的像素单元压成一个64位数据块
- 64位数据块中包含了A、B两张图(在原图基础上压缩到1/4的低分辨率图像)
- 不同模式下每个像素调制数据可以得到不同的混合值,根据这个混合值用A和B混合得出最终颜色值
位数占比:
- 32位的调制数据(2*16)
- 1位的调制标志(也称为模式)
- 15位的颜色A(554或4433),1位颜色A的不透明标志
- 14位颜色B(555或4443),1位颜色B的不透明标志
- 共计:32 +1 + 16 + 15 = 64位
压缩率
- 以RGB为参照标准
-
- 压缩率 = 24 / (64/16) = 6:1
- 以RGBA为参照标准
-
- 压缩率 = 32 / (64/16) = 8:1
- 2-bpp
把一个8×4的像素单元压成了64位数据块
4. 总结
4.1. 画质比较
RGBA > ASTC 4×4> ASTC6×6 > TEC2 ≈ ETC1
4.2. 压缩比
- DXT1 6:1
- DXT2/3 4:1
- DXT4/5 4:1
- ATI1 4:1
- ATI2 4:1
- BC6 6:1
- BC7 3:1
- ETC1 6:1
- PVRTC 6:1
- ASTC 4:1~35.95:1
4.3. 实际应用中的选择
PC
- ① 低质量使用DXT1格式不支持A通道,使用DXT5格式支持A通道;
- ② 高质量使用BC7格式,支持A通道;
安卓
- ① 低质量使用ETC1格式,但不支持A通道;
- ② 低质量使用ETC2格式,支持A通道,需要在OpenGL ES 3.0/OpenGL 4.3以上版本;
- ③ 高质量使用ASTC格式,需要在Android 5.0/OpenGL ES 3.1以上版本;
IOS
- ① 高质量使用ASTC格式,需要Iphone6以上版本;
- ② 低质量使用PVRTC2格式,支持Iphone6以下版本;
补充
- 实际手机端项目中,我们比较常用ASTC(安卓和IOS通用)
- 英伟达和Unity官方对于不同类型贴图给出了不同的压缩方案建议,感兴趣的可以看下:
5. 其他补充
① 常见分辨率及纹理压缩格式下的内存占比分析
具体可以看:5. 理解Textures · 语雀
一些简单的分析:
- 未压缩的RGBA格式在4K(4096)分辨下的占用是87424,是DXT1格式2K(2048)分辨下的30倍以上,所以纹理压缩是非常必要的
- DXT1是三通道的压缩格式,DXT5比多一个Alpha通道,但是在2K情况下,内存多了一倍,所以当我们不使用A通道时,要把贴图的压缩格式改为DXT1
② 关于纹理压缩的其他参考资料