區塊壓縮 (Direct3D 10)

區塊壓縮是減少紋理大小的紋理壓縮技術。 相較於每個色彩為 32 位元的紋理,區塊壓縮紋理最多可小 75%。 由於記憶體使用量較小,應用程式通常會在使用區塊壓縮時看到效能增加。

雖然遺失,但區塊壓縮運作良好,而且建議用於管線轉換和篩選的所有紋理。 直接對應到畫面的紋理 (圖示和文字等 UI 元素) 並非壓縮的好選擇,因為成品比較明顯。

區塊壓縮紋理必須在所有維度中建立為大小為 4 的倍數,而且不能當做管線的輸出使用。

區塊壓縮如何運作?

區塊壓縮是減少儲存色彩資料所需的記憶體數量的技術。 藉由將某些色彩儲存在原始大小,以及使用編碼配置的其他色彩,您可以大幅減少儲存影像所需的記憶體數量。 由於硬體會自動解碼壓縮的資料,因此使用壓縮的紋理不會有效能損失。

若要查看壓縮的運作方式,請查看下列兩個範例。 第一個範例描述儲存未壓縮資料時所使用的記憶體數量;第二個範例描述儲存壓縮資料時所使用的記憶體數量。

儲存未壓縮的數據

下圖代表未壓縮的 4×4 紋理。 假設每個色彩都包含單一色彩元件 (例如紅色),並儲存在一個位元組的記憶體中。

未壓縮的 4x4 紋理圖例

未壓縮的資料會循序配置在記憶體中,而且需要 16 個位元組,如下圖所示。

循序記憶體中未壓縮數據的圖例

儲存壓縮的數據

現在您已經了解了未壓縮圖像使用了多少記憶體,接下來看看壓縮影像可以節省多少記憶體。 BC4 壓縮格式會儲存 2 種色彩 (每個 1 個位元組) 和 16 個 3 位元索引 (48 位元或 6 個位元組),用來插補紋理中的原始色彩,如下圖所示。

bc4 壓縮格式的圖例

儲存已壓縮資料所需的總空間為 8 個位元組,比未壓縮的範例節省了 50% 的記憶體。 使用多個色彩元件時,節省的成本甚至更大。

區塊壓縮所提供的大量記憶體節省可能會導致效能增加。 這種性能是以影像品質為代價的 (由於色彩插補);然而,較低的品質通常不明顯。

下一節說明 Direct3D 10 如何讓您輕鬆地在應用程式中使用區塊壓縮。

使用區塊壓縮

建立區塊壓縮的紋理就像未壓縮的紋理一樣(請參閱 從檔案建立紋理),不同之處在於您指定區塊壓縮格式。

loadInfo.Format = DXGI_FORMAT_BC1_UNORM;
D3DX10CreateTextureFromFile(...);

接下來,建立檢視以將紋理系結至管線。 由於區塊壓縮紋理只能當做著色器階段的輸入使用,因此您想要藉由呼叫 CreateShaderResourceView 來建立著色器資源檢視。

使用區塊壓縮紋理的方式與使用未壓縮紋理的方式相同。 如果您的應用程式將獲取指向區塊壓縮資料的記憶體指標,則需要考慮 Mipmap 中的記憶體填充,這會導致宣告的大小與實際大小不同。

虛擬大小與實體大小

如果您有使用記憶體指標來逐步執行區塊壓縮紋理記憶體的應用程式程式碼,則應用程式程式碼中可能需要修改的一個重要考慮。 區塊壓縮紋理在所有維度中都必須是 4 的倍數,因為區塊壓縮演算法會在 4x4 紋素區塊上運作。 對於初始維度被 4 除以 4 的 Mipmap 而言,這會是個問題,但細分的層級則不是。 下圖顯示每個 Mipmap 層級的虛擬 (宣告) 大小與實體 (實際) 大小之間的區域差異。

未壓縮和壓縮 Mipmap 層級的圖表

圖表左側會顯示針對未壓縮的 60×40 紋理所產生的 mipmap 層級大小。 最上層大小取自產生紋理的 API 呼叫;每個後續層級都是上一個層級大小的一半。 對於未壓縮的紋理,虛擬 (宣告) 大小與實體 (實際) 大小之間沒有任何差異。

