   Material system help/oppinion
  Hi! I am desiging the material system for an engine and i wanted to share my thoughts and see wheter you think it makes sense or not. Sorry about the length of the post but if anyone can give an oppinion it would make me glad, since i don´t want to spend a lot of time in a system that ends up being s__t. If anyone could point me to literature on the subject (material system design) it would be great too. A quick background on how the engine is designed:*******I have a scenegraph (more like a scenetree) comprised of nodes, each one has a spatial transform, can have (or not) a mesh and can have (or not) a skeleton, which is itself composed of more nodes. Spatial transforms are inherited between the nodes the usual way. There are several kinds of special nodes (sprites, particle systems, octrees, etc.)Then i have render layers, which are lists of pointers to the scenegraph nodes. They can get ordered in two ways (front to back, back to front respect to the camera), and they are rendered using any node as the camera. On update, the scenetree is traversed and transforms updated.On render, the user defines which layers to draw, from which camera and in which order.*******So i want to have a separate Material class, not really part of the scenegraph, which is a list of RenderStates (texture, diffuse, shader, etc...). Each Node has a stack for each type of RenderState, with a default RenderState at the bottom. When you apply a material to a Node, all RenderStates that are "customized" by that material, get stacked in their corresponding stack. The Node always keeps a pointer to the top of each stack, and these are the states used when rendering. This allows you to override materials. If you remove a material whose RenderStates are not on the top, since the stacks are going to be implemented as doubly-linked lists, you can remove the states efficiently. It is also possible to indicate that you want to add the Material states not only for the Node but for all Nodes below it in the hierarchy.Additionally, when drawing a Layer you can specifiy a temporal override material which adds itself to all nodes prior to rendering, then restores the original renderstate stacks when the layer has been rendered. This would be useful for fullscreen normal or bloom passes, etc.That´s it. Does it sound right?   
