BC6H 格式
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 地圖、Cubemap 和紋理陣列。 所有影像磚的格式都必須相同。
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 一律在子集 0 中。 資料分割順序會從左上到右,從左至右移動,然後由上至下移動。
BC6H 壓縮端點格式
下表顯示壓縮端點的位欄位做為端點格式的函式,每個資料行都會指定編碼,以及每個指定位欄位的資料列。 此方法會針對兩個區域磚佔用 82 位,而一個區域磚則採用 65 位。 例如,上述一個區域 [16 4] 編碼的前 5 個位(特別是最右邊的資料行)是位 m[4:0],接下來的 10 個位是 bits rw[9:0],依此方式,最後 6 位包含 bw[10:15]。
上表中的功能變數名稱定義如下:
欄位 | 變數 |
---|---|
m | mode |
日 | 圖形索引 |
烏爾曼 | 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] |
by | endpt[1]。A[2] |
Bz | endpt[1]。B[2] |
Endpt[i],其中 i 為 0 或 1,分別參考第 0 或第 1 組端點。
端點值的符號延伸模組
針對兩個區域磚,有四個端點值可以進行擴充。 Endpt[0]。只有在格式為帶正負號的格式時,才會簽署 ;只有當端點已轉換,或格式為帶正負號的格式時,才會簽署其他端點。 下列程式碼示範擴充兩個區域端點值的符號的演算法。
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] 新增基底值。其他三個專案的 ,總共有 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(負數)才能延伸。
色彩端點的取消量化
假設未壓縮的端點,下一個步驟是執行色彩端點的初始未量化。 這牽涉到三個步驟:
- 調色盤的取消量化
- 調色盤的插補
- Unquantization finalization finalization
將未量化程式分成兩個部分(插補前的調色盤未量化和插補之後的最終未量化)可減少在調色盤插補之前的完整取消量化程式時所需的乘法作業數目。
下列程式碼說明擷取原始 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 函式(本節的第三個範例中所述),這會套用最終調整。
- 所有硬體解壓縮程式都需要使用這些函式傳回位精確結果。
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);
}
}
相關主題