Handle unknown number of texture in a generic context

Started by
2 comments, last by L. Spiro 9 years, 10 months ago

Ok I'm sorry for the terrible title, but I'm up for suggestion for a better one :)

I am trying to write a small framework for a project I have to do soon. I never did nothing more than "one cpp file" OpenGL stuff up to now, so I was trying to come up with a decent class structure as required to do what I have to do.

What is related to the following question is pretty basic:

- Mesh class that has

- ShaderProgram class (with a tables to link attributes/uniforms IDs and more user-friendly names) instance that is "used" when rendering the mesh.

- Various attributes (VBO etc.)

- A render function.

I had the plan to add a textures vector in Mesh class to relate all the textures that may be useful (e.g. normal map, occlusion, diffuse etc.) to the mesh they belong to (I have no re-use of texture for different models).

The big problem is that for the application I had to do at the end, there's no certainty on how many texture should I use for the Mesh, I could potentially have just diffuse and normal maps or I can have a dozen of texture maps needed for various parameters. That'd be fine if all the ShaderProgram will use all those textures, but that's not the case, I may need the same Mesh to be drawn with a fairly complex fragment shader or just drawing it's alpha matte.

How can I handle such situation? There's any way I can achieve such degree generality? Also I'm a tad confused, so I'm afraid this question came up like a mess, I'm sorry about that and I'm extremely happy to clarify anything unclear.

Thank you very much

Advertisement

You are confusing assets, which include all possible texture images and shader programs that could be used with a mesh, with actual OpenGL entities that are used for rendering (shader objects, and the texture objects they use). Your Mesh class sits in the middle between these two worlds, and it should handle creating OpenGL textures from images, shaders from text, etc. as sub-tasks of rendering itself in a certain way. All assets for all cases need to be associated with hte mesh, while in actual rendering you only need specific draw calls, specific shaders and only the textures they use.

Asset textures are your own read-only objects, OpenGL textures are integer IDs (presumably wrapped by entirely different classes). Even if they have a 1:1 correspondence in theory, the two kinds of texture have independent lifecycles (on disk/in memory; nonexistent/uninitialized/ready) and their only point of contact is calling glTexSubImage2D etc. when a Mesh needs to be rendered with that texture and it isn't already available from previous frames.

Omae Wa Mou Shindeiru


The big problem is that for the application I had to do at the end, there's no certainty on how many texture should I use for the Mesh, I could potentially have just diffuse and normal maps or I can have a dozen of texture maps needed for various parameters. That'd be fine if all the ShaderProgram will use all those textures, but that's not the case, I may need the same Mesh to be drawn with a fairly complex fragment shader or just drawing it's alpha matte.
How can I handle such situation? There's any way I can achieve such degree generality? ...

If the rendering pipeline supports several steps, e.g. the geometry pass and the lighting pass for a deferred renderer, or various display modes for objects in an editor, or whatever, then one needs a couple of related resources for each step. A list of resources compiled for each render call can be used to manage the state set-up of the render call. This usually includes the mesh, textures, shaders, and model belonging uniforms set-up.

If you want make things a bit more flexible, shader objects itself may declare what kind of textures they need. If the textures then are stored with a semantic, they can be picked from the model by the rendering sub-system itself.

The big problem is that for the application I had to do at the end, there's no certainty on how many texture should I use for the Mesh, I could potentially have just diffuse and normal maps or I can have a dozen of texture maps needed for various parameters. That'd be fine if all the ShaderProgram will use all those textures, but that's not the case, I may need the same Mesh to be drawn with a fairly complex fragment shader or just drawing it's alpha matte.

#1: Enumerate the types of textures you can have. Diffuse, normal maps, specular maps, etc. There is a finite amount of textures and usages of them, and at the end of the day your shader needs to know what to do with the textures it’s passed.

#2: For any given type of render, you must already have a way to determine which textures are in use for that render. Without this, you would have bigger problems than what you have now.

#3: After determining what textures you need to pass to the shader, create a shader key with that information (and in more advanced systems it can contain which type of lighting you want to use, shadows on or off, etc.) and pass that to a kind of shader manager.

#4: The shader manager uses the key to see if that shader already has been generated and compiled or not. If not, it generates it and compiles it. If so, it returns the already-generated shader.

#0: Make shaders “generatable”. There are 2 common ways to go about this.
-> #A: Stitching. Parts of the shader are in different files and using your custom system based on the flags related to which textures you need, which light equation you need, etc., you can stitch together a final shader for that specific combination of modifications.
-> #B: Permutation. It’s the same shader file, but using macros and #ifdef’s you can remove are add parts based on macros sent to the compiler unit.

At work we use #A but I personally use #B.
#A has advantages most seen in gigantic companies that produce AAA games, whereas #B is more manageable by individuals such as you and me, so for now I would recommend #B.



As I mentioned, the model has a way to know which textures to use and as such it generates a shader key.
The flags set in the shader key translate to macros that are passed to the model’s shader file and it is recompiled using #B for each unique shader key. Existing shader keys, again, are hash-mapped and quickly realized to be existing, resulting in minimal overhead even if done every frame. But you don’t need to do a hash look-up every frame because you can also easily see if the last render used the same shader key and if so immediately use the same shader that you used last time without even going to the shader manager (or whatever you have).


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement