共用方式為


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 地圖、Cubemap 和紋理數位。 所有影像磚的格式都必須相同。

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

下列虛擬程式代碼概述為每個子集完整譯碼端點色彩和 Alpha 元件的步驟,該子集指定了 16 位元組 BC7 區塊。

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 中的 紋理區塊壓縮