次の方法で共有


BC6H 形式

BC6H 形式は、ソース データの高ダイナミック レンジ (HDR) カラー スペースをサポートするように設計されたテクスチャ圧縮形式です。

BC6H/DXGI_FORMAT_BC6Hについて

BC6H 形式は、3 つの HDR カラー チャネルを使用する画像に高品質の圧縮を提供し、値のカラー チャネルごとに 16 ビット値を使用します (16:16:16)。 アルファ チャネルはサポートされません。

BC6H は、次のDXGI_FORMAT列挙値によって指定されます。

  • DXGI_FORMAT_BC6H_TYPELESS.
  • DXGI_FORMAT_BC6H_UF16. この BC6H 形式では、16 ビット浮動小数点カラー チャネル値に符号ビットは使用されません。
  • DXGI_FORMAT_BC6H_SF16.この BC6H 形式では、16 ビット浮動小数点カラー チャネル値で符号ビットが使用されます。

手記

カラー チャネルの 16 ビット浮動小数点形式は、多くの場合、"ハーフ" 浮動小数点形式と呼ばれます。 この形式には、次のビット レイアウトがあります。

形式 ビット レイアウト
UF16 (符号なし float) 5 指数ビット + 11 仮数ビット
SF16 (符号付き float) 1 符号ビット + 5 指数ビット + 10 仮数ビット

 

 

BC6H 形式は、Texture2D (配列を含む)、Texture3D、または TextureCube (配列を含む) テクスチャ リソースを するために使用できます。 同様に、この形式は、これらのリソースに関連付けられているすべての MIP マップ サーフェスに適用されます。

BC6H は、16 バイト (128 ビット) の固定ブロック サイズと 4 x 4 テクセルの固定タイル サイズを使用します。 以前の BC 形式と同様に、サポートされているタイル サイズ (4x4) よりも大きいテクスチャ イメージは、複数のブロックを使用して圧縮されます。 このアドレス指定 ID は、3 次元画像、MIP マップ、キューブマップ、テクスチャ配列にも適用されます。 すべてのイメージ タイルは同じ形式である必要があります。

BC6H 形式に関するいくつかの重要な注意事項:

  • BC6H は浮動小数点非正規化をサポートしますが、INF (無限大) と NaN (数値ではない) はサポートしていません。 例外は、-INF (負の無限大) をサポートする BC6H (DXGI_FORMAT_BC6H_SF16) の符号付きモードです。 この -INF のサポートは形式自体の成果物に過ぎず、この形式のエンコーダーでは特にサポートされていないことに注意してください。 一般に、エンコーダーが INF (正または負) または NaN 入力データを検出した場合、そのデータを INF 以外の表現の最大値に変換し、圧縮前に NaN を 0 にマップする必要があります。
  • BC6H はアルファ チャネルをサポートしていません。
  • BC6H デコーダーは、テクスチャ フィルタリングを実行する前に展開を実行します。
  • BC6H 展開はビット精度である必要があります。つまり、ハードウェアは、このドキュメントで説明されているデコーダーと同じ結果を返す必要があります。

BC6H の実装

BC6H ブロックは、モード ビット、圧縮エンドポイント、圧縮インデックス、およびオプションのパーティション インデックスで構成されます。 この形式では、14 種類のモードを指定します。

エンドポイントの色は RGB トリプレットとして格納されます。 BC6H は、定義された複数のカラー エンドポイントをまたがる近似線上の色のパレットを定義します。 また、モードに応じて、タイルを 2 つのリージョンに分割したり、1 つのリージョンとして扱ったりすることもできます。2 つの領域のタイルには、リージョンごとに個別の色のエンドポイントのセットがあります。 BC6H はテクセルごとに 1 つのパレット インデックスを格納します。

2 リージョンの場合、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)

 

この表の各形式は、モード ビットによって一意に識別できます。 最初の 10 個のモードは 2 領域タイルに使用され、モード ビット フィールドは 2 ビットまたは 5 ビットの長さにすることができます。 これらのブロックには、圧縮カラー エンドポイント (72 または 75 ビット)、パーティション (5 ビット)、パーティション インデックス (46 ビット) のフィールドもあります。

圧縮されたカラー エンドポイントの場合、前の表の値は、格納されている RGB エンドポイントの精度と、各色の値に使用されるビット数をメモします。 たとえば、モード 3 では、色エンドポイントの有効桁数レベルが 11 で、変換されたエンドポイントの差分値を格納するために使用されるビット数が赤、青、緑の色 (それぞれ 5、4、4) を指定します。 モード 10 ではデルタ圧縮は使用されず、代わりに 4 つのカラー エンドポイントすべてが明示的に格納されます。

最後の 4 つのブロック モードは、モード フィールドが 5 ビットである 1 領域タイルに使用されます。 これらのブロックには、エンドポイント (60 ビット) と圧縮インデックス (63 ビット) のフィールドがあります。 モード 11 (モード 10 など) は差分圧縮を使用せず、代わりに両方のカラー エンドポイントを明示的に格納します。

