7
votes

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.

2
"However, this means the rest of my code will only function with my specially laid out shaders and therefore seems like a suboptimal solution." Why? Do you plan on randomly using your shaders with other models they aren't designed for? Do you plan on grabbing random shaders off the internet and trying to use them? "I don't think you can do this for subroutines" People actually use subroutines?Nicol Bolas
@NicolBolas The OpenGL 4.0 SL cookbook uses subroutines and various tutorials around the web do as well. As far as I can tell, they seem like a good idea. Is there a good alternative? Also, I'd appreciate constructive comments over derogatory ones, do plan on making any?GraphicsMuncher
That was a constructive comment. I'm questioning the reasoning behind why you think that is "suboptimal". It's hard to know how to answer a question if you don't explain what you're looking for, what would be "optimal" to you. Because it's not obvious what you are doing that would make this not "optimal".Nicol Bolas
glBindAttribLocation() can be used to specify what index matches to what name before linking the shader program, allowing you to hardcode indices while retaining some compatibility. Some people enum(erate) their indices, glUniform1f(SOME_PROPERTY,kd) feels less error prone than glUniform1f(0,kd) … I'm looking into your question now pretty much...user234736

2 Answers

1
votes

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.

0
votes

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.