c++ 在DirectXTex中将任何输入DDS转换为另一种DDS格式

xe55xuns  于 2023-05-02  发布在  其他
关注(0)|答案(1)|浏览(136)

我想创建一个函数来将输入的dds文件转换为另一种格式(也是 DDS)函数签名将是这样的

std::vector<byte> ConvertDDS(const byte* src, const size_t src_len, const DXGI_FORMAT format)

在这里,src将是指向dds文件内容 (完整dds) 的指针,而format将是我们想要将src转换为的格式,该函数返回一个包含转换后的dds的字节向量。
正如我之前所说的,输入 DDS 可以是任何格式(我的意思是不是任何格式,但我们最好猜测它将是任何格式),它可以是 * 压缩 * 或 * 未压缩 *,所以我们需要处理这两种情况以及什么是正确的和正确的方式来编写这个函数?
目前我有这个代码,它 * 将 * 转换DDS,但结果不是我除了他们是:

  • 首先,大多数输入的dds都有 * 透明的背景 *,但是这个方法不能保持它,并且会用白色替换背景
  • 第二,转换后的纹理将只有一个alpha通道,这会使整个纹理变得无用

下面是我当前代码的一个工作示例

#include <iostream>
#include <fstream>
#include <vector>
#include <DirectXTex.h>

void throw_error(std::string err, HRESULT hr)
{
    LPWSTR errorText = nullptr;
    DWORD messageLength = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        nullptr, hr & 0x0000FFFF, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&errorText, 0, nullptr);
    if (messageLength > 0)
    {
        std::wstring wErrorText(errorText);
        std::string errorString(wErrorText.begin(), wErrorText.end());
        LocalFree(errorText);
        throw std::invalid_argument(err + ": " + errorString);
    }
}

