3.6 纹理压缩——包体瘦身术
一、什么是纹理压缩
纹理压缩是为了解决内存、带宽问题,专为在计算机图形渲染系统中存储纹理而使用的图像压缩技术。
二、为什么要纹理压缩
- 图片格式
-
- 图片格式是图片文件的存储格式,通常在磁盘、内存中存储和传输文件时使用。
- 例如:JPG、PNG、GIF、BMP等。
- 纹理格式
-
- 纹理格式是显卡能够直接采样的纹理数据格式,通常在向显卡中加载纹理时使用。
- 纹理压缩格式基于块压缩,能够更快读取像素所属字节块进行解压缩以支持随机访问。
- 图片压缩格式基于整张图片进行压缩,无法直接实现单个像素的解析。
- 图片压缩格式无法被GPU识别,还需要经CPU解压缩成非压缩纹理格式才能被识别。
三、常见纹理压缩格式
常见纹理格式(黄色为常用格式)
压缩比算法
压缩比算法:https://www.cnblogs.com/bylle/p/12212823.html
公式
像素大小 = 16-bytes / 分块宽高 * 8-bit;
压缩率 = 原始像素大小(32-bit) / 像素块大小;
图像大小 = 原始图片大小 / 压缩率;
假设一张4MB的1024*1024原始图片:
块宽高 | 像素大小(bits) | 压缩率 | 压缩后图片大小 |
4*4 | 8 | 4 | 1MB |
5*5 | 5.12 | 6.25 | 655KB |
8*8 | 2 | 16 | 256KB |
10*10 | 1.28 | 25 | 163KB |
12*12 | 0.89 | 35.95 | 113.93KB |
1.非压缩格式
- RGBA8888(RGBA32)
-
- 一个像素32位,包含A通道,即一个像素消耗4字节。
- RGBA4444(RGBA16)
-
- 一个像素16位,包含A通道,即一个像素消耗2字节。
- RG888(RGB24)
-
- 一个像素24位,无A通道,即一个像素消耗3字节。
- RGB565(RGB16)
-
- 一个像素16位,无A通道,即一个像素消耗2字节。
2.DirectX标准压缩-DXTC
DXTC纹理压缩格式来源于S3公司提出的S3TC算法,基本思想是把4x4的像素块压缩成一个64或128位的数据块,优点创建了一个固定大小且独立编码片段,没有共享查找表或其他依赖关系,简化了解码过程。
参考链接:DXT纹理压缩-CSDN博客
BC1 Block (S3TC/DXT1)
- BC1块由两个基色c0和c1以及一个索引表(位图)组成(见图6)。 然而,索引表有一个2位的条目,因为BC1允许有两个额外的颜色,即通过混合基色得到的c2和c3。所有的c0、c1、c2和c3可以被视为一个压缩块的局部调色板。与CCC相比,四种不同颜色的存在明显提高了图像质量。基色以RGB565格式存储,即红色和蓝色通道为5比特,绿色通道为6比特,导致4bpp压缩水平。
- 有两种类型的BC1块:第一种是不支持透明的,第二种是支持透明的。
- 对于第一种类型,有两种方法来定义c2和c3。在大多数实现中,它们是根据方程式2.1计算的,该方程式表示1:2和2:1的线性混合。如果我们假设一个区块内的颜色值是正态分布的,那么方程2.2将给出一个较低的误差。这种方法被用于NVidia的GPU。此外,RGB565到RGB888颜色转换的硬件实现可能有一些简化,这也影响了结果。因此,同样的压缩数据在不同的硬件上可能产生不同的结果。在这两种情况下,四种颜色都位于RGB空间的同一区段,c0和c1为其端点。
- 第二种类型的BC1允许人们编码一个单比特的alpha通道,可用于具有简单透明度的纹理。每个纹素只能是完全透明或完全不透明的。这种模式也被称为穿透式alpha。对于这种类型的BC1块,c3被计算为1:1的线性混合(公式2.3),c2代表完全透明。此外,一些没有透明文本的原始图像Tile可以用第二种类型的块进行更准确的编码。
个人理解:简单来说就是有两个极端颜色C0,C1,
,通过类似线性插值或者别的插值方式算出若干个颜色值C2、C3等。然后通过索引表进行颜色组合
,譬如C0对应01,C1对应00,C2对应10,C3对应11,然后进行填充。
BC2 Block (DXT2/DXT3)
- BC1格式可以管理24位RGB纹理,但不适合32位RGBA8888纹理。Alpha通道可以用来存储透明度、镜面数据或材料的其他属性。BC2和BC3 Direct3D格式就是为这类纹理设计的。BC2块占据了128位,是BC1大小的两倍。因此,压缩级别为8bpp。BC2的一半保留给4位精度的alpha值("alpha a "到 "alpha b"),另一半只是BC1,用于存储RGB数据(见图7)。实际上,BC2符合RGBA8884纹理的压缩的RGB通道。颜色通道的解码方式与BC1相同。唯一的区别是,它总是被当作第一种类型的块。
- DXT2在半透明纹理的情况下,为了进行合成或与背景混合,颜色值必须乘以来自alpha通道的透明度系数。DXT3块的布局与DXT2相吻合,但颜色值假定不被预先乘以。
- 尽管如此,DXT2和DXT3的解码程序都是一样的。格式名称只是作为对颜色数据含义的提示。
- 需要注意的是,在进行纹理过滤之前,颜色值应该总是乘以alpha。否则,过滤的结果将是不正确的。
BC3 Block (DXT4/DXT5)
- BC3块和BC2一样,由两个64位的部分组成:一个是alpha数据,一个是颜色数据。颜色部分也是重复BC1的布局,但alpha部分是以压缩的形式存储的。alpha压缩与DXT1非常相似,除了通道的数量;有两个8位精度的端点和3位索引表,允许从本地调色板的八个值中选择一个。
- 与BC1中相同的数据冗余技巧被应用于指定解码模式。如果alpha_0>alpha_1,本地调色板的六个额外值将通过线性内插计算。否则,只有四个值被插值,剩下的两个值对应于最大和最小的允许值。Alpha通道的调色板数量如清单1所示。BC1子块总是被视为第一种类型的块,并应用公式2.1或2.2。
- 与DXT2/DXT3类似,Direct3D 9中的DXT4/DXT5格式只在颜色通道数据的意义上有所不同。DXT4假设存储的颜色数据是预先乘以alpha的,而DXT5则假设它不是。在Direct3D 10中只有一种BC3格式,它不区分这两种情况。
- 通常情况下,BC3格式比BC2提供更好的质量,BC2应该用于低相关的alpha数据。
BC4 Block (ATI1/3Dc+)
- 3Dc格式最初是由ATI专门为法线图的压缩而开发的,因为DXT1格式不能为这种数据提供所需的质量。法线图包含了每个纹理的法线矢量方向的信息,这使得人们可以在不增加几何体复杂性的情况下计算出高水平的照明。法线矢量的坐标被存储在颜色通道中,这些值被解释为[-1, 1]范围内的浮点数。BC1压缩的问题是,不同颜色通道的值通过共享索引表耦合在一起。它适合于普通的RGB图像,但不适合于通道不相关的正常图。另外,由于局部调色板尺寸较小,BC1明显限制了梯度值。由于这些原因,用BC1压缩的法线图质量很差。
- 根据定义,法线向量有一个单位长度,所以它们只能由x和y坐标来指定,而z可以用公式2.4来计算。在切线空间法向量(24)的情况下,z的符号总是正的。
- BC5块只是一个双BC4。这允许独立存储两个通道。这一事实加上BC5更大的局部调色板,使得双通道纹理的图像质量优于BC1。
- 每个子块的解码过程与BC4的解码过程完全相同。默认情况下不计算Z坐标,因为BC5格式可以用于任何双分量的纹理,而且解码后的值会填充红色和绿色通道。在法线图的情况下,缺失的Z坐标可以在像素着色器中计算。
- 相应的OpenGL扩展被称为EXT_texture_compression_rgtc (19), ARB_texture_compression_rgtc (20) 和 EXT_texture_compression_latc (21)。_rgtc和_latc版本的规范同时描述了单通道BC4块和双通道BC5块。在_rgtc的情况下,未打包的数据被解释为红色和绿色颜色通道的值。在_latc的情况下,未打包的数据被解释为亮度或亮度和alpha的值。
BC6H and BC7 General Information
- 限制BC1压缩质量的主要因素是:
-
- 端点的精度低(RGB565)。此外,通道间的比特分布不均可能导致颜色偏移。例如,许多纯灰色不能在RGB565中 "纯 "表示。RGB565(12, 24, 12)是RGB888(99, 97, 99),它略带紫色,RGB565(12, 25, 12)则过于偏绿RGB888(99, 101, 99)。
- 本地调色板的尺寸小。一个区块中只能使用4种不同的颜色。
- 所有的颜色在RGB空间中都位于同一直线上。如果原始块中的颜色没有映射到线段上,压缩后的块可能看起来很糟糕(见图12)。
- 这些问题在新格式中通过提高端点精度和存储最多三对端点而得到解决。这两种格式都使用128位的块,导致8bpp的压缩水平。根据块的类型,一个压缩块有一组不同的字段和每个字段的不同大小。这允许在每个块的基础上选择最佳编码。这种灵活性极大地减少了压缩伪影,但却使压缩程序非常复杂。BC6H的块类型的数量增加到14个,BC7的块类型增加到8个。与BC1不同的是,块类型是在压缩块的第一个比特中明确设置的。块类型也被称为块模式。
- 为了采用多个端点对,一个Tile被划分为称为子集的纹素组。每个子集都有自己的端点对。二区Tile有64个预定义的分区集,三区Tile有64个分区集。因此,只有分区ID应该被存储在压缩块中以指定特定的分区。其中一些分区集如下图所示。
二区和三区Tile的前八个分区集
二区Tile的BC6H/BC7块解码的简化例子
- 和以前的格式一样,来自索引表的值指定了端点混合的比例。分区ID决定了每个纹素使用哪个端点对。简化的解码例子见上图,其中A0-A1和B0-B1端点对分别用于子集0和1。一个索引的大小可以是2到4比特。因此,每个子集中可用的中间色的数量从2到14不等。
- BC6是专门针对HDR(高动态范围)图像设计的压缩算法,压缩比为6:1;
- BC7 Block是专门针对LDR(低动态范围)图像设计的压缩算法,压缩比为3:1,该格式用于高质量的RGBA压缩,可以显著减少由于压缩法线带来的错误效果;
3.ETC
ETC(爱立信纹理压缩)格式最初是为了在移动设备中使用而开发的。今天,它是基于Android的设备的标准压缩方案。ETC1格式通过和WEBGL_compressed_texture_etc1扩展在OpenGL ES和WebGL中被支持。自OpenGL 4.3以来,ETC1和ETC2规范是OpenGL核心配置文件的一部分。
该压缩方案的第一个版本PACKMAN于2004年推出。后来,在2005年提出了名为iPACKMAN的增强版。这个版本更被称为ETC1,被广泛用于移动设备。这个压缩方案的后续发展导致了2007年ETC2格式的出现。
PACKMAN的核心思想
ETC压缩的主要思想是基于一个众所周知的色彩感知事实,即人眼对亮度而不是色度更敏感。因此,每个子块中只存储一种基色(ETC1/ETC2包括两个子块),但亮度信息是以每个顶点为基础存储的。亮度偏移是由一个单一的整数值来设定的,它被加到所有的颜色成分上。一个子块内只有四个不同的亮度偏移,所以只有四种不同的颜色可用。这些颜色可以被看作是一个局部调色板。
PACKMAN
如果一个压缩块所占的位数与总线宽度相同,那么就可以避免流水线停顿,从而简化了硬件实现。由于移动设备在内存大小和总线宽度方面受到严格限制,因此考虑在PACKMAN中使用2x4瓦片。结果是,压缩块只占用32位。因此,压缩级别为4bpp,与BC1的压缩级别相当。
一个块中只存储一种RGB444基色。其他颜色是通过改变文本的亮度获得的。虽然基本思路与BC1相去甚远,但解码过程却非常相似。首先,四色局部调色板被恢复。然后,索引表被用来从这个调色板中挑选颜色见下图。然而,由于块的大小较小,只有4位用于指定亮度变化和调色板中的三种额外颜色。因此,这4位被用来对预定的亮度集之一进行编码。
PACKMAN块解码示例
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Luminance 00 | 2 | 4 | 6 | 12 | 8 | 19 | 28 | 42 |
Luminance 01 | 8 | 12 | 31 | 34 | 50 | 47 | 80 | 127 |
Luminance 10 | -2 | -4 | -6 | -12 | -8 | -19 | -28 | -42 |
Luminance 11 | -8 | -12 | -31 | -34 | -50 | -47 | -80 | -127 |
用于PACKMAN的亮度编码本的前半部分
显然,表的下半部分是上半部分的negation ,简化了解码器。亮度集8-15(未在表中显示)等于0-7集的缩放系数为2。表中的数值是通过从随机数值开始,然后通过最小化测试图像(3)的误差来优化它们。
ETC1 (iPACKMAN)
单一的低精度(RGB444)基色是PACKMAN的主要质量限制因素。这个问题在ETC1中得到了解决。
ETC1 Tile的大小为4x4,它由两个子块组成,就像PACKMAN块一样。这些子块可以垂直或水平排列。这为Tile内的基础颜色选择提供了更大的灵活性。通过引入差分块类型来提高精度,其中第一个子块的基色以RGB555的精度存储,第二个基色则存储3位差分。一个块,其中两个基色都直接以RGB444格式存储,被称为个体。
为了给块型编码保留空间,亮度编码簿被减少到8组,因此索引大小(图中的T0和T1)下降到3比特。很明显,编码本中的数值也被重新计算了(见表)。 这就释放了2个比特。其中一个 "diff "指定了块类型,另一个 "flip "指定了子块的垂直或水平排列。
ETC2
ETC2是ETC1的扩展,支持了Alpha通道的压缩,硬件要求OpenGL ES 3.0和OpenGL 4.3以上;
4.PVRTC
PVRTC (PowerVR Texture Compression)由Imagination公司专为PowerVR显卡核心设计,由于专利原因一般它只被用于苹果的设备,仅Iphone、Ipad和部分PowerVR的安卓机支持。这可能是这几种压缩格式中最不公开的技术。
PVRTC不同于DXT和ETC这类基于块的算法,而将整张纹理分为了高频信号和低频信号,低频信号由两张低分辨率的图像A和B表示,这两张图在两个维度上都缩小了4倍,高频信号则是全分辨率但低精度的调制图像M,M记录了每个像素混合的权重。要解码时,A和B图像经过双线性插值(bilinearly)宽高放大4倍,然后与M图上的权重进行混合。
PVRTC 4-bpp模式下,每4x4像素占一个64位数据块,2-bpp模式下每8x4像素会有一个64位数据块。两者大同小异,我们仅说4-bpp模式。
4-bpp模式下,A和B图缩小后都只保存一个颜色值,如下图所示,A图比B图少1位,但两张图都可以选择以RGB或ARGB的方式存储(最高位决定为哪种),A色可以用RGB554或ARGB3443格式编码,B色可以用RGB555或ARGB3444格式编码。
在解码时,为了解码任意像素,必须读取4个相邻的PVRTC块,使用这4个块来解码一个5x5块。
使用双线性过滤来对A和B图进行扩大,然后A和B图根据M图与“Mode”位进行混合,这里的"Mode"位为1时,M图中10值像素被看作是开启了"punch-through alpha",alpha通道会被强制清零,这种神奇的操作是为了兼容旧应用程序,具体就不说了。
单从4bpp模式来看,PVRTC和BC、ETC非常相似,都有两个颜色值,但基本思想却是不同的。
Unity几种PVRTC的纹理压缩格式:
苹果的所有移动设备都支持PVRTC。
PVRTC2质量比PVRTC更高,而且支持NPOT(非2次幂纹理),是PVRTC的升级版,。
PVRTC和PVRTC2都支持4-bpp和2-bpp的ARGB格式。
RGB PVRTC 4 bit :4 bits/pixel,对RGB压缩比6:1,安卓设备需要PowerVR Series 5以上。
RGBA PVRTC 4 bit :4 bits/pixel,对RGBA压缩比8:1,3位Alpha值,设备同上。
RGB PVRTC 2 bit :2 bits/pixel,对RGB压缩比6:1,安卓和IOS设备需要PowerVR Series 5X以上。
RGBA PVRTC 2 bit :2 bits/pixel,对RGBA压缩比8:1,3位Alpha值,设备同上。
5.ASTC
ASTC(自适应可扩展纹理压缩)是由ARM和AMD联合开发的,并在2012年提出。格式规范被Khronos联盟批准并在OpenGL中采用。适当的OpenGL和OpenGL ES扩展被称为KHR_texture_compression_astc_hdr 。从Mali-T628和Mali-T678开始,所有ARM图形核心都有ASTC硬件支持。专利属于ARM。然而,ASTC是完全开放和免版税的格式。
- 每个用例都对压缩方案提出了自己的要求:
-
- 来自1-4分量的纹理支持。虽然单通道纹理可以使用BC7、PVRTC2或ETC2来存储,但大量的比特会被浪费在空通道上。
- 在不相关的通道的情况下,质量可以接受。这对法线图和RGBA图像很重要。
- 支持LDR和HDR。BC6H可以用于HDR纹理压缩,但它不支持alpha通道。
- 跨平台。特别是,PVRTC只在iOS平台上可用,BC6H/BC7在移动设备中缺失,ETC不被桌面GPU所支持。对于跨平台应用程序的开发者来说,这可以说是非常不方便的。
- 比特率/质量比的灵活性。根据纹理类型的不同,可以接受不同程度的压缩伪影,因为不同图像的可压缩性是不同的。以前提到的格式提供的比特率/质量选项不超过两个(BC1/BC7或PVRTC 4bpp/2bpp)。由于不能使用5bpp的压缩级别(如果4bpp的质量略显不足),必须使用8bpp的选项。它使带宽增加了一倍,但质量却没有明显改善。
- 支持2D和3D纹理。
一个新的压缩方案是在考虑了所有这些要求后开发的。ASTC格式有一个固定大小的128位块。然而,对于2D纹理来说,编码的Tile尺寸从4x4到12x12 texels不等,对于3D纹理来说,从3x3x3到6x6x6不等。所有支持的Tile尺寸和相应的比特率可以在上表中找到。增量 "一栏显示,比特率可以以非常细的步骤扩展。在ASTC规范中,Tile尺寸也被称为块足迹。
ASTC是我们文章中描述的最灵活的格式,因为它也支持LDR、HDR、2D和3D纹理,最多有4个组件。它甚至支持低于1bpp的比特率。
从概念上讲,ASTC与S3TC/BC7类似:一个压缩块中最多存储四个端点对和插值权重,只支持预定义的分区,特定的分区由分区ID指定,也存储在一个块中。在弱相关的情况下,独立的索引表被存储在该通道中。每个独立的编码被称为一个平面。
也许,最主要和最有趣的ASTC创新是用小数位对整数值进行编码的技术,称为BISE。同时,BISE可以在硬件中有效实现。
参考链接:ASTC纹理压缩格式详解
总结
画质比较
RGBA > ASTC 4×4 > ASTC 6×6 > ETC2 ≈ ETC1
四、实际应用中的选择
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以下版本;
英伟达和Unity官方对于不同类型贴图给出了不同的压缩方案建议:
Using ASTC Texture Compression for Game Assets | NVIDIA Developer
Unity - Manual: Recommended, default, and supported texture formats, by platform
参考链接:
http://sv-journal.org/2014-1/06/en/index.php?lang=en#5-1
你所需要了解的几种纹理压缩格式原理 - 知乎
ASTC纹理压缩格式详解 - 知乎
Using ASTC Texture Compression for Game Assets | NVIDIA Developer
Unity - Manual: Recommended, default, and supported texture formats, by platform