使用shaderc与SPIRV-Cross转换HLSL至GLSL时如何保持出入变量名一致?
HLSL转GLSL时顶点/片段着色器变量名不匹配的解决方法
问题背景
使用shaderc将HLSL编译为SPIR-V,再通过SPIRV-Cross转换为GLSL后,顶点着色器的输出变量名(如_entryPointOutput_texcoord)与片段着色器的输入变量名(如input_texcoord)不一致,导致OpenGL编译报错。
相关代码
C++转换代码
const shaderc::Compiler scompiler; shaderc::CompileOptions soptions; soptions.SetSourceLanguage(shaderc_source_language_hlsl); soptions.SetOptimizationLevel(shaderc_optimization_level_performance); soptions.SetIncluder(std::make_unique<MyIncluder>(includer)); soptions.SetAutoSampledTextures(true); soptions.SetGenerateDebugInfo(); const shaderc::SpvCompilationResult vertex_result = scompiler.CompileGlslToSpv( source, shaderc_vertex_shader, name, vert_entry, soptions); const shaderc::SpvCompilationResult fragment_result = scompiler.CompileGlslToSpv( source, shaderc_fragment_shader, name, frag_entry, soptions); const std::vector<uint32_t> spirv_vert(vertex_result.cbegin(), vertex_result.cend()); const std::vector<uint32_t> spirv_frag(fragment_result.cbegin(), fragment_result.cend()); // 原代码错误:用HLSL源码而非SPIR-V字节码初始化编译器 spirv_cross::CompilerGLSL gcompiler(source); spirv_cross::CompilerGLSL::Options goptions; goptions.version = 330; goptions.enable_row_major_load_workaround = false; goptions.emit_uniform_buffer_as_plain_uniforms = true; gcompiler.set_common_options(goptions); std::cout << gcompiler.compile();
原始HLSL代码
struct VertexInput { float4 position : POSITION; float2 texcoord : TEXCOORD0; }; struct VertexOutput { float4 position : SV_POSITION; float2 texcoord : TEXCOORD0; }; Texture2D mainTexture; SamplerState mainTextureSampler; VertexOutput vertShader(VertexInput input) { VertexOutput output; output.position = input.position; output.texcoord = input.texcoord; return output; }; float4 fragShader(VertexOutput input) : SV_TARGET { return mainTexture.Sample(mainTextureSampler, input.texcoord); };
转换后错误的GLSL
顶点着色器:
#version 330 #ifdef GL_ARB_shading_language_420pack #extension GL_ARB_shading_language_420pack : require #endif layout(location = 0) in vec4 input_position; layout(location = 1) in vec2 input_texcoord; out vec2 _entryPointOutput_texcoord; void main() { gl_Position = input_position; _entryPointOutput_texcoord = input_texcoord; }
片段着色器:
#version 330 #ifdef GL_ARB_shading_language_420pack #extension GL_ARB_shading_language_420pack : require #endif layout(binding = 0) uniform sampler2D mainTexture; in vec2 input_texcoord; layout(location = 0) out vec4 _entryPointOutput; void main() { _entryPointOutput = texture(mainTexture, input_texcoord); }
解决方法
核心思路是基于HLSL的语义标记(如TEXCOORD0)统一顶点输出与片段输入的变量名,通过SPIRV-Cross读取SPIR-V中的HLSL语义装饰来实现。
1. 修复SPIRV-Cross初始化错误
原代码错误地用HLSL源码而非编译后的SPIR-V字节码初始化编译器,需分别为顶点和片段着色器创建独立的编译器实例:
// 顶点着色器处理 spirv_cross::CompilerGLSL vert_compiler(spirv_vert); // 片段着色器处理 spirv_cross::CompilerGLSL frag_compiler(spirv_frag);
2. 基于HLSL语义统一变量名
遍历SPIR-V中的资源,读取HLSL语义标记,将顶点输出和片段输入的变量名设置为语义名称,确保二者一致:
// 通用GLSL转换配置 spirv_cross::CompilerGLSL::Options goptions; goptions.version = 330; goptions.enable_row_major_load_workaround = false; goptions.emit_uniform_buffer_as_plain_uniforms = true; // 处理顶点着色器:将输出变量名改为HLSL语义 auto& vert_resources = vert_compiler.get_shader_resources(); for (auto& output : vert_resources.stage_outputs) { // 获取HLSL语义(如TEXCOORD0) std::string semantic = vert_compiler.get_decoration_string(output.id, spv::DecorationHlslSemanticGOOGLE); // 设置变量名为语义名称 vert_compiler.set_name(output.id, semantic); } vert_compiler.set_common_options(goptions); std::string vert_glsl = vert_compiler.compile(); // 处理片段着色器:将输入变量名改为HLSL语义 auto& frag_resources = frag_compiler.get_shader_resources(); for (auto& input : frag_resources.stage_inputs) { std::string semantic = frag_compiler.get_decoration_string(input.id, spv::DecorationHlslSemanticGOOGLE); frag_compiler.set_name(input.id, semantic); } frag_compiler.set_common_options(goptions); std::string frag_glsl = frag_compiler.compile();
3. 验证shaderc编译选项
已设置的SetGenerateDebugInfo()会保留HLSL语义所需的装饰数据,无需额外修改编译选项。
转换后正确的GLSL示例
顶点着色器输出与片段着色器输入将统一使用语义名称:
顶点着色器:
#version 330 #ifdef GL_ARB_shading_language_420pack #extension GL_ARB_shading_language_420pack : require #endif layout(location = 0) in vec4 input_position; layout(location = 1) in vec2 input_texcoord; out vec2 TEXCOORD0; void main() { gl_Position = input_position; TEXCOORD0 = input_texcoord; }
片段着色器:
#version 330 #ifdef GL_ARB_shading_language_420pack #extension GL_ARB_shading_language_420pack : require #endif layout(binding = 0) uniform sampler2D mainTexture; in vec2 TEXCOORD0; layout(location = 0) out vec4 _entryPointOutput; void main() { _entryPointOutput = texture(mainTexture, TEXCOORD0); }
内容的提问来源于stack exchange,提问作者Sarathi
相关产品推荐
相关产品推荐

