什么是纹理压缩
为了解决内存、带宽问题,专为在计算机图形渲染系统中存储纹理而使用的图像压缩技术
区分图片格式和纹理压缩格式
图片格式
- 是图片文件的存储格式,通常在硬盘、内存中存储,传输文件时使用。常见的有jpg、png、bmp、gif
- 图片压缩格式是基于整张图片进行压缩,像素之间解码过程中存在依赖关系
- 无法实现单个像素级的解析,发挥不了显卡的并行能力
- 在显卡解码后都是RGBA的纹理格式
- 总结:无法减少显存的占用率,且需要CPU解压后才能被GPU读取,导致增加了CPU性能损耗和带宽
纹理压缩格式
- 是显卡能直接进行采样的纹理数据格式,通常在向显卡中加载纹理时才使用
- 基于块压缩,能够更快的读取像素所属字节块进行解压缩,以支持快速访问
- 随机访问:如果渲染一个物体时,需要在某个坐标上采样纹理,那么GPU只需要读取该像素所属固定大小字节块,对其进行解压即可
- 而且如果不进行纹理压缩,传给GPU的是RGBA32格式,未经压缩所占用的内存和带宽是不能接受的
- 纹理压缩相对正常图片格式,能够直接被GPU采样,发挥GPU强大的并行能力,且优化了带宽问题
为什么要纹理压缩
-
图片压缩格式下
- 无法实现像素级解析,无法发挥GPU并行能力,无法减少显存的占用率
- 需要在到GPU之间使用CPU解压缩,增加了CPU的时间和带宽
-
纹理压缩格式下
- GPU可以直接读取贴图,不需要经过中间CPU解码/解压缩的步骤
- 还支持“随机访问”
常见纹理压缩格式
黄色为常用格式
纹理格式知识
-
RGBA8888(RGBA32)
- R、G、B、A四个通道各占8位
- 一个像素占:4 * 8 = 32位(bit)= 4字节(byte)
-
RGBA4444(RGBA16)
- 四个通道各占4位内存
- 一个像素占:4 * 4 = 16位 = 2字节
-
RGB888(RBG24)
- 一个像素占:3 * 8 = 24位(bit)= 3字节(byte)
-
RGB565(RGB16)
- 一个像素占::5+6+5 = 16 位 = 2字节(可能是人眼对绿色敏感点,所以绿色通道精度高点,屏幕像素排列也类似)
-
总结
- 带透明通道的,单通道可以是4位、8位
- 不带透明通道的,单通道可以是8位,或者在RGB分16位(565、绿通道多给1位)
DXTC系列
DCTC纹理压缩格式来源于S3公司提出的S3TC算法,基本思想是把4×4的像素块压缩成一个64或128位的数据块,优点为创建了一个固定大小且独立的编码片段,没有共享查找表或其他依赖关系,简化了解码过程
DXT1(BC1)
将4×4的像素块压缩成了一个64位的数据块,这个64位的数据块的内容为:16位RGB565 + 16位RGB565 + 32位颜色索引值。
两个RGB颜色是4×4像素块中的两个极端颜色值,然后通过线性插值计算出剩余的两个中间颜色,一共四个颜色用于查找。
32位索引值分配给16个像素,每个像素占两位,即00 01 10 11四个位置,用于索引上面说的四种颜色。
需要注意的地方:
-
存储极端颜色的格式是RGB565,也就是说绿(G)通道的精度比其他两个通道精度高一些(所以有把信息放绿通道精度更高的说法)
-
DXT1格式适用于不具有透明度信息或者具有一位透明度信息(表示完全透明or完全不透明)的贴图
- 对于有Alpha信息的贴图
- 极端颜色插值时,中间颜色只有一个,另一个表示完全透明or完全不透明
- 每个像素索引时,极端颜色+中间颜色表示完全不透明,另外一个表示完全透明
- 对于有Alpha信息的贴图
DXT1的压缩率:
- 参照对象:RGB24(DXT1主要用于没有Alpha信息的贴图)
- DXT1: 总数据块为64位,16个像素共用 =>一个像素4位
- 压缩率为:24 / 4 = 6:1
DXT2/3 (BC2)
颜色信息和DXT1一样占用64位,多出64位用来增加Alpha信息
Alpha信息并没有插值,只是单纯的为每一个像素多给4位信息,用来记录Alpha信息,每个像素就占4+4=8位
DXT2和DXT3的区别:
- DXT2是已经完成了颜色与Alpha的混合,当透明度发生改变时,直接改变整体颜色值,不再单独进行复合
- DXT3的Alpha信息相对独立(分开压缩)
DXT2、3的压缩率
- 参照对象:RGBA(32位)
- 总数据块为128位,16个像素共用 =>一个像素8位
- 压缩率为:32 / 8 = 4:1
DXT4/5 (BC3)
与DXT2/3的区别:
-
Alpha信息是通过线性插值得来的
-
表示颜色信息的64位同上
-
多出的64位:
- 2个8位的极端值
- 每个像素3位的索引值(16*3)
DXT4和DXT5的区别:
- DXT4是已经完成了颜色与Alpha的混合,当透明度发生改变时,直接改变整体颜色值,不再单独进行复合
- DXT5的Alpha信息相对独立(分开压缩)
DXT4、5的压缩率:同为4:1
拓展知识
Unity将贴图类型选为法线时,会采用DXTnm格式
- 它基于DXT5,会把法线贴图的R通道存入A通道,然后将RB通道清除为1
- 这样就可以把法线xy信息分别存入到RGB和A中进行压缩,来获得更高的精度
- 最后再根据xy构建出z的信息
- 可以去看一下法线贴图unpack函数
ATI系列
ATI1(BC4)
- 64位,每一个数据块中存储的是单个颜色通道的数据
- 主要用于存储:高度图、光滑度贴图等单通道信息
- ATI1的压缩方式:
- 和DXT5中,对于Alpha数据处理一样
ATI2 (BC5)
-
128位,和ATI1的区别在于,它存储了两个颜色通道的数据,类似处理两个颜色通道版本的BC4
-
ATI2的压缩方式:
- 处理方式也是相同的,相当于存储了两个ATI1的数据块
-
如果想要节省通道只存储法线xy通道时,就可以采用BC5(ATI2)压缩格式
-
因为每个通道都会有自己的索引,会单独压缩,所以法线贴图的xy信息可以比DXT1中有更多保真度,但需要使用两倍内存,需要更多的带宽才能将纹理传递到着色器中
-
压缩比(一些资料上显示BC4和5的压缩比为4/1,可能是由于将单个像素按32位的大小计算):
- ATI1(参照对象:单通道 8位):
- 总数据块为 64位,16个像素,所以每个像素4位
- 压缩比为:8 / 4 =2:1
- ATI2(参照对象:两个通道 16位):
- 总数据块为 128位,16个像素,所以每个像素4位
- 压缩比为:16 / 8 = 2:1
- ATI1(参照对象:单通道 8位):
BC6/7
-
每个块占用16字节,仅在D3D11及以上图形硬件中受到支持
-
BC6
- 针对RGB半精度浮点数数据进行压缩
- 是专门针对HDR(高动态范围)图像设计的压缩算法
- 压缩比为6:1
-
BC7
- 针对8位RGB或RGBA的图像进行压缩
- 是专门针对LDR(低动态范围)图像设计的压缩算法,该格式用于高质量的RGBA压缩,可以显著减少由于压缩法线带来的错误效果
- 压缩比为3:1
-
Reference:
ETC系列
DirectX选择DXTC作为标准压缩格式,而OpenGL选择了爱立信研发的ETC格式
几乎所有安卓设备都支持ETC格式,所以它在移动端应用广泛
基本思想:
ETC的方案同样将4×4的像素单元压缩成64位数据块,同时,将像素单元水平或竖直朝向分为两个区块,每个像素颜色等于基础颜色加上索引指向的亮度范围
即每个区块中有12位用来存储颜色信息(12*2),16位存储其8个像素的索引(每个像素2位,16*2),4位存储亮度索引(4*2)
ETC1
上表为亮度索引值表
- 每个区块的亮度索引值(3位,0-7)会 从8个亮度索引值中获取当前像素单元的亮度表
- 注:课程里讲的是4位,0-15个亮度索引值,但资料中显示的是3位索引值,表中也是
像素索引值(上表,竖直方向):
- 每个像素的像素索引值(2位,0-3)可以从亮度表的四个值中选取对应的亮度补充值
最终的颜色 = 12位基础颜色信息 + 亮度补偿值
理解:
-
原理:
- 将4×4的像素块编码为2×4或者4×2像素的两个块
- 每个块指定一个基色,每个像素的颜色➕一个编码为相对于这个基色偏移的灰度值确定(上面提到的亮度)
-
位数占比:
- 亮度索引3位*2
- 像素索引2位*16
- 基础颜色12位(444*2,或者555+333)*2
- flip1位(控制水平或者竖直划分)*2
- 总位数 = 3*2 + 2*16 + 12*2 + 1*2 = 64位
- //注:*2是因为有两个块,*16是因为有16个像素
压缩率:
- 参照标准:RBG24
- 总共有64个数据块,针对16个像素,也就是每个像素4位
- 压缩比 = 24 / 4 = 6:1
对于ETC1不支持Alpha通道的解决方案
- 采用两张纹理混合的方式
ETC1的适用情况
- 长宽为2的幂次的贴图
- 不适用于带透明通道的贴图
- 适用于基本所有安卓设备
ETC2
- ETC2是ETC1的扩展,支持了Alpha通道(内存占用大于ETC1)
- 硬件要求OpenGL ES3.0和OpenGL 4.3以上
ASTC
-
ASTC是由ARM和AMD联合开发的纹理压缩格式
-
优点:
- 可以根据不同图片选择不同压缩率的算法
- 图片长宽不需要是2的次幂
- 同时支持HDR和LDR
-
缺点:
- 兼容性不够完善
- 解码时间较长
- 无法在iphone6以下的设备运行
-
基本思想:
- 同样是基于块的压缩算法,与BC7类似
- 数据块大小固定为128位
- 块中的像素数量可变,从4×4到12×12像素都有
-
每个数据块中存储了两个插值端点
- 存储的不一定是颜色信息,也可能是Layer信息,这样可以用来对Normal或Alpha进行更好的压缩(根据贴图类型进行针对性压缩)
-
块中的每个纹素,存储其对应插值点的权重值
- 权重值数量可以 少于纹素数量,可以通过插值得到每个纹素的权重值,再进行颜色计算
-
128位数据块中存储的信息:
- 11位,权重、高度信息、特殊块标识
- 2位,Part数量
- 4位,16中插值端点模式(LDR/HDR、RGB/RGBA)
- 111位,插值端点信息、纹素权重值、配置信息
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
- 以RGB为参照标准
- 把4×4的像素单元压成一个64位数据块
-
2-bpp
- 把一个8×4的像素单元压成了64位数据块
实际应用中的选择
画质对比
RGBA > ASTC 4×4> ASTC6×6 > TEC2 ≈ ETC1
(画质较为主观,且不同的贴图针对不同压缩格式也不同,仅供参考
压缩比
纹理格式 | 压缩比 |
---|---|
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 |
选择
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官方对于不同类型贴图给出了不同的压缩方案建议:
- Using ASTC Texture Compression for Game Assets | NVIDIA Developer
- Unity - Manual: Recommended, default, and supported texture compression formats, by platform (unity3d.com)