0

我已经阅读了很多文章和代码,但我仍然无法让它工作,我已经读取了纹理中标题的所有 128 字节,它们读取了实际纹理的 65536 字节压缩数据(纹理的分辨率为256x256,每个压缩像素使用 1 个字节)。我试图创建我的解压缩算法但没有成功,我决定使用别人的,所以我在这里找到了这段代码。这是我试图传递给它的参数,以便它解压缩我的 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指向纹理数据的最终数据数组,但是当我使用它时,是我得到的结果,只有一个带有随机颜色的帧,而不是我的实际纹理。我也有其他类型纹理的算法,它们可以正常工作,所以我可以保证问题只出在 DDS 压缩格式中。 编辑:另一个例子,这是一个胸部模型,程序应该渲染胸部的纹理:https ://prnt.sc/11b62b6

4

1 回答 1

2

有关 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 文档

使用压缩纹理渲染

如果要使用 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 / DX12的DirectX 工具包中。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 等)

于 2021-04-13T20:26:18.790 回答