Currently I have only a really basic shader and a shader class.
My question is that if I want to make different shaders (with different uniforms, inputs, etc) how should the architecture look like?
Edit: I want it in a way that I can add/remove/modify shaders without recompiling.
(#1, #2, #3 were written before this edit, so I'm sorry if they are irrelevant now)
(I wrote #4 just now, and currently I am very happy with it, but I would like to hear your opinion about it.)
I have a few ideas:
(1.) Should I make an abstract shader class, and a derived class for every invidual shader program?
- (So the abstract class handles the general things, and the derived class handles the specific things, like uniforms, inputs, etc)
But in my opinion this isn't a good practice, because for every shader I create, I have to create a new class too, which means that if I want to add a new shader, or modify one's source, I have to recompile the engine.
(2.) Should I register the shader specific things (location of inputs, uniforms, etc.), and pass EVERY possible required data to the shader, which will use only the ones which were registered during the shader's compile time?
Like Shader A needs position and normal, Shader B needs position and UV.
position, normal and UV will be passed to the active shader object, and it will buffer only those which it needs (based on the registered inputs, uniforms, etc) and won't care about the others.
(3.) Having every possible input, uniform etc in all the shaders. The active shader will receive every required input, uniform, etc. So a shader is only unique in its main(). Like Shader A uses input 1,2,3, shader B uses input 1,2,5. (But both of them will receive all inputs)
These are the way I thought of, but these aren't very good in my opinion, there must be a good way for this.
(4.) What I most recently thought of:
Only one shader class. has a field: unordered_map < name, location at shader >
During shader initialization, I store the input, uniforms, etc in the previously mentioned map.
When I want to render the Object, I call the active shader's
These methods receive a string and a data (like vec3, vec4, mat4, etc)
In these methods, I check if the string is in the map, and if it is, I buffer the data to the location.
I think this is good, because there will be a few method calls for every object, and then finding a key in an unordered_map is really fast.
What do you say?