You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

OpenGL法线映射中GetAttribLocation对两个输入返回-1问题排查

解决顶点着色器属性获取返回-1的问题

这问题我之前碰到过好几次!根源在于GLSL编译器的死代码消除优化——它会把任何在着色器执行过程中没有被实际用到的属性、变量或者uniform给删掉,哪怕你在代码里声明了它们。

看你的顶点着色器代码:当uUseNormalMap为0的时候,aNormalTangentaNormalBiTangent完全没被使用;而编译器在编译阶段没办法知道uUseNormalMap的运行时值,所以它会认为这两个属性「可能永远不会被用到」,直接把它们从编译后的程序里移除了。这就是为什么glGetAttribLocation返回-1的原因——程序里已经没有这些属性了。

给你两个靠谱的解决方案:

方案1:强制让编译器保留属性(修改着色器)

你只需要让这两个属性在所有执行路径中都被「用到」就行——哪怕是做一些不影响结果的无意义运算,比如:

在你的顶点着色器main函数开头添加几行代码:

void main() {
    // 强制引用属性,乘以0不影响任何结果,但编译器会认为它们被使用了
    vec3 dummy = aNormalTangent * 0.0 + aNormalBiTangent * 0.0;
    // 把dummy和已使用的varying变量结合,确保编译器不优化掉
    vColor += vec4(dummy, 0.0);

    // 剩下的原有代码...
    vShadowCoord = vec4(uShadowMVP * vec4(aPosition, 1.0));
    vPosition = uM * vec4(aPosition, 1.0);
    vColor = vec4(aColor, 1.0);
    vTexture = aTexture;
    vNormal = normalize(vec3(uNormalMatrix * vec4(aNormal, 0.0)));
    // ... 后续原有代码
}

或者更精准地在else分支里引用它们:

} else {
    vLightPosTanSpace = vec3[10]( vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0) );
    // 引用属性避免被优化
    vec3 unused = normalize(aNormalTangent) + normalize(aNormalBiTangent);
}

这样编译器就不会把这两个属性删掉了,glGetAttribLocation就能正确返回它们的位置。

方案2:提前绑定属性位置(修改OpenGL代码)

另一种更可靠的方法是在链接着色器程序之前,用glBindAttribLocation手动指定属性的位置,比如:

// 假设你的着色器程序ID是program
glBindAttribLocation(program, 4, "aNormalTangent");
glBindAttribLocation(program, 5, "aNormalBiTangent");
// 必须在链接程序之前调用上述绑定
glLinkProgram(program);

这样不管编译器有没有优化属性,glGetAttribLocation都会返回你指定的位置(示例中的4和5),你也可以直接用这些位置来传递顶点数据。注意:这个绑定操作必须在glLinkProgram执行之前完成,否则不会生效。

我个人更推荐方案1,因为它能确保属性真正被着色器使用,而不是仅仅保留位置;方案2适合你需要固定属性位置的场景。

试一下这两个方法,应该就能解决你的问题了!

内容的提问来源于stack exchange,提问作者AudioGuy

火山引擎 最新活动