代码环境:Qt + OpenGL
这段时间有做一个三维模型的viewer,默认加载的模型不包含任何光照效果,之后可以通过gui菜单栏选择光照。我的朴素想法是在最开始设置相关模型数据的时候,就将模型的顶点、(默认)顶点颜色、顶点法线存放到一个QOpenGLBuffer对象m_vbo中。相当于添加光照所需的法线数据已经有了,只要在切换相关光照时更新相应着色器代码即可。于是:
//glsl: default.vert
#version 330 core
layout(location = 0)in vec3 vertPos;
layout(location = 1)in vec3 vertColor;
layout(location = 2)in vec3 vertNormal;
uniform mat4 mvpMat;
out vec3 color;
void main(){
color = vertColor;
gl_Position = mvpMat*vec4(vertPos,1.0f);
}
//initializeGL()
...
int attr = shaderProgram->attributeLocation("vertPos");
shaderProgram->setAttributeBuffer(...);
shaderProgram->enableAttributeArray(attr);
attr = shaderProgram->attributeLocation("vertColor");
shaderProgram->setAttributeBuffer(...);
shaderProgram->enableAttributeArray(attr);
attr = shaderProgram->attributeLocation("vertNormal");
shaderProgram->setAttributeBuffer(...);
shaderProgram->enableAttributeArray(attr);
...
//other.vert
#version 330 core
layout(location = 0)in vec3 vertPos;
layout(location = 2)in vec3 vertNormal;
...
上述initializaGL()按道理讲应该能够成功设置属性相关数据。那么我切换到other.vert增加光照应该没大问题。但程序可以运行,就是没有光照效果。我一直觉得可能是我自己计算模型顶点法线的方法不正确导致的,然而并不是。这个问题磨了我一个月我一直想不明白,就先搁置了。这两天重新再来审视代码,发现问题出在attr = shaderProgram->attributeLocation("vertNormal")这里。这句代码返回的attr值居然是-1,说明属性vertNormal是无效的。唔,虽然我使用布局修饰符提供了相应的属性索引,但如果声明了属性而没有使用,编译器会认为它不是活动属性并丢弃它。
总之,目前有三种解决办法:
1:在default.vert中定义一个冗余变量使用该属性即可。
2:删除default.vert的layout(location = 2)行以及initializeGL()函数中vertNormal属性设置代码,而在更新着色器代码之后,再行设置vertNormal属性。目前使用这种方式。
3:使用QOpenGLFunctions类,该类提供对OpenGL ES 2.0 API的跨平台访问。使用时,需要为自己的OpenGL窗口继承该类,并在initializaGL()调用initializeOpenGLFunctions()函数为当前环境初始化OpenGL函数解决方案,之后,在读取到模型数据后,激活相关属性并设置数据,这样,即使GLSL中没有使用,但在当前环境下,属性仍然有效。推荐使用这种方式。
QOpenGLFunctions *f = QOpenGLContext::currentContext->functions();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(Point3D),0);
f>glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(Point3D),reinterpret_cast<void*>(sizeof(QVector3D)));
f>glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,sizeof(Point3D),reinterpret_cast<void*>(2*sizeof(QVector3D)));
上述代码可以理解为:在当前环境中,激活了顶点属性数组0、1、2,并设置了相应的数据。这样,只需要在各个着色器中为属性设置对应索引即可使用,即default.vert和other.vert可以这样写:
//default.vert
#version 330 core
layout(location = 0)in vec3 vertPos;
layout(location = 1)in vec3 vertColor;
...
//other.vert
#version 330 core
layout(location = 0)in vec3 vertPos;
layout(location = 2)in vec3 vertNormal;
...