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

问题描述 投票:0回答:1

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

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

在这里

src
将是指向 dds 文件内容的指针 (full dds),而
format
将是我们要将
src
转换成的格式,该函数返回一个字节向量,其中包含转换后的 dds.
正如我之前所说,输入 DDS 可以是任何格式(我的意思是不是任何,但我们最好猜测它是任何格式)它可以是 compresseduncompressed 所以我们也需要处理这两种情况 编写此函数的正确方法是什么?
目前我有这段代码,它will转换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;
}

此代码似乎有效,但如我所说,输出没有 transparent 背景,整体看起来不对......我该怎么办?我对 DirectXTex API 不是很熟悉,所以如果有人能帮助我,我会很高兴。

我尝试将格式为

DXGI_FORMAT_B8G8R8A8_UNORM
的 DDS 纹理转换为
DXGI_FORMAT_R8_UNORM
但失败了。
输入有透明度但输出只有白色背景
对话后纹理的内容也是错误的。

c++ directx dds-format directxtk
1个回答
0
投票

DXGI_FORMAT_B8G8R8A8_UNORM
DXGI_FORMAT_R8_UNORM
你想要什么行为?

RGB 到 R 转换的默认行为是使用亮度将 RGB 转换为灰度并将其存储在 RED 通道中。没有地方可以在目标中放置透明通道。

您是否希望 alpha 通道在这里“屏蔽”图像?如果是这样,“背景颜色”是什么?

如果你想保留 alpha 通道,你需要使用

DXGI_FORMAT_R8A8_UNORM
.

DirectXTex 支持其他映射通道的方式。您可以进行频道调配,也可以使用

TEX_FILTER_RGB_COPY_RED
TEX_FILTER_RGB_COPY_GREEN
TEX_FILTER_RGB_COPY_BLUE
等标志。请参见过滤标志

© www.soinside.com 2019 - 2024. All rights reserved.