GraphicsMuncher
GraphicsMuncher

Reputation: 4641

Proper way to manage shaders in OpenGL

I'm writing code in C++ to deal with the usual 3D stuff - models, materials, lights, etc. However, I'm finding that for anything to work, it needs to know about the shader. EG, to set uniform variables for a material, you need to know their handles from the shader; to load a mesh into memory, you need to know handles to different in locations.

I've ended up having models, materials, etc. each have a handle to the shader so they can do things like glUniform1f(shader->getKdLocation(),kd), but this kind of hot potato seems like bad design to me. I've seen tutorials where uniforms and ins and outs are hardcoded in the shader (eg layout = 0) and then just bound with glUniform1f(0,kd),. However, this means the rest of my code will only function with my specially laid out shaders and therefore seems like a suboptimal solution. Also, I don't think you can do this for subroutines, which makes this an inconsistent option.

It seems like a choice between everything getting a shader reference and in some cases being unable to even properly instantiate without one (eg meshes) OR hardcoding numbers in more places and having to deal with the problems that follow with hardcoding.

I should be able to have these models, lights, etc. live/operate independently and only "work" when I set a shader for the scene, but I can't seem to find a solid way to do this. My bottom line question is what is the best practice for handling shaders? I'm okay if it doesn't solve all my problems, I just want to know what a good design decision(s) is and why.

Upvotes: 7

Views: 3767

Answers (2)

luke
luke

Reputation: 37463

I had the same concern with regards to loose association between the uniform specification on the client and the actual uniform location as laid out in the shader.

One tact you can take is to use glGetUniformLocation to determine the location of a uniform variable based on its name. It may still not be perfect, but it's probably the best you can do.

Example:

// Old way:
GLfloat myfloat = ...;
glUniform1f(0, myfloat);   // Hope that myfloat was supposed to go at location 0...

vs

// Slightly better...
GLfloat myfloat = ...;
GLint location = glGetUniformLocation(myprogram, "myfloat");
glUniform1f(location, myfloat);   // We know that a uniform with name "myfloat" goes at this location

Some extra work with glGetActiveUniform would also allow you to make sure that "myfloat" has the type/size/etc that you had expected.

Upvotes: 0

Michael IV
Michael IV

Reputation: 11436

The problem is your engine architecture,or it's lack.In many game engines game objects are divided into several categories like Mesh object which takes care of geometry (vertex buffers etc),Material objects(responsible for the appearance of the mesh object).In such a design the material is usually an entity which contains info for the uniforms that being passed into shaders during rendering.Such a design allows a good amount of flexibility as you can reuse,reassign different materials between different renderable objects.So for the starter you can set specific shader program to specific material type so each time a mesh is drawn, its materials interacts with the shader program passing in all needed uniforms.

I would suggest you to take a look at open source OpenGL engine to get an idea how it works.

Upvotes: 2

Related Questions