BC7 格式
BC7 格式是一种用于对 RGB 和 RGBA 数据进行高质量压缩的纹理压缩格式。
有关 BC7 格式的块模式的信息,请参阅 BC7 格式模式参考。
关于 BC7/DXGI_FORMAT_BC7
BC7 由以下 DXGI_FORMAT 枚举值指定:
- DXGI_FORMAT_BC7_TYPELESS.
- DXGI_FORMAT_BC7_UNORM.
- DXGI_FORMAT_BC7_UNORM_SRGB.
BC7 格式可用于 Texture2D(包括阵列)、Texture3D 或 TextureCube(包括阵列)纹理资源。 同样,此格式适用于与这些资源相关联的任何 MIP 贴图表面。
BC7 使用 16 字节(128 位)的固定块大小和 4×4 纹素的固定磁贴大小。 与先前的 BC 格式一样,大于支持的磁贴大小 (4x4) 的纹理图像会通过使用多个块进行压缩。 此寻址标识也适用于三维图像和 MIP 贴图、立方体贴图和纹理阵列。 所有图像磁贴必须使用相同的格式。
BC7 可对三通道 (RGB) 和四通道 (RGBA) 固定点数据图像进行压缩。 通常,源数据是每颜色分量(通道)8 位,尽管该格式能够以每颜色分量更高位对源数据进行编码。 所有图像磁贴必须使用相同的格式。
BC7 解码器在应用纹理筛选之前执行解压缩。
BC7 解压缩硬件必须是位精确的;也就是说,该硬件返回的结果必须与此文档中所述的解码器返回的结果相同。
BC7 实现
BC7 实现可以使用在 16 字节(128 位)块的最低有效位中指定的模式指定 8 种模式之一。 该模式由零或更多位进行编码,使用值 0,后面跟着 1。
BC7 块可以包含多个终结点对。 出于本文档的目的,对应于终结点对的索引集可称为“子集”。此外,在某些块模式下,终结点表示形式编码为 ,同样,就本文档而言,应称为“RGBP”,其中“P”位表示终结点颜色分量中共享的最小有效位。 例如,如果该格式的终结点表示是“RGB 5.5.5.1”,则会将终结点解释为一个 RGB 6.6.6 值,其中 P 位的状态定义每个分量的最低有效位。 同样,对于使用某个 alpha 通道的源数据,如果该格式的表示是“RGBAP 5.5.5.5.1”,那么会将该终结点解释为 RGBA 6.6.6.6。 根据块模式,你可以单独为某个子集的全部两个终结点指定共享最低有效位(每个子集 2 个 P 位),或者为在某个子集的终结点之间共享的终结点指定共享最低有效位(每个子集 1 个 P 位)。
对于未显式编码 alpha 分量的 BC7 块,一个 BC7 块由多个模式位、多个分区位、多个压缩的终结点、多个压缩的索引和一个可选 P 位组成。 在这些块中,终结点有一个仅 RGB 表示,并且对于源数据中的所有纹素,alpha 分量被解码为 1.0。
对于已组合了颜色分量和 alpha 分量的 BC7 块,一个块由多个模式位、多个压缩的终结点、多个压缩的索引以及多个可选分区位和一个 P 位组成。 在这些块中,终结点颜色是用 RGBA 格式表示的,而 alpha 分量值会与颜色分量值一起以内插值替换。
对于具有单独的颜色和 alpha 分量的 BC7 块,一个块由多个模式位、多个旋转位、多个压缩的终结点、多个压缩的索引和一个可选索引选择器位组成。 这些块具有单独编码的有效 RG B向量 [R, G, B] 和标量 alpha 通道 [A]。
下表列出了每个块类型的分量。
BC7 块包含的项目 | 多个模式位 | 多个旋转位 | 索引选择器位 | 多个分区位 | 多个压缩终结点 | P 位 | 多个压缩的索引 |
---|---|---|---|---|---|---|---|
仅颜色分量 | 必需 | 空值 | 空值 | 必需 | 必需 | 可选 | 必需 |
组合的颜色 + alpha | 必需 | 空值 | 空值 | 可选 | 必需 | 可选 | 必需 |
单独的颜色和 alpha | 必需 | 必需 | 可选 | 空值 | 必需 | 空值 | 必需 |
BC7 在两个终结点之间的近似线上定义一个调色板。 该模式值确定每个块的内插终结点对的数量。 BC7 为每个纹素存储一个调色板索引。
对于与一对终结点对应的每个索引子集,编码器会修复该子集的压缩索引数据的一位的状态。 它完成此操作的方法是:选择一个终结点顺序,该顺序允许指定的“固定”索引的索引将其最高有效位设置为 0,然后就可以丢弃该最高有效位,为每个子集保存一位。 对于仅有一个子集的块模式,固定索引始终为索引 0。
解码 BC7 格式
以下伪代码概述了对于 16 字节 BC7 块,解压缩 (x,y) 处的像素的步骤。
decompress_bc7(x, y, block)
{
mode = extract_mode(block);
//decode partition data from explicit partition bits
subset_index = 0;
num_subsets = 1;
if (mode.type == 0 OR == 1 OR == 2 OR == 3 OR == 7)
{
num_subsets = get_num_subsets(mode.type);
partition_set_id = extract_partition_set_id(mode, block);
subset_index = get_partition_index(num_subsets, partition_set_id, x, y);
}
//extract raw, compressed endpoint bits
UINT8 endpoint_array[2 * num_subsets][4] = extract_endpoints(mode, block);
//decode endpoint color and alpha for each subset
fully_decode_endpoints(endpoint_array, mode, block);
//endpoints are now complete.
UINT8 endpoint_start[4] = endpoint_array[2 * subset_index];
UINT8 endpoint_end[4] = endpoint_array[2 * subset_index + 1];
//Determine the palette index for this pixel
alpha_index = get_alpha_index(block, mode, x, y);
alpha_bitcount = get_alpha_bitcount(block, mode);
color_index = get_color_index(block, mode, x, y);
color_bitcount = get_color_bitcount(block, mode);
//determine output
UINT8 output[4];
output.rgb = interpolate(endpoint_start.rgb, endpoint_end.rgb, color_index, color_bitcount);
output.a = interpolate(endpoint_start.a, endpoint_end.a, alpha_index, alpha_bitcount);
if (mode.type == 4 OR == 5)
{
//Decode the 2 color rotation bits as follows:
// 00 – Block format is Scalar(A) Vector(RGB) - no swapping
// 01 – Block format is Scalar(R) Vector(AGB) - swap A and R
// 10 – Block format is Scalar(G) Vector(RAB) - swap A and G
// 11 - Block format is Scalar(B) Vector(RGA) - swap A and B
rotation = extract_rot_bits(mode, block);
output = swap_channels(output, rotation);
}
}
以下伪代码概述了在给定 16 字节 BC7 块的情况下,针对每个子集完全解码终结点颜色和 alpha 组件的步骤。
fully_decode_endpoints(endpoint_array, mode, block)
{
//first handle modes that have P-bits
if (mode.type == 0 OR == 1 OR == 3 OR == 6 OR == 7)
{
for each endpoint i
{
//component-wise left-shift
endpoint_array[i].rgba = endpoint_array[i].rgba << 1;
}
//if P-bit is shared
if (mode.type == 1)
{
pbit_zero = extract_pbit_zero(mode, block);
pbit_one = extract_pbit_one(mode, block);
//rgb component-wise insert pbits
endpoint_array[0].rgb |= pbit_zero;
endpoint_array[1].rgb |= pbit_zero;
endpoint_array[2].rgb |= pbit_one;
endpoint_array[3].rgb |= pbit_one;
}
else //unique P-bit per endpoint
{
pbit_array = extract_pbit_array(mode, block);
for each endpoint i
{
endpoint_array[i].rgba |= pbit_array[i];
}
}
}
for each endpoint i
{
// Color_component_precision & alpha_component_precision includes pbit
// left shift endpoint components so that their MSB lies in bit 7
endpoint_array[i].rgb = endpoint_array[i].rgb << (8 - color_component_precision(mode));
endpoint_array[i].a = endpoint_array[i].a << (8 - alpha_component_precision(mode));
// Replicate each component's MSB into the LSBs revealed by the left-shift operation above
endpoint_array[i].rgb = endpoint_array[i].rgb | (endpoint_array[i].rgb >> color_component_precision(mode));
endpoint_array[i].a = endpoint_array[i].a | (endpoint_array[i].a >> alpha_component_precision(mode));
}
//If this mode does not explicitly define the alpha component
//set alpha equal to 1.0
if (mode.type == 0 OR == 1 OR == 2 OR == 3)
{
for each endpoint i
{
endpoint_array[i].a = 255; //i.e. alpha = 1.0f
}
}
}
要为每个子集生成每个内插分量,请使用以下算法:让“c”成为要生成的分量;让“e0”成为子集的终结点 0 的分量;并让“e1”成为子集的终结点 1 的分量。
UINT16 aWeights2[] = {0, 21, 43, 64};
UINT16 aWeights3[] = {0, 9, 18, 27, 37, 46, 55, 64};
UINT16 aWeights4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64};
UINT8 interpolate(UINT8 e0, UINT8 e1, UINT8 index, UINT8 indexprecision)
{
if(indexprecision == 2)
return (UINT8) (((64 - aWeights2[index])*UINT16(e0) + aWeights2[index]*UINT16(e1) + 32) >> 6);
else if(indexprecision == 3)
return (UINT8) (((64 - aWeights3[index])*UINT16(e0) + aWeights3[index]*UINT16(e1) + 32) >> 6);
else // indexprecision == 4
return (UINT8) (((64 - aWeights4[index])*UINT16(e0) + aWeights4[index]*UINT16(e1) + 32) >> 6);
}
以下伪代码说明如何提取颜色分量和 alpha 分量的索引和位计数。 具有单独的颜色和 alpha 的块也有两组索引数据:一组用于矢量通道,一组用于标量通道。 对于模式 4,这些索引具有不同的宽度(2 位或 3 位),并且存在一个一位选择器,该选择器指定矢量或标量数据是否使用 3 位索引。 (提取 alpha 位计数类似于提取颜色位计数,但根据 idxMode 位,其行为是相反的。)
bitcount get_color_bitcount(block, mode)
{
if (mode.type == 0 OR == 1)
return 3;
if (mode.type == 2 OR == 3 OR == 5 OR == 7)
return 2;
if (mode.type == 6)
return 4;
//The only remaining case is Mode 4 with 1-bit index selector
idxMode = extract_idxMode(block);
if (idxMode == 0)
return 2;
else
return 3;
}
相关主题