【问题标题】:How to decompress a BC3_UNORM DDS texture format?如何解压 BC3_UNORM DDS 纹理格式?
【发布时间】:2021-07-08 00:47:46
【问题描述】:

我已经阅读了很多文章和代码,但我仍然无法让它工作,我已经阅读了纹理中标题的所有 128 字节,它们读取了实际纹理的 65536 字节压缩数据(纹理的分辨率为 256x256,每个压缩像素使用 1 个字节)。我试图创建我的解压缩算法但没有成功,我决定使用别人的,所以我在这里找到了this 代码。这是我试图传递给它的参数,以便它解压缩我的 DDS 纹理。BlockDecompressImageDXT5(textureHeader.dwWidth, textureHeader.dwHeight, temp, packedData) 注意:textureHeader 是一个有效的结构,其中加载了 DDS 纹理的标头数据,temp 是一个无符号字符数组,包含从 DDS 纹理读取的所有 DDS 数据,packedData 是一个无符号长数组我期待收到最终的解压缩数据。因此,在我链接的代码中,每个像素的 RGBA 通道都打包在 PackRGBA 函数中,packedData 中的每种颜色对应一个字节。在将数据指向D3D11_SUBRESOURCE_DATApSysMem 处的纹理数据之前,我已经将每个字节从 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用来指向纹理数据的最终数据数组,但是当我使用它时this是我得到的那种结果,只有一个帧用随机颜色代替我的实际纹理。我也有其他类型纹理的算法,它们可以正常工作,所以我可以保证问题只出在 DDS 压缩格式中。 编辑: 另一个例子,这是一个胸部模型,程序应该渲染胸部的纹理:https://prnt.sc/11b62b6

【问题讨论】:

  • 您应该看看DirectXTex,它有用于压缩和解压缩所有 BC 格式的 C++ 代码。
  • 你到底想做什么?您想使用带有 BC3_UNORM 纹理的 Direct3D 进行渲染,或者 - 您是否尝试在 CPU 上将 BC3_UNORM 纹理数据转换为线性格式?
  • @ChuckWalbourn 我真的不太清楚,我在网上没有找到很多内容来解释这些带有块压缩和 BC3_UNORM 格式的 alpha 0 或 1 的东西是如何工作的,我只是走一条死路,但回答你的问题,我的目标是加载我在这里的一些 DDS 纹理并渲染它。
  • 所有纹理都采用相同的格式,我真的希望能够直接加载它们,这样我就可以在运行时访问它们的数据。但是,如果您能告诉我是否有一种方法可以加载它们而不必像您所说的那样转换它们,那也会很有帮助。
  • Direct3D 硬件支持直接渲染 BC 压缩纹理。您只需使用DXGI_FORMAT_BC3_UNORM 格式创建纹理并使用它进行渲染,然后在像素着色器中您将看到 RGBA 值。 DirectXTexDirectX Toolkit 中的DDSTextureLoader 可以为您加载和创建纹理。

标签: c++ image-compression direct3d11 dds-format


【解决方案1】:

有关 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时,一般检查颜色顺序如下:

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 种不透明的颜色。

对于 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 docs

使用压缩纹理进行渲染

如果您要使用 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、纹理数组、体积贴图、立方体贴图、立方体贴图数组等的全功能 DDS 加载器,请参阅DDSTextureLoader。此代码包含在DX11 / DX12DirectX 工具包中。 DirectXTex 中有 DirectX 9、DirectX 10 和 DirectX 11 的独立版本。

如果加载旧版 DDS 文件(即那些直接映射到 DXGI 格式的文件),则使用 DirectXTex 中的 DDS 函数,它会执行所需的所有各种像素格式转换 (3:3 :2、3:3:2:8、4:4、8:8:8、P8、A8P8 等)

【讨论】:

  • 我说不出话来,我真的很难理解所有这些,但你刚刚用这个答案解决了我所有的问题。
猜你喜欢
  • 2023-01-12
  • 2012-09-26
  • 2015-01-27
  • 2013-02-14
  • 2016-02-01
  • 2014-03-26
  • 1970-01-01
  • 1970-01-01
  • 2015-06-24
相关资源
最近更新 更多