圖表右側顯示針對壓縮為相同 60×40 紋理所產生的 Mipmap 層級大小。 請注意,第二個和第三個層級都有記憶體填補,讓每個層級的大小係數都為 4。 這是必要的,讓演算法可以在 4×4 個紋素區塊上運作。 如果您認為小於 4×4 的 mipmap 層級,這是顯而易見的;配置紋理記憶體時,這些非常小型Mipmap層級的大小會四捨五入到最接近的4因數。

取樣硬體會使用虛擬大小;取樣紋理時,會忽略記憶體填補。 對於小於 4×4 的 Mipmap 層級,只有前四個紋素將用於 2×2 對應,而 1×1 區塊只會使用第一個紋素。 不過,沒有 API 結構會公開實體大小 (包括記憶體填補)。

總而言之,在複製包含區塊壓縮資料的區域時,請小心使用對齊的記憶體區塊。 若要在取得記憶體指標的應用程式中執行這項操作,請確定指標使用表面傾斜來考慮實體記憶體大小。

壓縮演算法

Direct3D 中的區塊壓縮技術會將未壓縮的紋理資料分成 4×4 個區塊、壓縮每個區塊,然後儲存資料。 基於這個理由,預期要壓縮的紋理必須具有 4 倍的紋理維度。

區塊壓縮圖表

上圖顯示分割成紋素區塊的紋理。 第一個區塊會顯示標示為 a-p 的 16 個紋素的配置,但每個區塊都有相同的資料組織。

Direct3D 實作了多種壓縮配置,每種配置在儲存的元件數量、每個元件的位元數和消耗的記憶體量之間實作了不同的權衡。 使用此資料表可協助選擇最適合資料類型的格式,以及最適合您應用程式的資料解析度。

來源資料 資料壓縮解析度 (位元) 選擇此壓縮格式
三元件色彩和 Alpha 色彩 (5:6:5),Alpha (1) 或無 Alpha BC1
三元件色彩和 Alpha 色彩 (5:6:5),Alpha (4) BC2
三元件色彩和 Alpha 色彩 (5:6:5),Alpha (8) BC3
單一元件色彩 一個元件 (8) BC4
雙元件色彩 兩個元件 (8:8) BC5

 

BC1

使用第一個區塊壓縮格式 (BC1) (DXGI_FORMAT_BC1_TYPELESS、DXGI_FORMAT_BC1_UNORM 或 DXGI_BC1_UNORM_SRGB) 以 5:6:5 色彩 (5 位元紅色、6 位元綠色、5 位元藍色) 儲存三元件色彩資料。 即使資料也包含 1 位元 Alpha,也是如此。 假設 4×4 紋理可能使用最大的資料格式,BC1 格式會將所需的記憶體從 48 個位元組 (16 種色彩× 3 個元件/色彩× 1 個位元組/元件) 減少到 8 個位元組的記憶體。

此演算法適用於 4×4 個紋素區塊。 演算法不儲存 16 種色彩,而是會儲存 2 個參考色彩 (color_0 和 color_1),以及 16 個 2 位色彩索引 (區塊 a–p),如下圖所示。

bc1 壓縮版面配置的圖表

色彩索引 (a–p) 是用來查閱色彩資料表的原始色彩。 色彩資料表包含 4 種色彩。 前兩種色彩—color_0和color_1—是最小和最大色彩。 其他兩種色彩,color_2和color_3,都是使用線性插補計算的中繼色彩。

color_2 = 2/3*color_0 + 1/3*color_1
color_3 = 1/3*color_0 + 2/3*color_1

四種色彩會指派 2 位元索引值,這些值會儲存在 a–p 區塊中。

color_0 = 00
color_1 = 01
color_2 = 10
color_3 = 11

最後,a-p 區塊中的每個色彩都會與色彩資料表中的四種色彩進行比較,而最接近色彩的索引會儲存在 2 位元區塊中。

此演算法也適合包含 1 位元 Alpha 的資料。 唯一的差別在於,color_3 設定為 0 (代表透明色彩),而 color_2 是 color_0 和 color_1 的線性混合。

color_2 = 1/2*color_0 + 1/2*color_1;
color_3 = 0;

Direct3D 9 與 Direct3D 10 之間的差異:

