一、什么是纹理压缩
● 纹理压缩是:
○ 为了解决内存、带宽问题,专为在计算机图形渲染系统中存储纹理而使用的图像压缩技术。
● 区分图片格式和纹理压缩格式
○ 概念上讲
■ 图片格式:
● 是图片文件的存储格式,通常在硬盘、内存中存储,传输文件时使用
● 例如:jpg、png、gif、bmp
■ 纹理压缩格式:
● 是显卡能直接进行采样的纹理数据格式,通常在向显卡中加载纹理时才使用
○ 原理上讲
■ 图片格式:
● 图片压缩格式是基于整张图片进行压缩,像素之间解码过程中存在依赖关系
● 无法实现单个像素级的解析,发挥不了显卡的并行能力
● 并且,无论什么格式在显卡解码后都是RGBA的纹理格式
● 总结:无法减少显存的占用率,且需要CPU解压后才能被GPU读取,结果就是:增加了CPU的时间和带宽
纹理压缩格式:
● 基于块压缩,能够更快的读取像素所属字节块进行解压缩,以支持快速访问
● “随机访问”:如果渲染一个物体时,需要在某个坐标上采样纹理,那么GPU只需要读取该像素所属固定大小字节块,对其进行解压即可。
● 举例理解:
○ 如果拿到一张贴图,设置纹理压缩格式:
■ CPU会按照我们设定的格式进行压缩,然后传递给GPU读取
○ 如果不设置纹理压缩格式,以图片格式进行:
■ CPU也会进行压缩,但是会压缩为RGBA32格式,但其实这个格式是非常大的,并没有起到压缩的作用
○ 总结
■ 纹理压缩相对正常图片格式,能够直接被GPU采样,发挥GPU强大的并行能力,且优化了带宽问题
二、为什么要使用纹理压缩
● 实际上,使用纹理压缩的原因在上部分图片格式和纹理压缩格式区别的部分里已经讲了
○ 图片压缩格式下
■ 无法实现像素级解析,无法发挥GPU并行能力,无法减少显存的占用率
■ 需要在到GPU之间使用CPU解压缩,增加了CPU的时间和带宽
○ 纹理压缩格式下
■ GPU可以直接读取贴图,不需要经过中间CPU解码/解压缩的步骤
■ 还支持“随机访问”
● 总结一下就是:
○ 我们需要一种内存占用既小又能被GPU直接读取的格式(这种格式就是纹理压缩
三、常见的纹理压缩格式
前言:非纹理压缩格式
○ RGBA8888(RGBA32)
■ R、G、B、A四个通道各占8位
■ 所以一个像素消耗:4 * 8 = 32位(bit)= 4字节(byte)
○ RGBA4444(RGBA16)
■ 四个通道各占4位内存
■ 一个像素消耗:4 * 4 = 16位 = 2字节
○ RGB888(RBG24)
■ 同理,一个像素消耗: 3字节
○ RGB565(RGB16)
■ 一个像素消耗:2字节
○ 总结
■ 带透明通道的,单通道可以是4位、8位
■ 不带透明通道的,单通道可以是8位,或者在RGB分16位(565、绿通道多给1位)
1、DXTC系列
DXTC系列的纹理压缩格式来源于S3公司提出的S3TC算法
○ 基本思想:
■ 把4×4的像素块压缩成一个64或128位的数据块
○ 优点:
■ 创建了一个固定大小且独立的编码片段,没有共享查找表或其他依赖关系,简化了解码过程
DXT1(BC1)
● 也叫作BC1
● 内容:
■ 将4×4的像素块压缩成了一个64位的数据块,这个64位的数据块中包含:
● 其中32位是:
○ 两个16位RGB(RGB565)颜色。
○ (这两个RGB颜色是4×4像素块中的两个极端颜色值,然后通过线性插值计算出剩余的两个中间颜色)
● 剩余的32位
○ 平均分配给了16个像素作为颜色值的索引值,每个像素占2位
● 注意:
○ ①存储极端颜色的格式是RGB565,也就是说绿(G)通道的精度比其他两个通道精度高一些
■ 这就是有些人说把信息放绿通道精度更高的原因
■ //补充:多出来的精度给绿通道是因为:人眼对绿色更敏感
○ ②DXT1格式适用于不具有透明度信息或者具有一位透明度信息(表示完全透明or完全不透明)的贴图
■ 对于没有Alpha信息的贴图,压缩遵循上文
■ 对于有Alpha信息的贴图
● 极端颜色插值时,中间颜色只有一个,另一个表示完全透明or完全不透明(例如上述例子中,C为中间颜色,D表示透明信息)
● 每个像素索引时,极端颜色+中间颜色表示完全不透明,另外一个表示完全透明
● DXT1的压缩率
○ 参照对象:RGB24(DXT1主要用于没有Alpha信息的贴图)
○ DXT1:
■ 总数据块为64位,16个像素共用 =>一个像素4位
○ 所以压缩率为:24 / 4 = 6:1
DXT2/3(BC2)
● 也叫作BC2
● 128位
● 颜色信息和DXT1是一样占用64位,多出64位用来增加Alpha信息
○ Alpha信息并没有插值,只是单纯的为每一个像素多给4位信息,用来记录Alpha信息
○ 这样一来,每个像素就占4+4=8位,(0~3表示透明信息,4-7表示颜色信息)
● 简单来说就是:
○ 相比DXT1,多了64位用来存Alpha信息, 16个像素,每个4位
● DXT2和DXT3的区别:
○ DXT2是已经完成了颜色与Alpha的混合,当透明度发生改变时,直接改变整体颜色值,不再单独进行复合
○ DXT3的Alpha信息相对独立(分开压缩)
● DXT2、3的压缩率
○ 参照对象:RGBA(32位)
○ 总数据块为128位,16个像素共用 =>一个像素8位
○ 压缩率为:32 / 8 = 4:1
XT4/5(BC3)
● 也被称为BC3
● 128位
● 和DXT2/3的区别:
○ Alpha信息是通过线性插值得来的:
■ 表示颜色信息的64位同上
■ 多出的64位:
● 2个8位的极端值
● 每个像素3位的索引值(16*3)
● DXT4和DXT5的区别:
○ 同2和3
● DXT4、5的压缩率
○ 同为4:1
2、ATI系列
ATI1/2(BC4、BC5)
● ATI1,也被称为BC4
○ 64位
○ 每一个数据块中存储的是单个颜色通道的数据
○ 主要用于存储:高度图、光滑度贴图等单通道信息
○ ATI1的压缩方式:
■ 和DXT5中,对于Alpha数据处理一样
● ATI2,也被称为BC5
○ 128位
○ 和ATI1的区别在于,它存储了两个颜色通道的数据
○ ATI2的压缩方式:
■ 处理方式也是相同的,相当于存储了两个ATI1的数据块
○ 如果想要节省通道只存储法线xy通道时,就可以采用BC5(ATI2)压缩格式
○ 优点:
■ 因为每个通道都会有自己的索引,会单独压缩,所以法线贴图的xy信息可以比DXT1中有更多保真度
○ 缺点:
■ 需要使用两倍内存,需要更多的带宽才能将纹理传递到着色器中
○ 压缩比:
■ ATI1:
● 参照对象:单通道 8位
● 总数据块为 64位,16个像素,所以每个像素4位
● 压缩比为:8 / 4 =2:1
■ ATI2:
● 参照对象:两个通道 16位
● 总数据块为 128位,16个像素,所以每个像素4位
● 压缩比为:16 / 8 = 2:1
○ //注:一些资料上显示BC4和5的压缩比为4/1,查了很多还是没找到详细资料。这一块待定,日后搞清楚了回来修改
■ 弹幕有同学提出:维基百科中说,4:1的压缩比,是因为单个像素当作32位的大小来计算了
BC6/7
● BC6和BC仅在D3D11及以上图形硬件中受到支持
● 他们每个块占用16字节
● BC6
○ 针对RGB半精度浮点数数据进行压缩
○ 是专门针对HDR(高动态范围)图像设计的压缩算法
○ 压缩比为6:1
● BC7
○ 针对8位RGB或RGBA的图像进行压缩
○ 是专门针对LDR(低动态范围)图像设计的压缩算法,该格式用于高质量的RGBA压缩,可以显著减少由于压缩法线带来的错误效果
○ 压缩比为3:1
● 一般我们使用BC7给端游高质量图像进行压缩
● Reference:
○ BC6和BC7的官方原理说明
https://docs.microsoft.com/zh-cn/windows/uwp/graphics-concepts/bc6h-format https://docs.microsoft.com/zh-cn/windows/uwp/graphics-concepts/bc7-format
3、ETC系列
● DirectX选择DXTC作为标准压缩格式,而OpenGL选择了爱立信研发的ETC格式
● 几乎所有安卓设备都支持ETC格式,所以它在移动端应用广泛
● 基本思想:
○ ETC的方案同样将4×4的像素单元压缩成64位数据块,同时,将像素单元水平或竖直朝向分为两个区块,每个像素颜色等于基础颜色加上索引指向的亮度范围
总结:每个区块中有12位用来存储颜色信息(122),16位存储其8个像素的索引(每个像素2位,162),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位(4442,或者555+333)2
■ flip1位(控制水平或者竖直划分)2
■ 总位数 = 32 + 216 + 122 + 1*2 = 64位
■ //注:*2是因为有两个块,*16是因为有16个像素
● 压缩率:
○ 参照标准:RBG24
○ 总共有64个数据块,针对16个像素,也就是每个像素4位
○ 压缩比 = 24 / 4 = 6:1
● 对于ETC1不支持Alpha通道的解决方案
○ 采用两张纹理混合的方式
● ETC1的适用情况
○ 长宽为2的幂次的贴图
○ 不适用于带透明通道的贴图
○ 适用于基本所有安卓设备
ETC2
● TEC2是ETC1的扩展,支持了Alpha通道(内存占用大于ETC1)
● 硬件要求OpenGL ES3.0和OpenGL4.3以上
3、ETC系列
● DirectX选择DXTC作为标准压缩格式,而OpenGL选择了爱立信研发的ETC格式
● 几乎所有安卓设备都支持ETC格式,所以它在移动端应用广泛
● 基本思想:
○ ETC的方案同样将4×4的像素单元压缩成64位数据块,同时,将像素单元水平或竖直朝向分为两个区块,每个像素颜色等于基础颜色加上索引指向的亮度范围
○
○ 总结:每个区块中有12位用来存储颜色信息(122),16位存储其8个像素的索引(每个像素2位,162),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位(4442,或者555+333)2
■ flip1位(控制水平或者竖直划分)2
■ 总位数 = 32 + 216 + 122 + 1*2 = 64位
■ //注:*2是因为有两个块,*16是因为有16个像素
● 压缩率:
○ 参照标准:RBG24
○ 总共有64个数据块,针对16个像素,也就是每个像素4位
○ 压缩比 = 24 / 4 = 6:1
● 对于ETC1不支持Alpha通道的解决方案
○ 采用两张纹理混合的方式
● ETC1的适用情况
○ 长宽为2的幂次的贴图
○ 不适用于带透明通道的贴图
○ 适用于基本所有安卓设备
ETC2
● TEC2是ETC1的扩展,支持了Alpha通道(内存占用大于ETC1)
● 硬件要求OpenGL ES3.0和OpenGL4.3以上
4、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位,插值端点信息、纹素权重值、配置信息
5、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位数据块
新的算法
神经纹理压缩算法:NTC