D3D11 创建个顶点布局需要依赖编译的 vsBlob 二进制代码,这点太影响封装了,问了问 AI,引用的 GameDev 网站的一个帖子,大概原理就是:创建一个虚拟的 blob,然后通过这个 blob 创建顶点布局。

/**
 * 根据输入布局描述生成匹配的虚拟顶点着色器 Blob
 * @param layoutDesc 输入布局描述数组
 * @param numElements 元素个数
 * @param ppBlob 返回生成的Blob对象
 * @return HRESULT 成功返回S_OK
 */
CGL_API HRESULT CreateDummyVertexShaderBlob(
    const D3D11_INPUT_ELEMENT_DESC* layoutDesc,
    UINT size,
    ID3DBlob** ppBlob
)
{
    if (!layoutDesc || !ppBlob)
        return E_INVALIDARG;

    // 构建着色器代码字符串
    std::stringstream shaderCode;
    shaderCode << "struct VS_INPUT {\n";

    // 遍历每个输入元素,生成对应的成员变量
    for (UINT i = 0; i < size; ++i) {
        const auto& desc = layoutDesc[i];

        // 根据 DXGI 格式推导 HLSL 类型
        std::string hlslType = "float4"; // 默认
        switch (desc.Format) {
        case DXGI_FORMAT_R32_FLOAT:             hlslType = "float"; break;
        case DXGI_FORMAT_R32G32_FLOAT:          hlslType = "float2"; break;
        case DXGI_FORMAT_R32G32B32_FLOAT:       hlslType = "float3"; break;
        case DXGI_FORMAT_R32G32B32A32_FLOAT:    hlslType = "float4"; break;
        case DXGI_FORMAT_R32_UINT:              hlslType = "uint"; break;
        case DXGI_FORMAT_R32_SINT:              hlslType = "int"; break;
        // todo... 添加更多类型
        default:
            break;
        }

        // 语义名称 + 索引(例如:POSITION0, TEXCOORD1)
        std::string semantic = desc.SemanticName;
        if (desc.SemanticIndex > 0) {
            semantic += std::to_string(desc.SemanticIndex);
        }

        // 添加到结构体
        shaderCode << "    " << hlslType << " " << semantic
                   << " : " << desc.SemanticName << desc.SemanticIndex << ";\n";
    }

    // 添加占位的输出结构体(像素着色器需要)
    shaderCode << "};\n\n";
    shaderCode << "struct VS_OUTPUT {\n";
    shaderCode << "    float4 position : SV_POSITION;\n";
    shaderCode << "};\n\n";

    // 顶点着色器主函数 - 什么都不做,只返回一个固定的位置
    // 注意:SV_POSITION必须有值,否则D3D会报错
    shaderCode << "VS_OUTPUT main(VS_INPUT input) {\n";
    shaderCode << "    VS_OUTPUT output;\n";
    shaderCode << "    output.position = float4(0.0f, 0.0f, 0.0f, 1.0f);\n";
    shaderCode << "    return output;\n";
    shaderCode << "}\n";

    // 2. 编译这个动态生成的着色器
    ID3DBlob* pErrorBlob = nullptr;
    HRESULT hr           = D3DCompile(
        shaderCode.str().c_str(),
        shaderCode.str().length(),
        nullptr,                        // 文件名
        nullptr,                        // 宏定义
        nullptr,                        // 包含文件处理
        "main",                         // 入口函数名
        "vs_4_0",                       // 着色器模型
        D3DCOMPILE_OPTIMIZATION_LEVEL0, // 不优化,编译更快
        0,
        ppBlob,
        &pErrorBlob
    );

    // 如果编译失败,输出错误信息(用于调试)
    if (FAILED(hr) && pErrorBlob) {
        OutputDebugStringA((char*) pErrorBlob->GetBufferPointer());
        pErrorBlob->Release();
    }

    return hr;
}

//
// 使用
//

