簡體   English   中英

如何解壓 BC3_UNORM DDS 紋理格式?

[英]How to decompress a BC3_UNORM DDS texture format?

我已經閱讀了很多文章和代碼,但我仍然無法讓它工作,我已經閱讀了我的紋理中 header 的所有 128 字節,它們讀取了 65536 字節的實際紋理壓縮數據(紋理的分辨率為256x256,每個壓縮像素使用 1 個字節)。 我試圖創建我的解壓縮算法但沒有成功,我決定使用別人的,所以我在這里找到了這段代碼。 這是我試圖傳遞給它的 arguments,以便它解壓縮我的 DDS 紋理。 BlockDecompressImageDXT5(textureHeader.dwWidth, textureHeader.dwHeight, temp, packedData)注意:textureHeader 是一個有效的結構,其中加載了 DDS 紋理的 header 數據, temp是一個無符號字符數組,包含從 DDS 紋理中讀取的所有 DDS 數據和packedData是一個無符號長數組,我期望接收最終的解壓縮數據。 因此,在我鏈接的代碼中,每個像素的 RGBA 通道都打包在PackRGBA function 中, packedData中每種顏色一個字節。 在將數據指向D3D11_SUBRESOURCE_DATA pSysMem 中的紋理數據之前,我以這種方式將 unsigned long packedData中的每個字節分配給 4 個不同的 unsigned char m_DDSData

for (int i{ 0 }, iData{ 0 }; i < textureHeader.dwPitchOrLinearSize; i++, iData += 4) //dwPitchOrLinearSize is the size in bytes of the compressed data.
{
    m_DDSData[iData] = ((packedData[i] << 24) >> 24); //first char receives the 1st byte, representing the red color.
    m_DDSData[iData + 1] = ((packedData[i] << 16) >> 24); //second char receives the 2nd byte, representing the green color.
    m_DDSData[iData + 2] = ((packedData[i] << 8) >> 24); //third char receives the 3rd byte, representing the blue color.
    m_DDSData[iData + 3] = (packedData[i] >> 24); //fourth char receives the 4th byte, representing the alpha color.
}

注意:m_DDSData 應該是D3D11_SUBRESOURCE_DATA用來指向紋理數據的最終數據數組,但是當我使用它時,是我得到的結果,只有一個帶有隨機 colors 的幀,而不是我的實際紋理。 我也有其他類型紋理的算法,它們可以正常工作,所以我可以保證問題只出在 DDS 壓縮格式中。 編輯:另一個例子,這是一個胸部的 model,程序應該渲染胸部的紋理: https://prnt.sc/11b62b6

有關 BC3 壓縮方案的完整說明,請參閱Microsoft Docs BC3 只是 DXT4/DXT5 壓縮又名S3TC的現代名稱。 簡而言之,它一次將一個 4x4 像素塊壓縮為以下結構,每個塊 16 個字節:

struct BC1
{
    uint16_t    rgb[2]; // 565 colors
    uint32_t    bitmap; // 2bpp rgb bitmap
};

static_assert(sizeof(BC1) == 8, "Mismatch block size");

struct BC3
{
    uint8_t     alpha[2];   // alpha values
    uint8_t     bitmap[6];  // 3bpp alpha bitmap
    BC1         bc1;        // BC1 rgb data
};

static_assert(sizeof(BC3) == 16, "Mismatch block size");

CPU解壓

對於顏色部分,它與“BC1”又名 DXT1 壓縮塊相同。 這是偽代碼,但應該明白這一點:

auto pBC = &pBC3->bc1;
clr0 = pBC->rgb[0]; // 5:6:5 RGB
clr0.a = 255;

clr1 = pBC->rgb[1]; // 5:6:5 RGB
clr1.a = 255;

clr2 = lerp(clr0, clr1, 1 / 3);
clr2.a = 255;

clr3 = lerp(clr0, clr1, 2 / 3);
clr3.a = 255;

uint32_t dw = pBC->bitmap;

for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i, dw >>= 2)
{
    switch (dw & 3)
    {
        case 0: pColor[i] = clr0; break;
        case 1: pColor[i] = clr1; break;
        case 2: pColor[i] = clr2; break;
        case 3: pColor[i] = clr3; break;
    }
}

