BC6H 格式是一种纹理压缩格式,旨在支持源数据中的高动态范围(HDR)颜色空间。
关于 BC6H/DXGI_FORMAT_BC6H
BC6H 格式为使用三个 HDR 颜色通道的图像提供高质量的压缩,每个颜色通道的 16 位值(16:16:16)。 不支持 alpha 通道。
BC6H 由以下DXGI_FORMAT枚举值指定:
- DXGI_FORMAT_BC6H_TYPELESS。
- DXGI_FORMAT_BC6H_UF16。 此 BC6H 格式不使用 16 位浮点颜色通道值的登录位。
- DXGI_FORMAT_BC6H_SF16。此 BC6H 格式使用 16 位浮点颜色通道值的登录位。
注意
颜色通道的 16 位浮点格式通常称为“半”浮点格式。 此格式具有以下位布局:
格式 | 位布局 |
---|---|
UF16 (无符号浮点数) | 5 个指数位 + 11 个 mantissa 位 |
SF16 (带符号浮点) | 1 个符号位 + 5 个指数位 + 10 个 mantissa 位 |
BC6H 格式可用于 Texture2D(包括数组)、Texture3D 或 TextureCube(包括数组)纹理资源。 同样,此格式适用于与这些资源关联的任何 MIP 映射图面。
BC6H 使用固定块大小 16 字节(128 位),固定磁贴大小为 4x4 纹素。 与以前的 BC 格式一样,大于支持的磁贴大小(4x4)的纹理图像使用多个块进行压缩。 此寻址标识也适用于三维图像、MIP 地图、多维数据集地图和纹理数组。 所有图像磁贴的格式必须相同。
有关 BC6H 格式的一些重要说明:
- BC6H 支持浮点非规范化,但不支持 INF(无穷大)和 NaN(而不是数字)。 例外情况是 BC6H(DXGI_FORMAT_BC6H_SF16)的有符号模式,它支持 -INF(负无穷大)。 请注意,此对 -INF 的支持只是格式本身的项目,此格式的编码器并不特别支持。 通常,当编码器遇到 INF(正或负)或 NaN 输入数据时,它们应\ 将该数据转换为允许的最大非 INF 表示值,并将 NaN 映射到压缩前的 0。
- BC6H 不支持 alpha 通道。
- BC6H 解码器在执行纹理筛选之前执行解压缩。
- BC6H 解压缩必须准确;也就是说,硬件必须返回与本文档中所述的解码器相同的结果。
BC6H 实现
BC6H 块由模式位、压缩终结点、压缩索引和可选分区索引组成。 此格式指定 14 种不同的模式。
终结点颜色存储为 RGB 三重。 BC6H 在多个定义的颜色终结点的大致线条上定义颜色调色板。 此外,根据模式,磁贴可以分为两个区域或被视为单个区域,其中两个区域磁贴为每个区域提供一组单独的颜色终结点。 BC6H 为每个纹素存储一个调色板索引。
在两个区域的情况下,有 32 个可能的分区。
解码 BC6H 格式
下面的伪代码显示了在给定 16 字节 BC6H 块的情况下解压缩像素(x,y)的步骤。
decompress_bc6h(x, y, block)
{
mode = extract_mode(block);
endpoints;
index;
if(mode.type == ONE)
{
endpoints = extract_compressed_endpoints(mode, block);
index = extract_index_ONE(x, y, block);
}
else //mode.type == TWO
{
partition = extract_partition(block);
region = get_region(partition, x, y);
endpoints = extract_compressed_endpoints(mode, region, block);
index = extract_index_TWO(x, y, partition, block);
}
unquantize(endpoints);
color = interpolate(index, endpoints);
finish_unquantize(color);
}
下表包含 BC6H 块的 14 种可能格式中的每一种的位计数和值。
模式 | 分区索引 | 分区 | 颜色终结点 | 模式位 |
---|---|---|---|---|
1 | 46 位 | 5 位 | 75 位(10.555、10.555、10.555) | 2 位 (00) |
2 | 46 位 | 5 位 | 75 位 (7666, 7666, 7666) | 2 位 (01) |
3 | 46 位 | 5 位 | 72 位(11.555、11.444、11.444) | 5 位 (00010) |
4 | 46 位 | 5 位 | 72 位(11.444、11.555、11.444) | 5 位 (00110) |
5 | 46 位 | 5 位 | 72 位(11.444、11.444、11.555) | 5 位 (01010) |
6 | 46 位 | 5 位 | 72 位(9555、9555、9555) | 5 位 (01110) |
7 | 46 位 | 5 位 | 72 位 (8666, 8555, 8555) | 5 位 (10010) |
8 | 46 位 | 5 位 | 72 位 (8555, 8666, 8555) | 5 位 (10110) |
9 | 46 位 | 5 位 | 72 位 (8555, 8555, 8666) | 5 位 (11010) |
10 | 46 位 | 5 位 | 72 位 (6666, 6666, 6666) | 5 位 (11110) |
11 | 63 位 | 0 位 | 60 位(10.10、10.10、10.10) | 5 位 (00011) |
12 | 63 位 | 0 位 | 60 位(11.9、11.9、11.9) | 5 位 (00111) |
13 | 63 位 | 0 位 | 60 位(12.8、12.8、12.8) | 5 位 (01011) |
14 | 63 位 | 0 位 | 60 位(16.4、16.4、16.4) | 5 位 (01111) |
此表中的每种格式都可以由模式位唯一标识。 前十种模式用于两个区域磁贴,模式位字段可以是两位或五位长。 这些块还具有压缩颜色终结点(72 或 75 位)、分区(5 位)和分区索引(46 位)的字段。
对于压缩的颜色终结点,上表中的值记下存储的 RGB 终结点的精度,以及用于每个颜色值的位数。 例如,模式 3 指定颜色终结点精度级别为 11,以及用于存储已转换终结点的增量值(分别为 5、4 和 4)。 模式 10 不使用增量压缩,而是显式存储所有四个颜色终结点。
最后四种块模式用于一个区域磁贴,其中模式字段为 5 位。 这些块具有终结点(60 位)和压缩索引(63 位)的字段。 模式 11(如模式 10)不使用增量压缩,而是显式存储两个颜色终结点。
保留模式 10011、10111、11011 和 11111(未显示)。 请勿在编码器中使用这些值。 如果硬件是通过其中一种指定的模式传递的块,则生成的解压缩块必须包含除 alpha 通道以外的所有通道中的所有零。
对于 BC6H,无论模式如何,alpha 通道都必须始终返回 1.0。
BC6H 分区集
两个区域磁贴有 32 个可能的分区集,下表中定义了这些分区集。 每个 4x4 块表示单个形状。
表
在此分区集表中,加粗和带下划线的条目是子集 1 的修复索引的位置(用一个较少的位指定)。 子集 0 的修复索引始终为索引 0,因为分区始终排列,以便索引 0 始终位于子集 0 中。 分区顺序从左上到右下,从左到右移动,然后从上到下。
BC6H 压缩终结点格式
位字段
此表显示压缩终结点的位字段作为终结点格式的函数,每个列都指定一个编码,每行指定一个位字段。 此方法对于两个区域磁贴最多需要 82 位,一个区域磁贴需要 65 位。 例如,上面一个区域 [16 4] 编码的前 5 位(特别是最右边的列)是位 m[4:0],接下来的 10 位是位 rw[9:0],等等,最后 6 位包含 bw[10:15]。
上表中的字段名称定义如下:
田 | 变量 |
---|---|
m | 模式 |
d | 形状索引 |
乌尔曼 | endpt[0]。A[0] |
rx | endpt[0]。B[0] |
ry | endpt[1]。A[0] |
rz | endpt[1]。B[0] |
gw | endpt[0]。A[1] |
gx | endpt[0]。B[1] |
gy | endpt[1]。A[1] |
gz | endpt[1]。B[1] |
bw | endpt[0]。A[2] |
bx | endpt[0]。B[2] |
由 | endpt[1]。A[2] |
bz | endpt[1]。B[2] |
Endpt[i],其中 i 为 0 或 1,分别引用第 0 组或第 1 组终结点。
终结点值的签名扩展
对于两个区域磁贴,可以对四个终结点值进行签名扩展。 Endpt[0]。仅当格式为有符号格式时,才对 A 进行签名;仅当终结点已转换或格式为有符号格式时,才会对其他终结点进行签名。 下面的代码演示了用于扩展两个区域终结点值的符号的算法。
static void sign_extend_two_region(Pattern &p, IntEndpts endpts[NREGIONS_TWO])
{
for (int i=0; i<NCHANNELS; ++i)
{
if (BC6H::FORMAT == SIGNED_F16)
endpts[0].A[i] = SIGN_EXTEND(endpts[0].A[i], p.chan[i].prec);
if (p.transformed || BC6H::FORMAT == SIGNED_F16)
{
endpts[0].B[i] = SIGN_EXTEND(endpts[0].B[i], p.chan[i].delta[0]);
endpts[1].A[i] = SIGN_EXTEND(endpts[1].A[i], p.chan[i].delta[1]);
endpts[1].B[i] = SIGN_EXTEND(endpts[1].B[i], p.chan[i].delta[2]);
}
}
}
对于单区域磁贴,行为相同,仅删除 endpt[1]。
static void sign_extend_one_region(Pattern &p, IntEndpts endpts[NREGIONS_ONE])
{
for (int i=0; i<NCHANNELS; ++i)
{
if (BC6H::FORMAT == SIGNED_F16)
endpts[0].A[i] = SIGN_EXTEND(endpts[0].A[i], p.chan[i].prec);
if (p.transformed || BC6H::FORMAT == SIGNED_F16)
endpts[0].B[i] = SIGN_EXTEND(endpts[0].B[i], p.chan[i].delta[0]);
}
}
转换终结点值的反转
对于双区域磁贴,转换应用差异编码的反函数,在 endpt[0] 中添加基值。其他三个条目的 A 到总共 9 个添加作。 在下图中,基值表示为“A0”,并且具有最高的浮点精度。 “A1”、“B0”和“B1”都是从定位点值计算得出的增量值,这些增量值以较低的精度表示。 (A0 对应于 endpt[0]。A,B0 对应于 endpt[0]。B、A1 对应于 endpt[1]。A 和 B1 对应于 endpt[1].B.)
计算
对于单区域磁贴,只有一个增量偏移量,因此只有 3 个添加作。
解压缩程序必须确保反向转换的结果不会溢出 endpt[0].a 的精度。 对于溢出,由反向转换生成的值必须包装在同一个位数内。 如果 A0 的精度为“p”位,则转换算法为:
B0 = (B0 + A0) & ((1 << p) - 1)
对于有符号格式,增量计算的结果也必须进行扩展。 如果符号扩展作考虑扩展这两个符号,其中 0 为正,1 为负,则符号扩展 0 负责上述固定。 等效地,在上面的固定之后,只需将值 1(负数)进行签名扩展。
颜色终结点的未量化
给定未压缩的终结点,下一步是执行颜色终结点的初始未量化。 这涉及三个步骤:
- 调色板的取消量化
- 调色板的内插
- 非量化最终化
将非量子化过程分为两个部分(内插前的调色板未量化和内插后最终的未量化)可减少在调色板内插之前与完全无量子化过程相比所需的乘法作数。
下面的代码演示了检索原始 16 位颜色值的估计过程,然后使用提供的权重值向调色板添加 6 个其他颜色值。 在每个通道上执行相同的作。
int aWeight3[] = {0, 9, 18, 27, 37, 46, 55, 64};
int aWeight4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64};
// c1, c2: endpoints of a component
void generate_palette_unquantized(UINT8 uNumIndices, int c1, int c2, int prec, UINT16 palette[NINDICES])
{
int* aWeights;
if(uNumIndices == 8)
aWeights = aWeight3;
else // uNumIndices == 16
aWeights = aWeight4;
int a = unquantize(c1, prec);
int b = unquantize(c2, prec);
// interpolate
for(int i = 0; i < uNumIndices; ++i)
palette[i] = finish_unquantize((a * (64 - aWeights[i]) + b * aWeights[i] + 32) >> 6);
}
下一个代码示例演示了内插过程,并进行了以下观察:
- 由于 未量化 函数(以下)的完整颜色值范围从 -32768 到 65535,因此内插器是使用 17 位带符号算术实现的。
- 内插后,这些值将传递到 finish_unquantize 函数(在本部分中的第三个示例中所述),该函数应用最终缩放。
- 所有硬件解压缩器都需要使用这些函数返回位准确结果。
int unquantize(int comp, int uBitsPerComp)
{
int unq, s = 0;
switch(BC6H::FORMAT)
{
case UNSIGNED_F16:
if(uBitsPerComp >= 15)
unq = comp;
else if(comp == 0)
unq = 0;
else if(comp == ((1 << uBitsPerComp) - 1))
unq = 0xFFFF;
else
unq = ((comp << 16) + 0x8000) >> uBitsPerComp;
break;
case SIGNED_F16:
if(uBitsPerComp >= 16)
unq = comp;
else
{
if(comp < 0)
{
s = 1;
comp = -comp;
}
if(comp == 0)
unq = 0;
else if(comp >= ((1 << (uBitsPerComp - 1)) - 1))
unq = 0x7FFF;
else
unq = ((comp << 15) + 0x4000) >> (uBitsPerComp-1);
if(s)
unq = -unq;
}
break;
}
return unq;
}
调色板内插后调用 finish_unquantize。 取消量化 函数将已签名的缩放推迟 31/32,31/64 用于未签名。 完成调色板内插后,必须将最终值获取到有效的半范围(-0x7BFF ~ 0x7BFF),以减少必要的乘数。 finish_unquantize 应用最终缩放,并返回 未签名的短 值,该值将被重新解释为 半。
unsigned short finish_unquantize(int comp)
{
if(BC6H::FORMAT == UNSIGNED_F16)
{
comp = (comp * 31) >> 6; // scale the magnitude by 31/64
return (unsigned short) comp;
}
else // (BC6H::FORMAT == SIGNED_F16)
{
comp = (comp < 0) ? -(((-comp) * 31) >> 5) : (comp * 31) >> 5; // scale the magnitude by 31/32
int s = 0;
if(comp < 0)
{
s = 0x8000;
comp = -comp;
}
return (unsigned short) (s | comp);
}
}
相关主题
-
在 Direct3D 11 中 纹理块压缩