// 顶点格式
const D3D11_INPUT_ELEMENT_DESC VERTEX_LAYOUT[4] = {
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(vertex, position), D3D11_INPUT_PER_VERTEX_DATA, 0 },
    {   "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(vertex,   normal), D3D11_INPUT_PER_VERTEX_DATA, 0 },
    {    "COLOR", 0,  DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(vertex,    color), D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0,    DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(vertex, texcoord), D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

// Compile the vertex shader
ID3DBlob* pVSBlob = nullptr;
HRESULT hr = CreateDummyVertexShaderBlob(VERTEX_LAYOUT, 4, &pVSBlob);

if (FAILED(hr)) {
    // 检查错误...
}
else {
    // Create the input layout
    hr = d3dDevice->CreateInputLayout(
        inputLayout, inputLayoutSize,
        pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(),
        m_inputLayout.ReleaseAndGetAddressOf()
    );

    pVSBlob->Release();
}

 

附录:dxgi 格式转 hlsl 格式

#include <dxgiformat.h>

/* 将DXGI格式转换为HLSL类型字符串
 * 基于完整的DXGI_FORMAT枚举(十六进制值已标注)
 */
CGL_API std::string DXGIFormatToHLSLType(DXGI_FORMAT format)
{
    switch (format) {
    // === 128-bit (4分量) 格式 ===
    case DXGI_FORMAT_R32G32B32A32_TYPELESS: // 0x1
    case DXGI_FORMAT_R32G32B32A32_FLOAT:    // 0x2
        return "float4";
    case DXGI_FORMAT_R32G32B32A32_UINT:     // 0x3
        return "uint4";
    case DXGI_FORMAT_R32G32B32A32_SINT:     // 0x4
        return "int4";

    // === 96-bit (3分量) 格式 ===
    case DXGI_FORMAT_R32G32B32_TYPELESS: // 0x5
    case DXGI_FORMAT_R32G32B32_FLOAT:    // 0x6
        return "float3";
    case DXGI_FORMAT_R32G32B32_UINT:     // 0x7
        return "uint3";
    case DXGI_FORMAT_R32G32B32_SINT:     // 0x8
        return "int3";

    // === 64-bit (4分量) 格式 ===
    case DXGI_FORMAT_R16G16B16A16_TYPELESS: // 0x9
    case DXGI_FORMAT_R16G16B16A16_FLOAT:    // 0xa
        return "float4";
    case DXGI_FORMAT_R16G16B16A16_UNORM:    // 0xb
    case DXGI_FORMAT_R16G16B16A16_SNORM:    // 0xd
        return "float4";
    case DXGI_FORMAT_R16G16B16A16_UINT:     // 0xc
        return "uint4";
    case DXGI_FORMAT_R16G16B16A16_SINT:     // 0xe
        return "int4";

    // === 64-bit (2分量) 格式 ===
    case DXGI_FORMAT_R32G32_TYPELESS: // 0xf
    case DXGI_FORMAT_R32G32_FLOAT:    // 0x10
        return "float2";
    case DXGI_FORMAT_R32G32_UINT:     // 0x11
        return "uint2";
    case DXGI_FORMAT_R32G32_SINT:     // 0x12
        return "int2";

    // === 特殊深度/模板格式 ===
    case DXGI_FORMAT_R32G8X24_TYPELESS:        // 0x13
    case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:     // 0x14
    case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: // 0x15
    case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:  // 0x16
        return "float2";

    // === 32-bit (4分量) 打包格式 ===
    case DXGI_FORMAT_R10G10B10A2_TYPELESS: // 0x17
    case DXGI_FORMAT_R10G10B10A2_UNORM:    // 0x18
    case DXGI_FORMAT_R10G10B10A2_UINT:     // 0x19
    case DXGI_FORMAT_R11G11B10_FLOAT:      // 0x1a
        return "float4";

    // === 32-bit (4分量) 格式 ===
    case DXGI_FORMAT_R8G8B8A8_TYPELESS:   // 0x1b
    case DXGI_FORMAT_R8G8B8A8_UNORM:      // 0x1c
    case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: // 0x1d
    case DXGI_FORMAT_R8G8B8A8_SNORM:      // 0x1f
        return "float4";
    case DXGI_FORMAT_R8G8B8A8_UINT:       // 0x1e
        return "uint4";
    case DXGI_FORMAT_R8G8B8A8_SINT:       // 0x20
        return "int4";

    // === 32-bit (2分量) 格式 ===
    case DXGI_FORMAT_R16G16_TYPELESS: // 0x21
    case DXGI_FORMAT_R16G16_FLOAT:    // 0x22
    case DXGI_FORMAT_R16G16_UNORM:    // 0x23
    case DXGI_FORMAT_R16G16_SNORM:    // 0x25
        return "float2";
    case DXGI_FORMAT_R16G16_UINT:     // 0x24
        return "uint2";
    case DXGI_FORMAT_R16G16_SINT:     // 0x26
        return "int2";

    // === 32-bit (1分量) 格式 ===
    case DXGI_FORMAT_R32_TYPELESS: // 0x27
    case DXGI_FORMAT_D32_FLOAT:    // 0x28
    case DXGI_FORMAT_R32_FLOAT:    // 0x29
        return "float";
    case DXGI_FORMAT_R32_UINT:     // 0x2a
        return "uint";
    case DXGI_FORMAT_R32_SINT:     // 0x2b
        return "int";

    // === 24-bit 深度/模板格式 ===
    case DXGI_FORMAT_R24G8_TYPELESS:        // 0x2c
    case DXGI_FORMAT_D24_UNORM_S8_UINT:     // 0x2d
    case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: // 0x2e
    case DXGI_FORMAT_X24_TYPELESS_G8_UINT:  // 0x2f
        return "float";

    // === 16-bit (2分量) 格式 ===
    case DXGI_FORMAT_R8G8_TYPELESS: // 0x30
    case DXGI_FORMAT_R8G8_UNORM:    // 0x31
    case DXGI_FORMAT_R8G8_SNORM:    // 0x33
        return "float2";
    case DXGI_FORMAT_R8G8_UINT:     // 0x32
        return "uint2";
    case DXGI_FORMAT_R8G8_SINT:     // 0x34
        return "int2";

    // === 16-bit (1分量) 格式 ===
    case DXGI_FORMAT_R16_TYPELESS: // 0x35
    case DXGI_FORMAT_R16_FLOAT:    // 0x36
    case DXGI_FORMAT_D16_UNORM:    // 0x37
    case DXGI_FORMAT_R16_UNORM:    // 0x38
    case DXGI_FORMAT_R16_SNORM:    // 0x3a
        return "float";
    case DXGI_FORMAT_R16_UINT:     // 0x39
        return "uint";
    case DXGI_FORMAT_R16_SINT:     // 0x3b
        return "int";

    // === 8-bit (1分量) 格式 ===
    case DXGI_FORMAT_R8_TYPELESS: // 0x3c
    case DXGI_FORMAT_R8_UNORM:    // 0x3d
    case DXGI_FORMAT_R8_SNORM:    // 0x3f
        return "float";
    case DXGI_FORMAT_R8_UINT:     // 0x3e
        return "uint";
    case DXGI_FORMAT_R8_SINT:     // 0x40
        return "int";

    // === 特殊格式 ===
    case DXGI_FORMAT_A8_UNORM:           // 0x41 (单一 alpha 通道)
        return "float";
    case DXGI_FORMAT_R1_UNORM:           // 0x42 (1-bit格式)
        return "float";
    case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: // 0x43 (共享指数格式)
        return "float3";
    case DXGI_FORMAT_R8G8_B8G8_UNORM:    // 0x44
    case DXGI_FORMAT_G8R8_G8B8_UNORM:    // 0x45
        return "float4";                 // 打包格式

    // === BC压缩格式 (纹理压缩) ===
    case DXGI_FORMAT_BC1_TYPELESS:   // 0x46
    case DXGI_FORMAT_BC1_UNORM:      // 0x47
    case DXGI_FORMAT_BC1_UNORM_SRGB: // 0x48
    case DXGI_FORMAT_BC2_TYPELESS:   // 0x49
    case DXGI_FORMAT_BC2_UNORM:      // 0x4a
    case DXGI_FORMAT_BC2_UNORM_SRGB: // 0x4b
    case DXGI_FORMAT_BC3_TYPELESS:   // 0x4c
    case DXGI_FORMAT_BC3_UNORM:      // 0x4d
    case DXGI_FORMAT_BC3_UNORM_SRGB: // 0x4e
    case DXGI_FORMAT_BC4_TYPELESS:   // 0x4f
    case DXGI_FORMAT_BC4_UNORM:      // 0x50
    case DXGI_FORMAT_BC4_SNORM:      // 0x51
    case DXGI_FORMAT_BC5_TYPELESS:   // 0x52
    case DXGI_FORMAT_BC5_UNORM:      // 0x53
    case DXGI_FORMAT_BC5_SNORM:      // 0x54
    case DXGI_FORMAT_BC6H_TYPELESS:  // 0x5e
    case DXGI_FORMAT_BC6H_UF16:      // 0x5f
    case DXGI_FORMAT_BC6H_SF16:      // 0x60
    case DXGI_FORMAT_BC7_TYPELESS:   // 0x61
    case DXGI_FORMAT_BC7_UNORM:      // 0x62
    case DXGI_FORMAT_BC7_UNORM_SRGB: // 0x63
        return "float4";

    // === BGR格式 ===
    case DXGI_FORMAT_B5G6R5_UNORM:        // 0x55
    case DXGI_FORMAT_B5G5R5A1_UNORM:      // 0x56
    case DXGI_FORMAT_B8G8R8A8_UNORM:      // 0x57
    case DXGI_FORMAT_B8G8R8X8_UNORM:      // 0x58
    case DXGI_FORMAT_B8G8R8A8_TYPELESS:   // 0x5a
    case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: // 0x5b
    case DXGI_FORMAT_B8G8R8X8_TYPELESS:   // 0x5c
    case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: // 0x5d
    case DXGI_FORMAT_B4G4R4A4_UNORM:      // 0x73
        return "float4";

    case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: // 0x59
        return "float4";

    // === 视频格式 ===
    case DXGI_FORMAT_AYUV:       // 0x64
    case DXGI_FORMAT_Y410:       // 0x65
    case DXGI_FORMAT_Y416:       // 0x66
    case DXGI_FORMAT_NV12:       // 0x67
    case DXGI_FORMAT_P010:       // 0x68
    case DXGI_FORMAT_P016:       // 0x69
    case DXGI_FORMAT_420_OPAQUE: // 0x6a
    case DXGI_FORMAT_YUY2:       // 0x6b
    case DXGI_FORMAT_Y210:       // 0x6c
    case DXGI_FORMAT_Y216:       // 0x6d
    case DXGI_FORMAT_NV11:       // 0x6e
    case DXGI_FORMAT_P208:       // 0x82
    case DXGI_FORMAT_V208:       // 0x83
    case DXGI_FORMAT_V408:       // 0x84
        return "float4";

    // === 调色板格式 ===
    case DXGI_FORMAT_AI44: // 0x6f
    case DXGI_FORMAT_IA44: // 0x70
    case DXGI_FORMAT_P8:   // 0x71
    case DXGI_FORMAT_A8P8: // 0x72
        return "float4";

    default:
        return "unknown";
    }
}
View Code