此格式同時存在於 Direct3D 9 和 10 中。

  • Direct3D 9 中,BC1 格式稱為 D3DFMT_DXT1
  • Direct3D 10 中,BC1 格式是以 DXGI_FORMAT_BC1_UNORMDXGI_FORMAT_BC1_UNORM_SRGB 表示。

BC2

使用 BC2 格式 (DXGI_FORMAT_BC2_TYPELESS、DXGI_FORMAT_BC2_UNORM 或 DXGI_BC2_UNORM_SRGB) 來儲存含有色彩和 Alpha 資料且具有低一致性的資料 (對高度連貫的 Alpha 資料使用 BC3)。 BC2 格式會將 RGB 資料儲存為 5:6:5 色彩 (5 位元紅色、6 位元綠色、5 位元藍色) 和 Alpha 做為個別的 4 位元值。 假設 4×4 紋理可能使用最大的資料格式,此壓縮技術會將所需的記憶體從 64 個位元組 (16 個色彩× 4 個元件/色彩× 1 個位元組/元件) 減少到 16 個位元組的記憶體。

BC2 格式會儲存與 BC1 格式相同的位元和資料配置數目的色彩;不過,BC2 需要額外的 64 位元記憶體才能儲存 Alpha 資料,如下圖所示。

bc2 壓縮版面配置的圖表

Direct3D 9 與 Direct3D 10 之間的差異:

此格式同時存在於 Direct3D 9 和 10 中。

  • Direct3D 9 中,BC2 格式稱為 D3DFMT_DXT2D3DFMT_DXT3

  • Direct3D 10 中,BC2 格式是以 DXGI_FORMAT_BC2_UNORMDXGI_FORMAT_BC2_UNORM_SRGB 表示

BC3

使用 BC3 格式 (DXGI_FORMAT_BC3_TYPELESS、DXGI_FORMAT_BC3_UNORM 或 DXGI_BC3_UNORM_SRGB) 來儲存高度連貫的色彩資料 (針對較不連貫的 Alpha 資料使用 BC2)。 BC3 格式會使用 5:6:5 色彩儲存色彩資料 (5 位元紅色、6 位元綠色、5 位元藍色) 和 Alpha 資料使用一個位元組。 假設 4×4 紋理可能使用最大的資料格式,此壓縮技術會將所需的記憶體從 64 個位元組 (16 個色彩× 4 個元件/色彩× 1 個位元組/元件) 減少到 16 個位元組的記憶體。

BC3 格式會儲存與 BC1 格式相同的位元和資料配置數目的色彩;不過,BC3 需要額外的 64 位元記憶體來儲存 Alpha 資料。 BC3 格式會藉由儲存兩個參考值並在它們之間插補來處理 Alpha (類似於 BC1 儲存 RGB 色彩的方式)。

此演算法適用於 4×4 個紋素區塊。 演算法不是儲存 16 個 alpha 值,而是儲存 2 個參考 alpha (alpha_0 和 alpha_1) 和 16 個 3 位元色彩索引 (alpha a 到 p),如下圖所示。

bc3 壓縮版面配置的圖表

BC3 格式會使用 Alpha 索引 (a–p) 從包含 8 個值的查閱資料表中查閱原始色彩。 前兩個值—alpha_0 和 alpha_1—是最小值和最大值;其他六個中繼值是使用線性插補來計算。

演算法會檢查兩個參考 Alpha 值,以判斷插補 Alpha 值的數目。 如果 alpha_0 大於 alpha_1,BC3 就會插補 6 個 Alpha 值;否則,它會插補 4。 當 BC3 只插補 4 個 Alpha 值時,它會設定兩個額外的 Alpha 值 (0 表示完全透明,255 表示完全不透明)。 BC3 會儲存對應至插補 Alpha 值的位元程式碼,以壓縮 4×4 紋素區域中的 Alpha 值,這最符合指定紋素的原始 Alpha 值。