モード 10011、10111、11011、および 11111 (不図示) は予約されています。 エンコーダーではこれらを使用しないでください。 ハードウェアがこれらのモードのいずれかを指定してブロックを渡す場合、結果の圧縮解除ブロックには、アルファ チャネルを除くすべてのチャネルのすべてのゼロが含まれている必要があります。

BC6H の場合、アルファ チャネルはモードに関係なく常に 1.0 を返す必要があります。

BC6H パーティション セット

2 リージョン タイルには 32 個のパーティション セットがあり、次の表で定義されています。 各 4x4 ブロックは、1 つの図形を表します。

bc6h パーティション セットのテーブル

パーティション セットのこのテーブルでは、太字と下線付きのエントリは、サブセット 1 の修正インデックスの場所です (1 ビット未満で指定)。 インデックス 0 が常にサブセット 0 になるようにパーティション分割が常に配置されるため、サブセット 0 の修正インデックスは常にインデックス 0 になります。 パーティションの順序は、左上から右下に移動し、左から右に、次に上から下に移動します。

BC6H 圧縮エンドポイント形式

bc6h 圧縮エンドポイント形式の ビット フィールド

次の表は、圧縮されたエンドポイントのビット フィールドをエンドポイント形式の関数として示し、各列でエンコードを指定し、各行でビット フィールドを指定します。 この方法では、2 領域タイルでは 82 ビット、1 領域タイルでは 65 ビットを使用します。 たとえば、上記の 1 領域 [16 4] エンコードの最初の 5 ビット (具体的には右端の列) はビット m[4:0]、次の 10 ビットはビット rw[9:0]、最後の 6 ビットには bw[10:15] が含まれます。

上記の表のフィールド名は次のように定義されています。

変数
m モード
d shape index
rw endpt[0]。A[0]
rx endpt[0]。B[0]
リュー 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 番目のセットを指します。

エンドポイント値の拡張機能に署名する

2 リージョンタイルの場合、署名拡張できるエンドポイント値は 4 つあります。 Endpt[0]。A は、形式が符号付き形式の場合にのみ署名されます。他のエンドポイントは、エンドポイントが変換された場合、または形式が符号付き形式の場合にのみ署名されます。 次のコードは、2 リージョンエンドポイント値の符号を拡張するためのアルゴリズムを示しています。

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

1 領域タイルの場合、動作は同じですが、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]);
    }
}

エンドポイント値の反転の変換

2 領域タイルの場合、変換によって差分エンコードの逆が適用され、endpt[0] に基本値が追加されます。合計 9 個の追加操作について、他の 3 つのエントリに A を追加します。 次の図では、基本値は "A0" として表され、浮動小数点の精度が最も高くなっています。 "A1"、"B0"、および "B1" はすべてアンカー値から計算される差分であり、これらのデルタ値はより低い精度で表されます。 (A0 は endpt[0] に対応します。A、B0 は endpt[0] に対応します。B、A1 は endpt[1] に対応します。A と B1 は endpt[1].B に対応します。

変換反転エンドポイント値計算

1 リージョンタイルの場合、デルタ オフセットは 1 つだけであるため、追加操作は 3 つだけです。

圧縮解除器は、逆変換の結果が endpt[0].a の有効桁数をオーバーフローしないようにする必要があります。 オーバーフローの場合、逆変換の結果として得られる値は、同じビット数内でラップする必要があります。 A0 の精度が "p" ビットの場合、変換アルゴリズムは次のようになります。

B0 = (B0 + A0) & ((1 << p) - 1)

符号付き形式の場合、デルタ計算の結果も符号拡張する必要があります。 符号拡張演算で両方の符号の拡張が考慮される場合 (0 が正で、1 が負の場合)、0 の符号拡張が上記のクランプの処理を行います。 同様に、上記のクランプの後に、1 (負) の値のみを符号拡張する必要があります。

カラー エンドポイントの非量子化

非圧縮エンドポイントの場合、次の手順は、カラー エンドポイントの最初の非量子化を実行することです。 これには、次の 3 つの手順が含まれます。

  • カラー パレットの非量子化
  • パレットの補間
  • Unquantization finalization

非量子化プロセスを 2 つの部分 (補間前のカラー パレットアンカンティゼーションと補間後の最終的なアンカンティゼーション) に分離すると、パレット補間前の完全なアンカンティゼーション プロセスと比較して必要な乗算操作の数が減ります。

次のコードは、元の 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);
}

次のコード サンプルでは、補間プロセスを示し、次の観察結果を示します。

  • unquantize 関数のカラー値の全範囲 (以下) は -32768 から 65535 までであるため、補間器は 17 ビット符号付き算術を使用して実装されます。
  • 補間の後、値は finish_unquantize 関数に渡されます (このセクションの 3 番目のサンプルで説明します)。これは最終的なスケーリングを適用します。
  • これらの関数を使用してビット精度の結果を返すには、すべてのハードウェア展開器が必要です。
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 は、パレット補間後に呼び出されます。 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 でのテクスチャ ブロック圧縮の