GLSL向几何着色器传递数据时出现编译错误的问题问询
问题原因
报错的核心原因是顶点着色器的输出接口块声明不符合GLSL接口规范:
- GLSL中,连接顶点着色器和几何着色器的接口块,只有几何着色器侧的输入需要声明为不定长数组——因为几何着色器一次要读取整个图元的所有顶点数据,比如三角形图元对应3个顶点;顶点着色器是逐顶点执行的,每个着色器调用仅输出单个顶点的对应数据,输出接口块不能声明为数组。
- 你在顶点着色器中将输出块
data_out声明为data_out[]不定长数组,后续直接通过data_out.xxx访问数组成员时,编译器就会抛出dot operator to an array only takes length()的错误,提示数组类型仅支持调用.length()方法,不能直接访问成员字段。 - 额外的冗余问题:你将全局统一的MVP矩阵通过逐顶点接口块传递完全没有必要——
u_Proj、u_View、u_Model本身是uniform变量,所有着色器阶段都可以直接声明访问,逐顶点传递只会浪费接口带宽,没有任何收益。
移除几何着色器时代码能正常运行,是因为部分GPU驱动在顶点着色器直连片元着色器的管线中,会对未指定大小的输出接口块做隐式兼容,默认按单实例处理;但加入几何着色器后,管线接口校验规则变严格,非法声明就会直接报错。
修复方案
按以下步骤调整代码即可:
- 修改顶点着色器的输出接口块声明,删除末尾的
[],将其定义为单实例变量而非数组 - 删除接口块中冗余的
mat4 v_Proj字段,在几何着色器中直接声明对应uniform变量计算MVP矩阵即可 - 保持几何着色器的输入接口块为不定长数组,该写法符合规范,因为几何着色器需要访问图元的所有顶点数据
修正后的顶点着色器代码
#version 330 core layout(location = 0) in vec4 position; layout(location = 1) in vec2 texCoord; layout(location = 2) in vec3 normal; //MVP uniform变量 uniform mat4 u_Model; uniform mat4 u_View; uniform mat4 u_Proj; //光照相关uniform变量 uniform mat4 u_InvTranspModel; uniform mat4 u_LightMVP; //输出接口块:顶点着色器侧为单实例,不要加[] out DATA { vec2 v_TexCoord; vec3 v_Normal; vec3 v_FragPos; vec4 v_LightSpacePos; } data_out; void main() { // 输出世界空间下的顶点位置到内置gl_Position,供几何着色器读取 gl_Position = u_Model * position; data_out.v_TexCoord = texCoord; data_out.v_Normal = mat3(u_InvTranspModel) * normal; // 光照相关数据赋值 data_out.v_FragPos = vec3(u_Model * position); data_out.v_LightSpacePos = u_LightMVP * position; }
修正后的几何着色器代码
#version 330 core layout(triangles) in; layout(triangle_strip, max_vertices = 3) out; // 直接声明需要用到的MVP相关uniform,不需要通过顶点接口块传递 uniform mat4 u_Proj; uniform mat4 u_View; uniform mat4 u_Model; //输入接口块:几何着色器侧为不定长数组,用于接收三角形3个顶点的数据,该写法正确 in DATA { vec2 v_TexCoord; vec3 v_Normal; vec3 v_FragPos; vec4 v_LightSpacePos; } data_in[]; //输出到片元着色器的varying变量 out vec2 g_TexCoord; out vec3 g_Normal; out vec3 g_FragPos; out vec4 g_LightSpacePos; void main() { // 全局计算一次MVP矩阵即可,所有顶点复用 mat4 MVP = u_Proj * u_View * u_Model; // 处理第一个顶点 gl_Position = MVP * gl_in[0].gl_Position; g_Normal = data_in[0].v_Normal; g_FragPos = data_in[0].v_FragPos; g_TexCoord = data_in[0].v_TexCoord; g_LightSpacePos = data_in[0].v_LightSpacePos; EmitVertex(); // 处理第二个顶点 gl_Position = MVP * gl_in[1].gl_Position; g_Normal = data_in[1].v_Normal; g_FragPos = data_in[1].v_FragPos; g_TexCoord = data_in[1].v_TexCoord; g_LightSpacePos = data_in[1].v_LightSpacePos; EmitVertex(); // 处理第三个顶点 gl_Position = MVP * gl_in[2].gl_Position; g_Normal = data_in[2].v_Normal; g_FragPos = data_in[2].v_FragPos; g_TexCoord = data_in[2].v_TexCoord; g_LightSpacePos = data_in[2].v_LightSpacePos; EmitVertex(); EndPrimitive(); }
内容的提问来源于stack exchange,提问作者will martin
相关产品推荐
相关产品推荐