if( alpha_0 > alpha_1 )
{
  // 6 interpolated alpha values.
  alpha_2 = 6/7*alpha_0 + 1/7*alpha_1; // bit code 010
  alpha_3 = 5/7*alpha_0 + 2/7*alpha_1; // bit code 011
  alpha_4 = 4/7*alpha_0 + 3/7*alpha_1; // bit code 100
  alpha_5 = 3/7*alpha_0 + 4/7*alpha_1; // bit code 101
  alpha_6 = 2/7*alpha_0 + 5/7*alpha_1; // bit code 110
  alpha_7 = 1/7*alpha_0 + 6/7*alpha_1; // bit code 111
}
else
{
  // 4 interpolated alpha values.
  alpha_2 = 4/5*alpha_0 + 1/5*alpha_1; // bit code 010
  alpha_3 = 3/5*alpha_0 + 2/5*alpha_1; // bit code 011
  alpha_4 = 2/5*alpha_0 + 3/5*alpha_1; // bit code 100
  alpha_5 = 1/5*alpha_0 + 4/5*alpha_1; // bit code 101
  alpha_6 = 0;                         // bit code 110
  alpha_7 = 255;                       // bit code 111
}

Direct3D 9 與 Direct3D 10 之間的差異:

  • Direct3D 9 中,BC3 格式稱為 D3DFMT_DXT4D3DFMT_DXT5

  • Direct3D 10 中,BC3 格式是以 DXGI_FORMAT_BC3_UNORMDXGI_FORMAT_BC3_UNORM_SRGB來表示。

BC4

使用 BC4 格式,針對每一種色彩使用 8 位來儲存單一元件色彩資料。 由於精確度增加 (與 BC1 相比),BC4 非常適合使用 DXGI_FORMAT_BC4_UNORM 格式將浮點資料儲存在 [0 到 1] 的範圍內,並使用 DXGI_FORMAT_BC4_SNORM 格式將浮點數儲存在 [1 到 1]。 假設 4×4 紋理可能使用最大的資料格式,此壓縮技術會將所需的記憶體從 16 個位元組 (16 個色彩× 4 個元件/色彩× 1 個位元組/元件) 減少到 8 個位元組。

此演算法適用於 4×4 個紋素區塊。 演算法不是儲存 16 種色彩,而是儲存 2 種參考色彩 (red_0 和 red_1) 和 16 個 3 位元色彩索引 (紅色 a 到紅色 p),如下圖所示。

bc4 壓縮版面配置的圖表

此演算法會使用 3 位元索引,從包含 8 種色彩的色彩資料表中查閱色彩。 前兩種色彩—red_0 和 red_1—是最小和最大色彩。 此演算法會使用線性插補來計算其餘色彩。

此演算法透過檢查兩個參考值來確定插值色彩值的數量。 如果 red_0 大於 red_1,BC4 就會插補 6 個色彩值;否則,它會插補 4。 當 BC4 只插補 4 個色彩值時,它會設定兩個額外的色彩值 (0.0f 表示完全透明,1.0f 表示完全不透明)。 BC4 會儲存對應至插補 Alpha 值的位元程式碼,以壓縮 4×4 紋素區域中的 Alpha 值,這最符合指定紋素的原始 Alpha 值。

BC4_UNORM

單一元件資料的插補如下列程式碼範例所示。

unsigned word red_0, red_1;

if( red_0 > red_1 )
{
  // 6 interpolated color values
  red_2 = (6*red_0 + 1*red_1)/7.0f; // bit code 010
  red_3 = (5*red_0 + 2*red_1)/7.0f; // bit code 011
  red_4 = (4*red_0 + 3*red_1)/7.0f; // bit code 100
  red_5 = (3*red_0 + 4*red_1)/7.0f; // bit code 101
  red_6 = (2*red_0 + 5*red_1)/7.0f; // bit code 110
  red_7 = (1*red_0 + 6*red_1)/7.0f; // bit code 111
}
else
{
  // 4 interpolated color values
  red_2 = (4*red_0 + 1*red_1)/5.0f; // bit code 010
  red_3 = (3*red_0 + 2*red_1)/5.0f; // bit code 011
  red_4 = (2*red_0 + 3*red_1)/5.0f; // bit code 100
  red_5 = (1*red_0 + 4*red_1)/5.0f; // bit code 101
  red_6 = 0.0f;                     // bit code 110
  red_7 = 1.0f;                     // bit code 111
}

參考色彩會指派 3 位元索引 (000-111,因為有 8 個值),這會在壓縮期間以紅色 a 到紅色 p 儲存在區塊中。

