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"; } }
sdragonx https://github.com/sdragonx