std::vector<byte> ConvertDDS(const byte* src, const size_t src_len, const DXGI_FORMAT format)
{
    DirectX::TEX_FILTER_FLAGS dwFilter = DirectX::TEX_FILTER_DEFAULT;
    DirectX::TEX_FILTER_FLAGS dwSRGB = DirectX::TEX_FILTER_DEFAULT;
    DirectX::TEX_FILTER_FLAGS dwConvert = DirectX::TEX_FILTER_DEFAULT;
    DirectX::TEX_FILTER_FLAGS dwFilterOpts = DirectX::TEX_FILTER_DEFAULT;
    DirectX::TexMetadata meta;

    // Initialize COM (needed for WIC)
    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr))
        throw_error("Failed to initialize COM", hr);

    std::unique_ptr<DirectX::ScratchImage> image(new (std::nothrow) DirectX::ScratchImage);
    // load texture from memory, supported types are DDS, PNG and TGA
    hr = DirectX::LoadFromDDSMemory(src, src_len, DirectX::DDS_FLAGS_ALLOW_LARGE_FILES, &meta, *image);

    if (FAILED(hr))
        throw_error("Can't load texture from memory", hr);

    // If format doesn't match convert it
    if (meta.format != format)
    {
        // --- Planar ------------------------------------------------------------------
        if (DirectX::IsPlanar(meta.format))
        {
            auto img = image->GetImage(0, 0, 0);
            assert(img);
            const size_t nimg = image->GetImageCount();

            std::unique_ptr<DirectX::ScratchImage> timage(new (std::nothrow) DirectX::ScratchImage);

            hr = ConvertToSinglePlane(img, nimg, meta, *timage);
            if (FAILED(hr))
                throw_error(" FAILED [converttosingleplane]", hr);

            auto& tinfo = timage->GetMetadata();

            meta.format = tinfo.format;

            assert(meta.width == tinfo.width);
            assert(meta.height == tinfo.height);
            assert(meta.depth == tinfo.depth);
            assert(meta.arraySize == tinfo.arraySize);
            assert(meta.mipLevels == tinfo.mipLevels);
            assert(meta.miscFlags == tinfo.miscFlags);
            assert(meta.dimension == tinfo.dimension);

            image.swap(timage);
        }

        // --- Decompress --------------------------------------------------------------
        if (DirectX::IsCompressed(meta.format))
        {
            std::unique_ptr<DirectX::ScratchImage> nimage(new (std::nothrow) DirectX::ScratchImage);
            hr = DirectX::Decompress(
                image->GetImages(),
                image->GetImageCount(),
                meta,
                DXGI_FORMAT_UNKNOWN,
                *nimage
            );
            if (FAILED(hr))
                throw_error("Can't decompress dds texture", hr); // decompress new texture failed
            meta.format = nimage->GetMetadata().format;
            image.swap(nimage);
        }

        if (meta.format != format)
        {
            // If the original file is compressed we compress the src
            // Else we just convert it
            if (DirectX::IsCompressed(format))
            {
                std::unique_ptr<DirectX::ScratchImage> nimage(new (std::nothrow) DirectX::ScratchImage);
                hr = DirectX::Compress(
                    image->GetImages(),
                    image->GetImageCount(),
                    meta,
                    format,
                    DirectX::TEX_COMPRESS_FLAGS::TEX_COMPRESS_DEFAULT,
                    DirectX::TEX_THRESHOLD_DEFAULT,
                    *nimage
                );
                if (FAILED(hr))
                    throw_error("Can't compress dds texture", hr); // convert new texture failed
                image.swap(nimage);
            }
            else
            {
                std::unique_ptr<DirectX::ScratchImage> nimage(new (std::nothrow) DirectX::ScratchImage);
                hr = DirectX::Convert(
                    image->GetImages(),
                    image->GetImageCount(),
                    meta,
                    format,
                    dwFilter | dwFilterOpts | dwSRGB | dwConvert,
                    DirectX::TEX_THRESHOLD_DEFAULT,
                    *nimage
                );
                if (FAILED(hr))
                    throw_error("Can't convert dds texture", hr); // convert new texture failed
                image.swap(nimage);
            }
            meta.format = image->GetMetadata().format;
        }
    }

    std::unique_ptr<DirectX::Blob> blob(new (std::nothrow) DirectX::Blob);
    DirectX::SaveToDDSMemory(
        image->GetImages(),
        image->GetImageCount(),
        image->GetMetadata(),
        DirectX::DDS_FLAGS_NONE,
        *blob.get()
    );
    std::vector<byte> result(blob->GetBufferSize());
    std::memcpy(result.data(), blob->GetBufferPointer(), blob->GetBufferSize());
    return result;
}

这段代码似乎是工作,但输出正如我所说的没有 * 透明 * 的背景和整体看起来是错误的。我该怎么办我不是很熟悉DirectXTex API,所以如果有人能帮助我,我会很高兴。
我尝试将格式为DXGI_FORMAT_B8G8R8A8_UNORM的DDS纹理转换为DXGI_FORMAT_R8_UNORM,但失败。
输入具有透明度,但输出仅具有白色背景
经过交谈后,纹理的内容也是错误的。

polhcujo

polhcujo1#

DXGI_FORMAT_B8G8R8A8_UNORMDXGI_FORMAT_R8_UNORM需要什么行为?
RGB到R转换的默认行为是使用亮度将RGB转换为灰度并将其存储在RED通道中。在目标中没有放置透明通道的位置。
您是否希望Alpha通道在此处“屏蔽”图像?如果是,“背景色”是什么?
如果你想保留alpha通道,你需要使用DXGI_FORMAT_R8A8_UNORM
DirectXTex支持其他Map通道的方式。您可以进行通道混合,也可以使用TEX_FILTER_RGB_COPY_REDTEX_FILTER_RGB_COPY_GREENTEX_FILTER_RGB_COPY_BLUE等标志。参见Filter flags
有关执行任意通道混合的示例,请参见texconv。

相关问题