如何解决如何解压 BC3_UNORM DDS 纹理格式?
我已经阅读了很多文章和代码,但我仍然无法让它工作,我已经阅读了纹理中标头的所有 128 字节,它们读取了实际纹理的 65536 字节压缩数据(纹理的分辨率为 256x256,每个压缩像素使用 1 个字节)。我试图创建我的解压算法但没有成功,我决定使用别人的,所以我在这里找到了 this 代码。这是我试图传递给它的参数,以便它解压缩我的 DDS 纹理。BlockDecompressImageDXT5(textureHeader.dwWidth,textureHeader.dwHeight,temp,packedData)
注意:textureHeader 是一个有效的结构,其中加载了 DDS 纹理的头数据,temp
是一个无符号字符数组,包含从 DDS 纹理读取的所有 DDS 数据和 {{1 }} 是一个无符号长数组,我希望收到最终的解压数据。因此,在我链接的代码中,每个像素的 RGBA 通道都打包在 packedData
函数中,PackRGBA
中的每种颜色对应一个字节。在将数据指向 packedData
pSysMem 处的纹理数据之前,我已将每个字节从 unsigned long D3D11_SUBRESOURCE_DATA
分配到 4 个不同的 unsigned char packedData
,如下所示:
m_DDSData
注意: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.
}
用来指向纹理数据的最终数据数组,但是当我使用它时 this 是我得到的那种结果,只有一个带有随机颜色的框架,而不是我的实际纹理。我也有其他类型纹理的算法,并且它们可以正常工作,因此我可以确保问题仅出现在 DDS 压缩格式中。
编辑:另一个例子,这是一个胸部模型,程序应该渲染胸部的纹理: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,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,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,2 / 7);
alpha[4] = lerp(alpha0,3 / 7);
alpha[5] = lerp(alpha0,4 / 7);
alpha[6] = lerp(alpha0,5 / 7);
alpha[7] = lerp(alpha0,6 / 7);
}
else
{
// 4 interpolated alpha values.
alpha[2] = lerp(alpha0,1 / 5);
alpha[3] = lerp(alpha0,2 / 5);
alpha[4] = lerp(alpha0,3 / 5);
alpha[5] = lerp(alpha0,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
的作用,请参阅 wikipedia 或 HLSL 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 / DX12 的 DirectX Tool Kit 中。 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等)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。