为何无法获取OpenGL着色器中position属性的位置?
这种情况我之前也踩过坑,大概率是两个原因之一:链接器优化了未被“真正使用”的顶点属性,或者你的着色器源代码传递给OpenGL时出了问题。下面给你拆解具体的问题和解决办法:
1. 链接器的激进优化“吃掉”了属性
OpenGL的链接器会非常主动地精简代码——如果它判断某个顶点属性最终不会对渲染输出产生实际影响,就会把这个属性从程序中移除,直接导致glGetAttribLocation返回-1。
看起来你的顶点着色器里把position赋值给了gl_Position,按说这个内置变量是光栅化阶段必须的,不应该被优化,但有时候链接器的判断会超出预期(比如上下文设置有特殊情况,或者后续没有实际调用这个程序进行绘制)。
最可靠的解决办法:显式绑定属性位置
在链接程序之前,用glBindAttribLocation强制指定position的属性索引,这样链接器就不会优化掉它了。代码要加在glAttachShader之后、glLinkProgram之前:
GLuint shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vsID); glAttachShader(shaderProgram, fsID); // 新增这一行,把"position"绑定到属性索引0 glBindAttribLocation(shaderProgram, 0, "position"); glLinkProgram(shaderProgram);
之后再调用glGetAttribLocation,就能拿到你指定的0了。
2. 着色器源代码传递错误
看你写的着色器字符串:
char vsSource[] = "#version 330 core\n\ in vec4 position;\ void main(void)\ {\ gl_Position = position;\ }";
这里的\n\写法有问题——在C语言字符串里,\n是换行符,但后面跟着的\会被当成字符串的一部分,导致你的顶点着色器源代码里出现无效的\字符,比如这行会变成:
\ in vec4 position;
这显然是语法错误,建议你改用更清晰的方式写着色器代码,比如用C++11的原始字符串(不用手动转义换行):
const char* vsSource = R"( #version 330 core in vec4 position; void main(void) { gl_Position = position; } )"; const char* fsSource = R"( #version 330 core out vec4 color; void main(void) { color = vec4(1.0, 0.0, 0.0, 1.0); } )";
另外注意片段着色器最好也用#version 330 core,和顶点着色器保持一致的profile,避免潜在的兼容性问题。
3. 检查链接日志排查细节
即使glLinkProgram返回成功,也可能有警告信息(比如属性被优化的提示)。建议你添加代码获取程序的链接日志,看看具体原因:
GLint linkSuccess = GL_FALSE; glGetProgramiv(shaderProgram, GL_LINK_STATUS, &linkSuccess); if (linkSuccess != GL_TRUE) { GLint logLen = 0; glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &logLen); std::string linkLog(logLen, ' '); glGetProgramInfoLog(shaderProgram, logLen, nullptr, &linkLog[0]); // 打印日志,查看警告或错误 std::cout << "Program link log:\n" << linkLog << std::endl; }
按这几个步骤排查,应该能解决问题。
内容的提问来源于stack exchange,提问作者Frank

