Best Practices for Multiple Shaders

I am a longtime user of the fixed function pipeline and I’m still adjusting to new world of shaders and deprecated functions. It seems obvious to me that if I have a collection of different objects with different rendering needs that I want a collection of different shader programs that I selectively enable and disable before I draw each object. The advice that I’ve encountered seems to support the multiple shader approach rather than the “ubershader” with lots of switches model.

My question is what are the best practices for handling functionality that is common to all of the shaders? Let’s say I have two objects in a scene and one I want to render using toon lighting and the other using a more realistic approach. My view and projection matrices are the same. In the old fixed function style I would set them once for the scene and not worry about them again. It seems that older versions of GLSL allowed for that kind of approach with ftransform or the common uniform variables like gl_ProjectionMatrix, but I see that use of these is not recommended. I can certainly just call glUniform again for all of the things that are in common between the shaders whenever I enable a different program, but this seems a bit onerous (especially if the vertex shader is actually the same and I’m just changing the fragment shader).

If that is the way this works, that is fine and I can certain write code to handle that, it just seems to awkward and clumsy that I thought I would ask how others approach this and if there was a better practice that I should be following.

Behind the scenes, the compatibility profile still loads uniforms to your shaders when they reference the built-in uniforms (gl_ModelViewMatrix) and the like. So using them is no different than supplying your own set of uniforms - except that your application can sometime be easier to manage.

If your version of GL supports uniform buffer objects then you can pack all your ‘static matricies for the frame’ into a buffer object and reference that as a uniform block. You can then just use glSubBufferData to modify the matrix which needs altering and the entire BO is automatically avialble to all shaders where the uniform block was ‘linked’. Clever this ‘global’ parameter stuff.

Okay, the first option is basically “the built-in uniforms are still available”. That seems less compelling since I was trying to find ways to update myself to using non-deprecated functionality. I’ll agree that they are certainly easier (which would lead one to wonder why they would be deprecated, but that is a whole different discussion…).

The uniform buffer objects I had not previously encountered, however. It looks like that is at least a step in the right direction.

You may also want to look at subroutine uniforms. The pointer has to be loaded to choose the subroutine but the shader doesn’t have to be reloaded

eg
vec4 colour = fetch_colour(tex0.st);
vec4 colour = apply_lighting(colour);
output_colour(colour);

where each of these procedures is a uniform loaded to control the logic

I have a very similar problem. I made my framework by looking at TheOpenGLBook (http://openglbook.com/the-book/chapter-4-entering-the-third-dimension/) website which updates the view matrix thus:

glUseProgram(ProgramId);
glUniformMatrix4fv(ViewMatrixUniformLocation, 1, GL_FALSE, ViewMatrix);
glUseProgram(0);

Even the projection matrix is updated in the same way.

Now I maintain static matrices for the view matrix and projection matrix, but I call glCreateProgram() multiple times and attach different vertex and fragment shaders to these programs.
Now here lies the rub, when I make the call to update the view and projection matrix, which program ID do I use?

Since they all have their own uniform variables, you have to update all of them.

Yes that works. Thanks a lot.
But why do I need to update for every ProgramId? Shouldn’t the View Matrix and Projection Matrix be a property of the OpenGL state rather than individual shaders?
And when is it advisable to make multiple ProgramIDs and when to make just one and attach different shaders to the same ProgramID?

Shouldn’t the View Matrix and Projection Matrix be a property of the OpenGL state rather than individual shaders?

No. Uniform state, all uniform state, is owned by the individual programs.

If you want to share uniform state, then you need to use a uniform buffer.

And when is it advisable to make multiple ProgramIDs and when to make just one and attach different shaders to the same ProgramID?

The later is never advisable. It’s best to forget that you can “re-link” a program.

Ah so I should put my static View and Projection matrices in a uniform buffer. That makes sense.

Thanks a lot.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.