BC4_SNORM

DXGI_FORMAT_BC4_SNORM 完全相同,不同之處在於資料是以 SNORM 範圍編碼,以及插補 4 個色彩值時。 單一元件資料的插補如下列程式碼範例所示。

signed word red_0, red_1;

if( red_0 > red_1 )
{
  // 6 interpolated color values
  red_2 = (6*red_0 + 1*red_1)/7.0f; // bit code 010
  red_3 = (5*red_0 + 2*red_1)/7.0f; // bit code 011
  red_4 = (4*red_0 + 3*red_1)/7.0f; // bit code 100
  red_5 = (3*red_0 + 4*red_1)/7.0f; // bit code 101
  red_6 = (2*red_0 + 5*red_1)/7.0f; // bit code 110
  red_7 = (1*red_0 + 6*red_1)/7.0f; // bit code 111
}
else
{
  // 4 interpolated color values
  red_2 = (4*red_0 + 1*red_1)/5.0f; // bit code 010
  red_3 = (3*red_0 + 2*red_1)/5.0f; // bit code 011
  red_4 = (2*red_0 + 3*red_1)/5.0f; // bit code 100
  red_5 = (1*red_0 + 4*red_1)/5.0f; // bit code 101
  red_6 = -1.0f;                     // bit code 110
  red_7 =  1.0f;                     // bit code 111
}

參考色彩會指派 3 位元索引 (000-111,因為有 8 個值),這會在壓縮期間以紅色 a 到紅色 p 儲存在區塊中。

BC5

使用 BC5 格式,針對每一種色彩使用 8 位來儲存兩個元件色彩資料。 由於精確度增加 (與 BC1 相比),BC5 非常適合使用 DXGI_FORMAT_BC5_UNORM 格式儲存 [0 到 1] 範圍內的浮點資料,以及使用 DXGI_FORMAT_BC5_SNORM 格式儲存 [-1 到 +1] 範圍內的浮點資料。 假設 4×4 紋理可能使用最大的資料格式,此壓縮技術會將所需的記憶體從 32 個位元組 (16 個色彩× 4 個元件/色彩× 2 個位元組/元件) 減少到 16 個位元組。

此演算法適用於 4×4 個紋素區塊。 演算法並非為兩個元件儲存 16 個色彩,而是為每個元件儲存 2 個參考色彩 (red_0、red_1、green_0 和green_1) 以及為每個元件儲存 16 個 3 位元色彩索引 (紅色 a 到紅色 p 和綠色 a 到綠色 p),如下圖所示。

bc5 壓縮版面配置的圖表

此演算法會使用 3 位元索引,從包含 8 種色彩的色彩資料表中查閱色彩。 前兩種色彩—red_0 和 red_1 (或 green_0 和 green_1) 是最小和最大色彩。 此演算法會使用線性插補來計算其餘色彩。

此演算法透過檢查兩個參考值來確定插值色彩值的數量。 如果 red_0 大於 red_1,BC5 就會插補 6 個色彩值;否則,它會插補 4。 當 BC5 只插補 4 個色彩值時,它會將其餘兩個色彩值設定為 0.0f 和 1.0f。

BC5_UNORM

單一元件資料的插補如下列程式碼範例所示。 綠色元件的計算很類似。

unsigned word red_0, red_1;

if( red_0 > red_1 )
{
  // 6 interpolated color values
  red_2 = (6*red_0 + 1*red_1)/7.0f; // bit code 010
  red_3 = (5*red_0 + 2*red_1)/7.0f; // bit code 011
  red_4 = (4*red_0 + 3*red_1)/7.0f; // bit code 100
  red_5 = (3*red_0 + 4*red_1)/7.0f; // bit code 101
  red_6 = (2*red_0 + 5*red_1)/7.0f; // bit code 110
  red_7 = (1*red_0 + 6*red_1)/7.0f; // bit code 111
}
else
{
  // 4 interpolated color values
  red_2 = (4*red_0 + 1*red_1)/5.0f; // bit code 010
  red_3 = (3*red_0 + 2*red_1)/5.0f; // bit code 011
  red_4 = (2*red_0 + 3*red_1)/5.0f; // bit code 100
  red_5 = (1*red_0 + 4*red_1)/5.0f; // bit code 101
  red_6 = 0.0f;                     // bit code 110
  red_7 = 1.0f;                     // bit code 111
}