Quote:Original post by ArKano22So i want to have a separate Material class, not really part of the scenegraph, which is a list of RenderStates (texture, diffuse, shader, etc...). Each Node has a stack for each type of RenderState, with a default RenderState at the bottom. When you apply a material to a Node, all RenderStates that are "customized" by that material, get stacked in their corresponding stack. The Node always keeps a pointer to the top of each stack, and these are the states used when rendering. To be honest, I don't understand what benefit is gained by putting all the render states on a stack when you only use the top one for rendering ?Quote:This allows you to override materials. If you remove a material whose RenderStates are not on the top, since the stacks are going to be implemented as doubly-linked lists, you can remove the states efficiently. It is also possible to indicate that you want to add the Material states not only for the Node but for all Nodes below it in the hierarchy.Additionally, when drawing a Layer you can specifiy a temporal override material which adds itself to all nodes prior to rendering, then restores the original renderstate stacks when the layer has been rendered. This would be useful for fullscreen normal or bloom passes, etc.That´s it. Does it sound right?It should work, but it sounds a little bit complicated. There's always the danger to develop something "generic" which will never be used to its extends and be prone to workarounds. After 11 years of developing my engine I came up with some easy and by far not perfect system. Use it to your inspiration or as daunting example :-)Similar to your approach I have a material class which contains X definitions for X passes. Each material definition has an render stage (similar to your layer) and consists of a shader, shader parameters and render states(similar to your material content):material=post_processing  pass=x_blur, stage=10    shader=x_blur    render_state =...  pass=bloom, stage=19    shader=...When I render the scene, I collect all surfaces and put them into buckets associated with an unique material. Then I run through all passes and check if the material supports the according pass and at which stage it should be rendered. ;Quote:Original post by Ashaman73Quote:Original post by ArKano22So i want to have a separate Material class, not really part of the scenegraph, which is a list of RenderStates (texture, diffuse, shader, etc...). Each Node has a stack for each type of RenderState, with a default RenderState at the bottom. When you apply a material to a Node, all RenderStates that are "customized" by that material, get stacked in their corresponding stack. The Node always keeps a pointer to the top of each stack, and these are the states used when rendering. To be honest, I don't understand what benefit is gained by putting all the render states on a stack when you only use the top one for rendering ?Its a non-destructive way to build materials. Suppose you have 2 or 3 base materials, and you need a lot of variations in texture, blend mode and color. You can stack the base material, then add on top of it the renderstates that you need to override. If you only keep the last added renderstate, if you remove a variation to obtain the original material which is what you would expect, instead you get the default renderstates, and you would have to set the base material again. If you stack the variation on top, you can create complex materials by combination on simple ones, then remove pieces in no particular order and obtain the expected result. Another (possibly useless) benefit is that you could "blend" the states in the same stack, for example the color stack. Maybe i´m overcomplicating things though. thank you for your post, it has been very helpful!;Quote:Original post by ArKano22Its a non-destructive way to build materials. Suppose you have 2 or 3 base materials, and you need a lot of variations in texture, blend mode and color. You can stack the base material, then add on top of it the renderstates that you need to override. But I've can't see the benefit of stacking yet :-)Here's an other thought from my experiences, what doesnt mean much ;-) I believe that renderstates will be less important in the future. As an example I included a renderstate which handles texture modes etc. Nowadays this is completly handles by the shader. When you take a look at OGL3, alphatest has been deprecated (handled in the shader by discard). Leaving only z-test, stencil and blending. On the other hand (from my experience) renderstates are tighly coupled with shaders, so changing one renderstate by putting an other renderstate on the stack, could be of little or seldom benefit.If you really want to add more i.e. more details to a texture or other color, I think, that you need to handle shaders. I think of a graph material/shader system like blender or unreal(?), where you can build shaders by combining material/shader nodes. You could create shaders on the fly this way. I'm not sure if a stack-based material system would be a good compromise between usability, benefit and complexity.Still, it's just my opinion and I might be wrong :PQuote:If you only keep the last added renderstate, if you remove a variation to obtain the original material which is what you would expect, instead you get the default renderstates, and you would have to set the base material again. If you stack the variation on top, you can create complex materials by combination on simple ones, then remove pieces in no particular order and obtain the expected result. Another (possibly useless) benefit is that you could "blend" the states in the same stack, for example the color stack. Maybe i´m overcomplicating things though. This sound much like you want to blend different materials on top of previous. This means, that you have a high degree of overdraw for many effects, which could be handled by a single shader. Except post processing, here a stack system would be usable, but this is the only sitution I can think of. In your, btw. excellent, article about SSAO , did you use a deferred shader ? Blending on top of other materials is more usable in a forward shader, in a deferred or pre-light pass shader only post processing comes to mind.Btw. a graph based shader system which builds shaders on-the-fly would be sexy ^^;  I recently moved to something very similar to Ashaman's system. I was inspired by working with rendermonkey and wanted a system that allowed me to set up a render pipeline in a similarly data-driven way. So now, the only things in my scenegraph are draw nodes and transform nodes. The draw nodes can be assigned textures and geometry from a shared pool. Each draw node must live in a particular draw layer. I decide which layer to put the draw nodes in based on what shader I want them to use. The "scenegraph" gets traversed and the output put in several containers that contain arrays of texture/geometry/transform structures.I define the rendering pipeline via a human-readable configuration file. Here's an example from my half-finished deferred renderer:link to example render.conf.deferredI define a list of shaders, a list of outputs (these map to OpenGL FBOs), and a list of "passes" where, for each pass, I say which shader to use, which draw node layer to use (or "postprocess" if I just want to draw a full screen quad), what inputs to use (the textures attached to each draw node, if present, get automatically bound to TU0 and TU1), what outputs to use, and any special flags to set (alpha blending modes, etc). The code for drawing just loops through each "pass" binding the proper inputs and outputs, enabling the correct shader, and iterating through the texture/geometry/transform array in the specified layer(s) binding textures, multiplying transforms, and sending draw calls.   