Maybe Uniform Buffer Objects may help.
Here is some pseudo code showing how I use uniform buffer objects to make dealing with Uniforms nicer:
//shader Uniform Block Object declaration
layout(std140) uniform matrix1
{
uniform mat4 glm_ProjectionMatrix;
uniform mat4 glm_ModelViewMatrix;
uniform mat4 glm_NormalMatrix;
};
//C++ side "map" into shader UBO
GLfloat uniformBlock_matrix1[] =
{ //layout(std140) uniform matrix1
1.0,0.0,0.0,0.0, //mat4 glm_ProjectionMatrix
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
1.0,0.0,0.0,0.0, //mat4 glm_ModelViewMatrix
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
1.0,0.0,0.0,0.0, //mat4 glm_NormalMatrix
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
};
GLuint sizeof_uniformBlock_matrix1 = sizeof(uniformBlock_matrix1);
//convenience map into uniformBlock_matrix1
mat4 &glm_ProjectionMatrix = (mat4&)uniformBlock_matrix1[0];
mat4 &glm_ModelViewMatrix = (mat4&)uniformBlock_matrix1[16];
mat4 &glm_NormalMatrix = (mat4&)uniformBlock_matrix1[32];
GLuint uniformBlock_matrix1_id; // uniform block matrix1 ID
GLuint shader_id;
void defineUniformBlockObject(GLuint binding_point, const char *GLSL_block_string, GLuint &uniformBlock_id)
{
// externally used ID returned //////////////////////////////////
glGenBuffers(1, &uniformBlock_id);
////////////////////////////////////////////////////////////////
//"layout(std140) uniform GLSL_block_string"
GLuint uniformBlockIndex = glGetUniformBlockIndex(shader_id, GLSL_block_string);
//And associate the uniform block to binding point
glUniformBlockBinding(shader_id, uniformBlockIndex, binding_point);
//Now we attach the buffer to UBO binding_point...
glBindBufferBase(GL_UNIFORM_BUFFER, binding_point, uniformBlock_id);
//We need to get the uniform block's size in order to back it with the
//appropriate buffer
GLsizei uniformBlockSize;
glGetActiveUniformBlockiv(shader_id, uniformBlockIndex,
GL_UNIFORM_BLOCK_DATA_SIZE,
&uniformBlockSize);
//Create UBO.
glBindBuffer(GL_UNIFORM_BUFFER, uniformBlock_id);
glBufferData(GL_UNIFORM_BUFFER, uniformBlockSize, NULL, GL_DYNAMIC_DRAW);
}
// just after compile, link, use shader code ie
shader_id = glCreateProgram();
defineUniformBlockObject(1,"matrix1",uniformBlock_matrix1_id);
Set_values_each_frame() {
// Now you can just set each uniform on C++ side with convenient looking code
glm_ModelViewMatrix *= lookAt( vec3(eye_pos),
vec3(0.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0)
);
glm_ProjectionMatrix *= perspective(20.0f,w/h,1.0f,21.0f);
glm_NormalMatrix = transpose(inverse(glm_ModelViewMatrix));
//etc ...
}
Draw() {
// then in your render function the part that updates the uniform values
// never has to change, just call the following two lines
// set your UBO. It sets all at once glm_ModelViewMatrix,
// glm_ProjectionMatrix, glm_NormalMatrix ... and whatever you have in your UBO
glBindBuffer(GL_UNIFORM_BUFFER, uniformBlock_matrix1_id);
glBufferData(GL_UNIFORM_BUFFER, sizeof_uniformBlock_matrix1, uniformBlock_matrix1, GL_DYNAMIC_DRAW);
// then draw your VAOs
glBindVertexArray(vao_id);
glDrawArrays(GL_TRIANGLES , 0, vao_elementcount);
}
Notice how I have not set any individual uniform values anywhere with glUniformNx. It seems that this might be slower sending the whole block of uniforms at once but I have found in my case that this is actually faster to send a entire block at once rather than using separate glUniformNx calls. So it has made my code easier to deal with and performance has been improved with UBOs.
Note the OpenGL Math Libray library is used to get the matrix math functionality.