有关 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
作用,请参阅wikipedia或HLSL 文档。
使用压缩纹理渲染
如果要使用 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 等)