參考色彩會指派 3 位元索引 (000-111,因為有 8 個值),這會在壓縮期間以紅色 a 到紅色 p 儲存在區塊中。

BC5_SNORM

DXGI_FORMAT_BC5_SNORM 完全相同,不同之處在於資料是以 SNORM 範圍編碼,且插補 4 個資料值時,另外兩個值為 -1.0f 和 1.0f。 單一元件資料的插補如下列程式碼範例所示。 綠色元件的計算很類似。

signed word red_0, red_1;

if( red_0 > red_1 )
{
  // 6 interpolated color values
  red_2 = (6*red_0 + 1*red_1)/7.0f; // bit code 010
  red_3 = (5*red_0 + 2*red_1)/7.0f; // bit code 011
  red_4 = (4*red_0 + 3*red_1)/7.0f; // bit code 100
  red_5 = (3*red_0 + 4*red_1)/7.0f; // bit code 101
  red_6 = (2*red_0 + 5*red_1)/7.0f; // bit code 110
  red_7 = (1*red_0 + 6*red_1)/7.0f; // bit code 111
}
else
{
  // 4 interpolated color values
  red_2 = (4*red_0 + 1*red_1)/5.0f; // bit code 010
  red_3 = (3*red_0 + 2*red_1)/5.0f; // bit code 011
  red_4 = (2*red_0 + 3*red_1)/5.0f; // bit code 100
  red_5 = (1*red_0 + 4*red_1)/5.0f; // bit code 101
  red_6 = -1.0f;                    // bit code 110
  red_7 =  1.0f;                    // bit code 111
}

參考色彩會指派 3 位元索引 (000-111,因為有 8 個值),這會在壓縮期間以紅色 a 到紅色 p 儲存在區塊中。

使用 Direct3D 10.1 進行格式轉換

Direct3D 10.1 可讓預先結構化類型紋理與相同位寬度區塊壓縮紋理之間的複本。 可以完成此作業的函式為 CopyResourceCopySubresourceRegion

從 Direct3D 10.1 開始,您可以使用 CopyResource CopySubresourceRegion 在幾個格式類型之間複製。 這種類型的複製作業會執行格式轉換類型,以不同的格式類型重新解釋資源資料。 請考慮此範例,其中顯示重新解釋資料與更典型轉換行為方式之間的差異:

    FLOAT32 f = 1.0f;
    UINT32 u;

若要將 『f』 重新解譯為 'u' 的類型,請使用 memcpy

    memcpy( &u, &f, sizeof( f ) ); // ‘u’ becomes equal to 0x3F800000.

在上述重新解釋中,資料的基礎值不會變更;memcpy 會將 float 重新解釋為無符號整數。

若要執行更典型的轉換類型,請使用指派:

    u = f; // ‘u’ becomes 1.

在上述轉換中,資料的基礎值會變更。

以下資料表列出您可以在此重新解釋格式轉換類型中使用的允許來源和目的地格式。 您必須正確編碼值,重新解釋才能如預期般運作。

位元寬度 未壓縮的資源 區塊壓縮資源
32 DXGI_FORMAT_R32_UINT
DXGI_FORMAT_R32_SINT
DXGI_FORMAT_R9G9B9E5_SHAREDEXP
64 DXGI_FORMAT_R16G16B16A16_UINT
DXGI_FORMAT_R16G16B16A16_SINT
DXGI_FORMAT_R32G32_UINT
DXGI_FORMAT_R32G32_SINT
DXGI_FORMAT_BC1_UNORM[_SRGB]
DXGI_FORMAT_BC4_UNORM
DXGI_FORMAT_BC4_SNORM
128 DXGI_FORMAT_R32G32B32A32_UINT
DXGI_FORMAT_R32G32B32A32_SINT
DXGI_FORMAT_BC2_UNORM[_SRGB]
DXGI_FORMAT_BC3_UNORM[_SRGB]
DXGI_FORMAT_BC5_UNORM
DXGI_FORMAT_BC5_SNORM

 

資源 (Direct3D 10)