請注意,雖然 BC3 包含一個 BC1 塊,但 BC1 的解碼規則略有修改。 解壓BC1時,一般檢查colors的順序如下:

if (pBC->rgb[0] <= pBC->rgb[1])
{
    /* BC1 with 1-bit alpha */
    clr2 = lerp(clr0, clr1, 0.5);
    clr2.a = 255;

    clr3 = 0; // alpha of zero
}

BC2 和 BC3 已經包含了 alpha 通道,所以沒有使用這個額外的邏輯,你總是有 4 個不透明的 colors。

對於 alpha 部分,BC3 使用兩個 alpha 值,然后根據這些值生成一個查找表:

alpha[0] = alpha0 = pBC3->alpha[0];
alpha[1] = alpha1 = pBC3->alpha[1];

if (alpha0 > alpha1)
{
    // 6 interpolated alpha values.
    alpha[2] = lerp(alpha0, alpha1, 1 / 7);
    alpha[3] = lerp(alpha0, alpha1, 2 / 7);
    alpha[4] = lerp(alpha0, alpha1, 3 / 7);
    alpha[5] = lerp(alpha0, alpha1, 4 / 7);
    alpha[6] = lerp(alpha0, alpha1, 5 / 7);
    alpha[7] = lerp(alpha0, alpha1, 6 / 7);
}
else
{
    // 4 interpolated alpha values.
    alpha[2] = lerp(alpha0, alpha1, 1 / 5);
    alpha[3] = lerp(alpha0, alpha1, 2 / 5);
    alpha[4] = lerp(alpha0, alpha1, 3 / 5);
    alpha[5] = lerp(alpha0, alpha1, 4 / 5);
    alpha[6] = 0;
    alpha[7] = 255;
}

uint32_t dw = uint32_t(pBC3->bitmap[0]) | uint32_t(pBC3->bitmap[1] << 8)
    | uint32_t(pBC3->bitmap[2] << 16);

for (size_t i = 0; i < 8; ++i, dw >>= 3)
    pColor[i].a = alpha[dw & 0x7];

dw = uint32_t(pBC3->bitmap[3]) | uint32_t(pBC3->bitmap[4] << 8)
    | uint32_t(pBC3->bitmap[5] << 16);

for (size_t i = 8; i < NUM_PIXELS_PER_BLOCK; ++i, dw >>= 3)
    pColor[i].a = alpha[dw & 0x7];

DirectXTex包括對所有 BC 格式進行所有壓縮/解壓縮的函數。

如果您想知道偽函數lerp的作用,請參閱wikipediaHLSL 文檔

使用壓縮紋理渲染

如果要使用 Direct3D 進行渲染,則不需要解壓縮紋理。 所有 Direct3D 硬件功能級別都包括對 BC1 - BC3 紋理壓縮的支持。 您只需使用DXGI_FORMAT_BC3_UNORM格式創建紋理並正常創建紋理。 像這樣的東西:

D3D11_TEXTURE2D_DESC desc = {};
desc.Width = textureHeader.dwWidth;
desc.Height = textureHeader.dwHeight;
desc.MipLevels = desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_BC3_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;


D3D11_SUBRESOURCE_DATA initData = {}; 
initData.pSrcBits = temp;
initData.SysMemPitch = 16 * (textureHeader.dwWidth / 4);
    // For BC compressed textures pitch is the number of bytes in a ROW of blocks

Microsoft::WRL::ComPtr<ID3D11Texture2D> pTexture;
hr = device->CreateTexture2D( &desc, &initData, &pTexture );
if (FAILED(hr))
    // error

有關支持任意 DXGI 格式、mipmap、紋理 arrays、體積貼圖、立方體貼圖、立方體貼圖 arrays 等的全功能 DDS 加載器,請參閱DDSTextureLoader 此代碼包含在用於DX11 / DX12DirectX 工具包中。 DirectXTex中有 DirectX 9、DirectX 10 和 DirectX 11 的獨立版本。

如果加載舊版 DDS 文件(即那些不將map 直接轉換為 DXGI 格式的文件),則使用DirectXTex中的 DDS 函數,該函數執行所需的所有各種像素格式轉換(3:3:2、3:3:2:8、4 :4、8:8:8、P8、A8P8 等)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM