纹理压缩
本文以 ASTC 为主线,介绍块纹理压缩的基本原理、编码与解码流程,并对比 ETC 与 BC 两大主流格式。
目录
1. 简介
1.1 为什么需要纹理压缩
纹理通常是游戏和实时渲染中占用显存最多的资源之一。一张 2048 × 2048 的 RGBA8 纹理约占 16 MiB,如果有 Mipmap、法线贴图、遮罩、光照贴图和 UI 图集,显存占用会迅速上升。除了容量,纹理还会持续消耗:
- 显存带宽:采样、Mipmap、各向异性过滤都会读取纹理数据;
- 缓存容量:纹理越大,越容易造成缓存未命中;
- 功耗:移动设备上,内存访问往往比算术运算更昂贵;
- 加载时间:未压缩资源的传输和解码成本更高。
通用无损压缩(如 PNG、ZIP)可以减少磁盘体积,但不适合直接用于 GPU 实时采样,因为它的数据长度不固定、随机访问困难,而且每次采样都可能需要昂贵的串行解压。纹理压缩采用的是另一种思路:块压缩(Block Compression)。
1.2 块压缩的基本模型
块压缩把纹理切成固定大小的小块,每个块独立编码、独立解码。常见块尺寸是 4 × 4 texel,也有 5 × 5、6 × 6、8 × 8、12 × 12 等。
一个压缩块通常包含以下信息:
| 概念 | 作用 |
|---|---|
| 端点(Endpoint) | 定义块内颜色渐变的起点和终点,通常是两个颜色 |
| 权重 / 索引(Weight / Index) | 表示某个 texel 落在两个端点之间的哪一级 |
| 分区(Partition) | 把块内 texel 分成若干区域,每个区域使用不同端点 |
| 权重网格(Weight Grid) | 以低于 texel 分辨率的网格保存权重,解码时插值 |
| 块模式(Block Mode) | 告诉解码器如何解释块内的位流 |
块压缩有三个非常重要的工程性质:
- 固定码率:每个块固定占用若干字节,因此可以精确计算显存和带宽;
- 随机访问:解码一个块不依赖其他块,GPU 可以在任意位置直接读取;
- 硬件解码:格式由 GPU 固定功能单元解码,采样时不需要运行通用计算程序。
代价是有损压缩。解码出来的纹理不是原始像素,而是在端点、权重和量化精度限制下得到的近似结果。
1.3 比特率与块大小
压缩格式常用 bpp(bits per pixel) 描述平均每个像素占用的位数:
bpp = 每块位数 / (块宽 × 块高)
例如:
- ASTC 每块固定 128 bit,4 × 4 块为
128 / 16 = 8 bpp; - ASTC 8 × 8 块为
128 / 64 = 2 bpp; - ASTC 12 × 12 块为
128 / 144 ≈ 0.89 bpp; - ETC1 / BC1 每块 64 bit,4 × 4 块为 4 bpp;
- BC3 / BC7 每块 128 bit,4 × 4 块为 8 bpp。
块越大,每个 texel 分到的位数越少,压缩率越高,但块内可表达的细节也越少。对于文字、边缘、法线、粗糙度遮罩等高频内容,盲目使用大块会造成明显的块状伪影。
1.4 主流纹理压缩类型
| 格式家族 | 典型块大小 | 每块位数 | 典型 bpp | 主要特点 |
|---|---|---|---|---|
| ASTC | 4 × 4 至 12 × 12 | 128 bit | 0.89–8 | 块尺寸可调,LDR/HDR、1–4 通道,跨平台 |
| ETC1 | 4 × 4 | 64 bit | 4 | 仅 RGB,移动端 ES 2.0 普及 |
| ETC2 / EAC | 4 × 4 | 64 bit / 128 bit | 4 / 8 | ETC2 增加更多模式和 RGBA 支持 |
| BC1–BC7 | 4 × 4 | 64 bit / 128 bit | 4 / 8 | 桌面 GPU 常用,BC6H 支持 HDR,BC7 支持高质量 RGBA |
| PVRTC | 可变 | — | 2–4 | PowerVR 平台传统格式,现代项目使用减少 |
在现代跨平台项目中,常见的组合是:
- ASTC 为主:移动端和现代桌面端统一使用,块尺寸按内容选择;
- ETC2 作为 Android 回退:覆盖不支持 ASTC 的旧设备;
- BC 系列作为桌面/DirectX 路径:BC7 用于高质量 RGBA,BC6H 用于 HDR,BC5 用于法线。
1.5 解码与采样的关系
需要区分“块”和“texel”:
- 块是显存中的压缩数据单元,例如一个 ASTC 块固定 128 bit;
- texel 是解码后纹理中的一个像素颜色。
GPU 采样纹理时,会定位到覆盖采样位置的压缩块,读出 128 bit(或 64 bit)块数据,解出块内的 texel 颜色,再在 texel 颜色上执行双线性、三线性或各向异性过滤。采样不是直接对 128 bit 做插值,而是对解码后的 texel 颜色做过滤。
2. ASTC
2.1 ASTC 的设计目标
ASTC(Adaptive Scalable Texture Compression)是 Khronos 制定的块纹理压缩标准,目标是用同一套编码框架覆盖从高质量到高压缩率、从 LDR 到 HDR、从 RGB 到 RGBA 的广泛场景。
ASTC 最核心的两个特点:
- 每块固定 128 bit(16 字节);
- 块尺寸可选,因此比特率可以在约 0.89–8 bpp 之间变化。
ASTC 规范允许的二维块尺寸共有 14 种:
| 块尺寸 | texel 数 | bpp |
|---|---|---|
| 4 × 4 | 16 | 8.00 |
| 5 × 4 | 20 | 6.40 |
| 5 × 5 | 25 | 5.12 |
| 6 × 5 | 30 | 4.27 |
| 6 × 6 | 36 | 3.56 |
| 8 × 5 | 40 | 3.20 |
| 8 × 6 | 48 | 2.67 |
| 8 × 8 | 64 | 2.00 |
| 10 × 5 | 50 | 2.56 |
| 10 × 6 | 60 | 2.13 |
| 10 × 8 | 80 | 1.60 |
| 10 × 10 | 100 | 1.28 |
| 12 × 10 | 120 | 1.07 |
| 12 × 12 | 144 | 0.89 |
注意:表中的 5 × 4 和 4 × 5 在编码语义上是等价的旋转关系,具体存储时以规范定义的块尺寸编码为准。
2.2 ASTC 的编码流程
ASTC 的编码器不是按固定格式往 128 bit 里填字段,而是先选择一种块模式(Block Mode),再由该模式决定后续字段如何解释。
一个典型的编码流程如下:
选择块尺寸和 Block Mode
│
▼
把块划分成 1–4 个 partition
│
▼
为每个 partition 估计两个颜色端点
│
▼
选择端点量化模式和权重网格
│
▼
把每个 texel 投影到端点渐变线上,得到权重
│
▼
量化端点与权重,打包成 128 bit
2.2.1 选择块模式
块模式决定了:
- 块尺寸;
- partition 的数量和模式;
- 端点量化方式;
- 权重网格尺寸;
- 权重位宽;
- 是否使用 dual-plane;
- 颜色分量和 HDR/LDR 模式。
因此,ASTC 的位流没有一个“从 bit 0 到 bit 127 永远不变”的布局。Block Mode 是解码器理解其余位的入口。
2.2.2 分区
一个块内的颜色可能不属于同一条渐变线。例如一个 8 × 8 块里同时有皮肤、头发和背景,单个 Min/Max 端点无法拟合。
ASTC 支持把块分成 1–4 个 partition。每个 partition 有自己的一对端点,并共享或独立使用权重信息。分区模式由块模式和分区索引共同决定,解码端可以通过规范中的哈希函数还原每个 texel 属于哪个 partition。
分区不是任意多边形划分,而是规范定义的有限模式集合。它足以把块内颜色聚类到几个区域,在比特预算内显著改善质量。
2.2.3 端点
对每个 partition,编码器需要选择两个端点,通常表示该区域颜色分布的两个极值或主方向端点。
在简化编码器中,可以直接取 partition 内所有 texel 的 min/max 颜色:
E0 = min(color of texels)
E1 = max(color of texels)
更高质量的编码器会搜索端点位置,或者使用主成分分析、最小二乘、误差迭代等方法,使所有 texel 到端点渐变线的整体误差最小。
ASTC 的端点量化不是只有 8 bit 一种选择。常见端点量化模式包括:
- 8 bit;
- 6 bit;
- 5 bit + trit(三进制值);
- 4 bit + quint(五进制值)。
trit 和 quint 的意义在于:用非 2 的幂的量化级数,在有限位数内获得更细的端点精度。例如 QUANT80 每通道有 80 个级别,接近 6.32 bit 的表达能力,但实际只需要 4 bit + 一个 quint 分量。
这里容易产生误解:“两个端点”不是每个 texel 都存两个颜色。 端点属于整个 partition,一个块通常只存少量的端点;大量 texel 通过权重共享这对端点。
2.2.4 权重与权重网格
给定端点 E0、E1,一个 texel 的颜色可以写成:
color = E0 + t × (E1 - E0)
其中 t 是 0 到 1 之间的插值参数。编码器把 texel 颜色投影到 E0 → E1 的向量上,求出 t,再量化为 n bit:
t = dot(p - E0, E1 - E0) / dot(E1 - E0, E1 - E0)
Index = clamp(round(t × (2ⁿ - 1)), 0, 2ⁿ - 1)
实际实现中通常会预先计算每块的缩放和偏置:
Range = E1 - E0
Scale = (2ⁿ - 1) / dot(Range, Range)
Scaled = Range × Scale
Bias = (dot(E0, E0) - dot(E1, E0)) × Scale
Index = clamp(round(dot(p, Scaled) + Bias), 0, 2ⁿ - 1)
这样每个 texel 只需要一次 dot、一次加法和一次 round。
ASTC 的权重不一定是“每个 texel 一个”。规范允许权重网格的尺寸小于块尺寸,解码时通过双线性插值把粗网格扩展到每个 texel。这样做可以节省大量权重位,尤其适合 8 × 8、10 × 10、12 × 12 等大块。
2.2.5 打包成 128 bit
最终编码器需要把以下信息写入 128 bit:
- Block Mode;
- partition 数量和 partition 模式;
- 每个 partition 的端点量化值;
- 权重网格尺寸与权重位宽;
- 每个网格点的量化权重;
- dual-plane、HDR、通道模式等标志。
由于不同 Block Mode 的字段数量不同,ASTC 的打包逻辑必须严格遵循规范的位布局。解码器不能靠“猜”字段,只能先解析 Block Mode,再按对应模式读取。
2.3 ASTC 的解码流程
解码端大致执行以下步骤:
读取 128 bit 块
│
▼
解析 Block Mode
│
▼
确定块尺寸、partition 和端点模式
│
▼
反量化端点,得到 E0、E1
│
▼
读取权重网格,按 texel 位置双线性插值
│
▼
color = E0 + w / (2ⁿ - 1) × (E1 - E0)
│
▼
输出 texel 颜色
对于 dual-plane 模式,RGB 和 Alpha 可以使用不同的权重网格;对于 HDR 模式,端点反量化和颜色表示会使用不同的数值范围。无论哪种模式,核心结构都可以概括为:
端点 + 权重 + 分区 + 插值
2.3.1 权重网格如何对应到 texel
假设一个 8 × 8 块使用 4 × 4 权重网格。4 × 4 网格不是只有 4 个值,而是 4 列 × 4 行 = 16 个权重值。每个网格点位于一个 2 × 2 texel 单元的中心:
8 × 8 texel 块 4 × 4 权重网格
┌────┬────┬────┬────┐
│ G00│ G01│ G02│ G03│ G00 覆盖左上 2 × 2 texel
├────┼────┼────┼────┤ G01 覆盖下一组 2 × 2 texel
│ G10│ G11│ G12│ G13│ ...
├────┼────┼────┼────┤
│ G20│ G21│ G22│ G23│
├────┼────┼────┼────┤
│ G30│ G31│ G32│ G33│
└────┴────┴────┴────┘
解码某个 texel 时,先根据它的块内坐标计算网格坐标:
gx = (x + 0.5) × gridWidth / blockWidth - 0.5
gy = (y + 0.5) × gridHeight / blockHeight - 0.5
然后把 gx、gy 夹取到合法范围,再对周围 2 × 2 个网格权重做双线性插值。例如 8 × 8 块、4 × 4 网格、texel (3,3):
gx = (3 + 0.5) × 4 / 8 - 0.5 = 1.25
gy = (3 + 0.5) × 4 / 8 - 0.5 = 1.25
它使用 G11、G12、G21、G22 四个权重:
w = 0.75 × 0.75 × G11
+ 0.75 × 0.25 × G12
+ 0.25 × 0.75 × G21
+ 0.25 × 0.25 × G22
这解释了为什么粗网格不会让整个块只有一个颜色:网格虽然粗,但每个 texel 仍然会得到独立的、连续变化的权重。
2.4 不同块大小的处理方式
ASTC 的编码框架相同,但块尺寸不同会导致明显的策略差异:
| 块尺寸 | 比特率 | 典型处理 |
|---|---|---|
| 4 × 4 | 8.00 bpp | 权重网格可以接近全分辨率,适合高质量颜色、法线、细节 |
| 5 × 4 / 5 × 5 | 6.40 / 5.12 bpp | 仍可保留较高权重精度,适合通用颜色纹理 |
| 6 × 5 / 6 × 6 | 4.27 / 3.56 bpp | 权重开始变紧,常需要减少权重位宽或使用更粗网格 |
| 8 × 5 / 8 × 6 / 8 × 8 | 3.20 / 2.67 / 2.00 bpp | 通常使用缩减权重网格和分区,适合中等频率内容 |
| 10 × 5 / 10 × 6 / 10 × 8 / 10 × 10 | 2.56–1.28 bpp | 需要更粗的网格、更多依赖分区和内容平滑性 |
| 12 × 10 / 12 × 12 | 1.07 / 0.89 bpp | 适合低频、大面积渐变或远距离物体,不适合文字和硬边 |
具体来说:
- 4 × 4:一个块只有 16 个 texel,128 bit 相对充裕。ASTC 可以给端点分配较高精度,并使用全尺寸权重网格。例如,一种单分区、全尺寸权重网格的简化编码器可以使用两个 RGB888 端点(48 bit)加 16 个 3-bit 权重(48 bit),其余位留给块模式和配置。高质量 ASTC 编码器还会在这个块尺寸上尝试多个 partition 和端点模式。
- 5 × 5:25 个 texel,如果使用 3-bit 权重需要 75 bit。例如使用 RGB666 端点时,两个端点约 36 bit,加上 75 bit 权重和模式位,仍能放进 128 bit。相比 4 × 4,它节省约 36% 的存储,但块内细节拟合能力下降。
- 6 × 6:36 个 texel。如果仍然使用 3-bit 权重,需要 108 bit,留给端点和模式的位太少。因此通常把权重降为 2-bit,并使用 QUANT80 端点,权重占 72 bit,端点和模式占其余部分。6 × 6 的质量通常已能看出下降,适合对体积敏感、内容较平滑的纹理。
- 8 × 8 及以上:权重位预算更紧。8 × 8 如果存 2-bit 权重且每个 texel 一个权重,就需要 128 bit,完全没有空间放端点、分区和模式。因此必须使用缩减权重网格,并通常配合分区。例如 8 × 8 块使用 4 × 4 网格、2-bit 权重时,权重只需 32 bit,剩下的位可以用于端点和配置。
- 10 × 10 / 12 × 12:块内 texel 数达到 100/144,几乎只能表达低频变化。编码器通常依赖更粗的权重网格、多个 partition 和内容自适应搜索;如果原图细节丰富,就会出现明显的块状模糊或颜色渗漏。
这里的“必须/通常”是工程上的实际约束:规范虽然定义了合法的模式组合,但 128 bit 的总预算决定了哪些组合真正可用。具体合法组合应以 ASTC 规范中的 block mode 表为准。
2.5 ASTC 的采样过程
渲染时对 ASTC 纹理采样,硬件大致执行:
- 根据 UV 和 Mip 选择定位纹理坐标;
- 找到覆盖该坐标的一个或多个 128 bit 块;
- 解码这些块中的 texel 颜色;
- 在解码后的 texel 颜色上做双线性/三线性/各向异性过滤;
- 返回最终的 RGBA 值。
硬件只解码采样所需的块,而不是一次性解码整张贴图。相邻采样点如果落在同一个块内,可以复用块解码结果和缓存。
3. ETC
3.1 ETC 的定位
ETC(Ericsson Texture Compression)是为移动端设计的块纹理压缩家族。它在 Android/OpenGL ES 生态中非常普及:
- ETC1:OpenGL ES 2.0 时代的基础格式,仅支持 RGB;
- ETC2:OpenGL ES 3.0 起强制支持,增加差分模式、T/H/Planar 模式和 EAC 透明通道;
- ETC2 + EAC:可以组合成 RGBA 压缩。
ETC 的基本块是固定的 4 × 4 texel。ETC1 的颜色块为 64 bit,因此是 4 bpp;ETC2 的 RGBA 通常由两个 64 bit 块组成,达到 8 bpp。
3.2 ETC1 的编码与解码
3.2.1 块结构
ETC1 的 64 bit 块通常包含:
- 两个子块(2 × 4 或 4 × 2);
- 每个子块一个基色(RGB444);
- 每个子块一个 3-bit 调制表索引;
- 每个 texel 一个 2-bit 索引;
- flip 位和 differential 位。
一个简化的逻辑布局如下:
┌──────────────────────────────────────────────┐
│ Base Color 0 │ Base Color 1 │ Table │ Indices │
│ RGB444 │ RGB444 │ 3×2 │ 2×16 │
└──────────────────────────────────────────────┘
64 bit / 4×4 块
3.2.2 解码公式
ETC1 的核心是“基色 + 调制表”:
color(texel) = BaseColor[subblock]
+ Modifier[tableIndex][pixelIndex]
每个 texel 的 2-bit 索引不是直接表示颜色,而是从 4 个调制值中选择一个。调制表中的值通常是正负亮度偏移,例如 {-8, -2, 2, 8} 这样的形式。这样,一个子块可以表示基色附近的四个明暗台阶。
3.2.3 编码流程
ETC1 编码器大致需要:
- 选择 flip 方向,把 4 × 4 块分成 2 × 4 或 4 × 2 两个子块;
- 对每个子块估计基色,通常取子块的平均色或颜色范围中心;
- 选择调制表,使每个 texel 的量化误差尽量小;
- 为每个 texel 选择最优的 2-bit 索引;
- 把基色、表索引、像素索引和标志位打包成 64 bit。
由于每个子块只有一个基色和一张调制表,ETC1 对色相变化和复杂渐变的表达能力有限。它适合颜色相对均匀的漫反射纹理,不适合高质量法线、文字和细节丰富的贴图。
3.3 ETC2 的扩展
ETC2 在保持 4 × 4 / 64 bit 结构的基础上,增加了多种模式:
Individual 模式
两个子块各自使用独立的 RGB444 基色。适合两个区域颜色差异较大的情况。
Differential 模式
第一个子块使用 RGB555 基色,第二个子块的颜色通过与第一个子块的 3-bit 有符号差值表示。这样可以在两个子块颜色接近时获得更高精度;如果差值超出范围,编码器会退回 individual 模式。
T 模式
T 模式使用两个颜色端点和一个中间色,适合双色内容和边缘。它比 ETC1 的“单基色 + 调制表”更灵活。
H 模式
H 模式从两个端点派生出更多明暗级别,适合平滑渐变和具有明显方向性的颜色变化。
Planar 模式
Planar 模式使用一个基色和两个斜率来表示平滑的颜色平面。它特别适合低频、缓变的区域。
EAC 透明通道
EAC(ETC2 Alpha Compression)是单独的 64 bit 单通道块,使用基值、倍数和逐 texel 索引表示 Alpha 或单通道数据。一个 ETC2 RGBA 纹理通常由一个 64 bit 颜色块和一个 64 bit Alpha 块组成,总计 128 bit / 4 × 4,即 8 bpp。
ETC2 的编码器需要在多种模式之间选择。通常会对每种模式计算误差,再选出最优模式;这也是 ETC2 编码比 ETC1 更复杂的原因。
3.4 ETC 的块大小与处理方式
ETC 家族没有 ASTC 那样可自由选择的块尺寸,通常固定为 4 × 4:
| 格式 | 每块位数 | bpp | 处理方式 |
|---|---|---|---|
| ETC1 RGB | 64 bit | 4 | 两个子块,各自基色 + 调制表 + 2-bit 索引 |
| ETC2 RGB | 64 bit | 4 | individual/differential/T/H/Planar 多模式选择 |
| ETC2 RGBA | 128 bit | 8 | 64 bit 颜色块 + 64 bit EAC Alpha 块 |
因为块尺寸固定,ETC 无法通过“把块变大”继续降低码率。要获得更低的码率,需要使用其他格式或降低分辨率。这也是 ASTC 在大块、低码率场景中更有弹性的原因之一。
3.5 ETC 的使用场景
- Android OpenGL ES 3.0 及以上设备的保底格式;
- 对兼容性要求高、不能假设 ASTC 可用的项目;
- 颜色相对简单、分辨率不高的漫反射贴图;
- 需要与 ETC1 旧资源保持兼容的项目。
在高通、Mali、PowerVR 等移动 GPU 上,ETC2 通常都有硬件解码支持。工程上常见策略是:优先 ASTC,ETC2 作为最低端设备回退。
4. BC
4.1 BC 家族概览
BC(Block Compression)是 Microsoft DirectX 体系中的块压缩格式家族,常见于桌面 GPU 和 DirectX 纹理管线。BC 系列通常使用固定的 4 × 4 块:
- BC1–BC3:颜色和 Alpha,64 bit 或 128 bit;
- BC4–BC5:单通道/双通道,常用于法线、粗糙度、遮罩;
- BC6H:HDR RGB,128 bit;
- BC7:高质量 RGBA,128 bit。
4.2 BC1
BC1(也称 DXT1)是最经典的颜色块格式:
64 bit / 4×4 块
┌──────────────┬──────────────┬────────────────┐
│ Endpoint 0 │ Endpoint 1 │ Pixel Indices │
│ RGB565·16bit │ RGB565·16bit │ 2bit × 16 │
└──────────────┴──────────────┴────────────────┘
解码时,两个 RGB565 端点生成四个候选颜色:
C0 = E0
C1 = E1
C2 = 2/3 × E0 + 1/3 × E1
C3 = 1/3 × E0 + 2/3 × E1
每个 texel 用 2-bit 索引选择其中一个颜色。通过调整端点顺序,BC1 还可以启用 1-bit Alpha 或透明模式。
BC1 的编码重点是选择两个端点和每个 texel 的 2-bit 索引,使整体误差最小。它适合不透明或简单透明颜色纹理,但不适合高质量渐变和法线。
4.3 BC2 与 BC3
BC2 使用 128 bit:
- 64 bit 颜色块,结构类似 BC1;
- 64 bit 显式 4-bit Alpha,每个 texel 一个 4-bit Alpha 值。
BC3 也使用 128 bit,但 Alpha 采用“端点 + 索引”的方式:
64 bit 颜色块(类似 BC1)
+ 64 bit Alpha 块
├─ 2 个 8-bit Alpha 端点
└─ 16 个 3-bit Alpha 索引
BC3 的 Alpha 渐变比 BC2 的 4-bit 显式 Alpha 更平滑,适合带柔和透明边缘的纹理。
4.4 BC4 与 BC5
BC4 是单通道格式,64 bit / 4 × 4:
- 两个 8-bit 通道端点;
- 16 个 3-bit 索引;
- 解码时在端点之间插值。
BC5 由两个 BC4 块组成,共 128 bit / 4 × 4,包含两个独立通道。它常用于法线贴图:把法线 X/Y 两个分量分别压缩,运行时根据 X/Y 重建 Z,避免直接压缩 RGB 法线导致的归一化误差。
4.5 BC6H 与 BC7
BC6H 面向 HDR RGB,128 bit / 4 × 4,支持半精度端点、分区和多种模式。它适合 HDR 环境贴图、光照贴图和高质量 HDR 颜色数据。
BC7 面向 LDR RGBA,128 bit / 4 × 4,提供 8 种模式。不同模式可以组合:
- 0–3 个 partition;
- 不同位宽的端点;
- 2–4 bit 的 per-texel 索引;
- 显式或隐含的 Alpha;
- 多种端点预测方式。
BC7 的编码器需要在多个模式、分区模式和端点组合中搜索,复杂度较高,但质量明显优于 BC1/BC3,特别适合高质量 UI、颜色纹理和带 Alpha 的贴图。
4.6 BC 的编码与解码核心
BC 家族的格式差异很大,但核心都可以抽象为:
对每个 partition:
选择端点
选择索引位宽和分区模式
为每个 texel 选择索引
解码:
color = endpoint0 + index / maxIndex × (endpoint1 - endpoint0)
BC1/BC3 的搜索空间相对较小;BC7 需要在 8 个模式、多个分区和不同端点量化之间搜索,因此编码速度和质量的取舍更明显。
4.7 BC 的块大小与平台
BC 通常固定为 4 × 4:
| 格式 | 每块位数 | bpp | 主要用途 |
|---|---|---|---|
| BC1 | 64 bit | 4 | RGB,简单透明 |
| BC2 | 128 bit | 8 | RGB + 4-bit Alpha |
| BC3 | 128 bit | 8 | RGB + 平滑 Alpha |
| BC4 | 64 bit | 4 | 单通道 |
| BC5 | 128 bit | 8 | 双通道、法线 |
| BC6H | 128 bit | 8 | HDR RGB |
| BC7 | 128 bit | 8 | 高质量 RGBA |
BC 主要在 DirectX / 桌面 GPU 上使用。移动端是否支持 BC 取决于 GPU、图形 API 和具体格式能力;在跨平台项目中,通常需要为不同平台准备 ASTC、ETC2 或 BC 的资源变体。
5. 格式对比与选型
5.1 核心对比
| 维度 | ASTC | ETC1 | ETC2 / EAC | BC1–BC7 |
|---|---|---|---|---|
| 块大小 | 4 × 4 至 12 × 12 | 固定 4 × 4 | 固定 4 × 4 | 固定 4 × 4 |
| 每块位数 | 128 bit | 64 bit | 64 / 128 bit | 64 / 128 bit |
| bpp | 0.89–8 | 4 | 4 / 8 | 4 / 8 |
| RGB 支持 | 是 | 是 | 是 | 是 |
| Alpha 支持 | 是 | 否 | ETC2 RGBA + EAC | BC2/BC3/BC7 |
| HDR | 是 | 否 | 否 | BC6H |
| 分区 | 1–4 | 两个子块 | 多模式 | BC7 支持 1–3 个分区 |
| 权重网格 | 可选缩减、双线性插值 | 固定 2-bit 索引 | 固定/模式相关 | 固定索引或模式相关 |
| 主要平台 | 移动 + 现代桌面 | 旧 Android | Android ES 3.0+ | DirectX / 桌面 |
| 典型定位 | 统一、可伸缩 | 兼容保底 | Android 保底 | 桌面高质量 |
5.2 选型建议
颜色纹理
- 高质量移动端:ASTC 5 × 5 或 6 × 6;
- 桌面高质量:BC7;
- Android 兼容回退:ETC2;
- 细节很多且显存充足:ASTC 4 × 4 或 BC7。
法线贴图
- ASTC 4 × 4 / 5 × 5;
- 桌面优先 BC5;
- 避免低质量 BC1/ETC1 直接压缩 RGB 法线。
带 Alpha 的纹理
- ASTC RGBA;
- BC3 或 BC7;
- ETC2 RGBA + EAC;
- 注意 Alpha 边缘的块状伪影,必要时使用更小块或独立 Alpha 通道。
HDR 纹理
- ASTC HDR;
- BC6H。
超低码率、远距离或低频内容
- ASTC 8 × 8、10 × 10、12 × 12;
- 配合 Mipmap 和流式加载;
- 不建议对文字、UI、硬边法线使用大块。
5.3 工程注意事项
- 尺寸对齐:块压缩要求纹理宽高是块尺寸的倍数。带 Mipmap 时,最低一级也要满足对齐要求。
- sRGB 标记:压缩格式本身不等于颜色空间,创建纹理时要正确设置 sRGB/线性标记。
- Mipmap:压缩前生成 Mipmap,或按 Mip 逐级压缩;不能简单把压缩块直接平均。
- 平台变体:同一资源通常需要 ASTC、ETC2、BC 等多个变体。
- 质量验证:压缩质量与内容强相关,必须用目标内容在目标 GPU 上验证,不能只看理论 bpp。
- 运行时代价:GPU 编码适合实时/运行时生成纹理,但完整 ASTC 搜索通常还是离线编码质量更高。
6. 参考资料
- Khronos Data Format Specification(ASTC、ETC 等):https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html
- ARM astc-encoder:https://github.com/ARM-software/astc-encoder
- Microsoft BC7 格式文档:https://learn.microsoft.com/en-us/windows/win32/direct3d11/bc7-format
- Microsoft BC6H 格式文档:https://learn.microsoft.com/en-us/windows/win32/direct3d11/bc6h-format
- 参考实现库:
UnityAstcGpuEncoder(当前项目,仅作参考实现记录)
附:一句话总结
纹理压缩的本质,是用固定大小的块、少量端点和小型权重网格近似表示大量 texel;ASTC 通过可变的块尺寸、分区、端点量化和权重网格,在质量、码率和平台覆盖之间提供了最大的可调空间。