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 位),固定磁贴大小为 4x4 纹素。 与以前的 BC 格式一样,大于支持的磁贴大小(4x4)的纹理图像使用多个块进行压缩。 此寻址标识也适用于三维图像和 MIP 地图、多维数据集地图和纹理数组。 所有图像磁贴的格式必须相同。

BC7 压缩三通道(RGB)和四通道(RGBA)固定点数据图像。 通常,源数据是每个颜色组件(通道)的 8 位,尽管该格式能够编码每个颜色分量更高的位的源数据。 所有图像磁贴的格式必须相同。

BC7 解码器在应用纹理筛选之前执行解压缩。

BC7 解压缩硬件必须准确;也就是说,硬件必须返回与本文档中所述解码器返回的结果相同的结果。

BC7 实现

BC7 实现可以指定 8 种模式之一,模式在 16 字节(128 位)块中指定的最低有效位。 模式由零位或更多位编码,值为 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 块,块由模式位、旋转位、压缩终结点、压缩索引和可选的索引选择器位组成。 这些块具有有效的 RGB 矢量 [R, G, B] 和标量 alpha 通道 [A] 单独编码。

下表列出了每个块类型的组件。

BC7 块包含... 模式位 旋转位 索引选择器位 分区位 压缩的终结点 P 位 压缩索引
仅颜色组件 必填 N/A N/A 必填 必填 自选 必填
color + alpha combined 必填 N/A N/A 自选 必填 自选 必填
颜色和 alpha 分隔 必填 必填 自选 N/A 必填 N/A 必填

 

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;
}

在 Direct3D 11 纹理块压缩