Sign in to follow this  
NickGravelyn

Shader Based Scene Graph

Recommended Posts

It only took me a few minutes to get a fixed function scene graph operating, but I want to avoid fixed function as much as possible. Does anyone have any suggestions on implementing a shader based scene graph? Here's what my fixed function scene graph looked like:
-RootNode
   -TransformNode
      -MeshNode
That worked great. The root node would call its children's render function. The transform node's render function set the world matrix and then rendered all its children. The mesh node simply used the ID3DXMesh::DrawSubset() function to render itself. Everything worked fine, but to do shader based, I'm not sure how it'll work. Any ideas? [Edited by - NickGravelyn on June 9, 2006 3:45:54 AM]

Share this post


Link to post
Share on other sites
There are a few methods.
One could be to create a "shader node". It sets the shader, its parameter, then render all the childrens, and close the shader.
A second one is to move the rendering code outside the scenegraph. Create a renderer class, and in the Render() method of the scenegraph's nodes, you only send to the renderer all the informations needed to render the node (VB, IB, textures, etc.) This way you can create 2 pathes in the renderer. One which render using the FFP, and the other which render using shaders.

I'm currently using the seconde solution and am quite happy with it ^^

Share this post


Link to post
Share on other sites
I think that second idea is a great idea. That will allow me to send what I need to the function. Cool. I did create a shader node and gave it the mesh node as a child, but this clearly would mean that I could have only one mesh per shader as the shader has no way of using multiple world matrices. Your idea sounds perfect. Thanks.

Share this post


Link to post
Share on other sites
Interesting topic.
The way i handle it is that all scenenodes are processed into rendering queues within the SceneGraph which basically are lists sorted by material/shader.
The rendering occurs separately in a render manager which keeps the scene management and rendering management separate.
I also use a SceneGraph with childnodes etc, but i setup rendering lists internally for the node->mesh->submeshes based on their material/shader.

That way, if you have a shader node with a mesh that uses a shader, render it, and then traverse down the childnodes an find a node which does not use a shader but instead a simple material (say a single texture) you wouldnt have to switch states before rendering it as often if you had these material changes optimized in lists.
Or perhaps youre not allowed to add non-shader nodes to a shader node in your system?

Correct me if im wrong, cuz i might just be rambling about something ive missunderstood here :)

Share this post


Link to post
Share on other sites
Quote:
Original post by paic
There are a few methods.
One could be to create a "shader node". It sets the shader, its parameter, then render all the childrens, and close the shader.
A second one is to move the rendering code outside the scenegraph. Create a renderer class, and in the Render() method of the scenegraph's nodes, you only send to the renderer all the informations needed to render the node (VB, IB, textures, etc.) This way you can create 2 pathes in the renderer. One which render using the FFP, and the other which render using shaders.

I'm currently using the seconde solution and am quite happy with it ^^


Can you post either actual code or some pseudo code to help explain the second point? I understand it pretty well, but I'm not sure how to actually implement it.

Share this post


Link to post
Share on other sites
Quote:
Original post by NickGravelyn
Quote:
Original post by paic
There are a few methods.
One could be to create a "shader node". It sets the shader, its parameter, then render all the childrens, and close the shader.
A second one is to move the rendering code outside the scenegraph. Create a renderer class, and in the Render() method of the scenegraph's nodes, you only send to the renderer all the informations needed to render the node (VB, IB, textures, etc.) This way you can create 2 pathes in the renderer. One which render using the FFP, and the other which render using shaders.

I'm currently using the seconde solution and am quite happy with it ^^


Can you post either actual code or some pseudo code to help explain the second point? I understand it pretty well, but I'm not sure how to actually implement it.

I believe what he is talking about is something along the lines of this. Using the other device states in a scene graph (shader constants, render states, textures, ect) can greatly improve performance, since it reduces the number of useless state changes you make.

Share this post


Link to post
Share on other sites
What is the advantage of having shader information in your scene graph? Why not having shader information per model/object/entity/whatever, and having only spatial relationships in your scenegraph? Just get the visible objects from the scenegraph, sort them (by shader/texture/etc), render them. Probably I'm missing something obvious.

Share this post


Link to post
Share on other sites
I believe it's really so obvious.
Suppose, as you say, to have shader info per-node and sort nodes per-shader. Then, since this is not going to change, you save the order somewhere. No matter where, you have somewhat modified the scene graph to account for shader information.

Share this post


Link to post
Share on other sites
Quote:
Original post by roel
What is the advantage of having shader information in your scene graph? Why not having shader information per model/object/entity/whatever, and having only spatial relationships in your scenegraph? Just get the visible objects from the scenegraph, sort them (by shader/texture/etc), render them. Probably I'm missing something obvious.


Agreed. Unless we're missing something obvious, that last article oversimplifies the issue - the second approach described there does not deal with visibility culling at all, does it?

Share this post


Link to post
Share on other sites
Quote:
Original post by roel
What is the advantage of having shader information in your scene graph? Why not having shader information per model/object/entity/whatever, and having only spatial relationships in your scenegraph? Just get the visible objects from the scenegraph, sort them (by shader/texture/etc), render them. Probably I'm missing something obvious.


You are correct. Most people try to make a scene graph into something it isn't. Scene graphs shouldn't, and don't care about shaders. They only care about the relationship between the objects in the scene. A shader node isn't a good idea in my opinion. If I were doing this, I would prefer a form of the second option.

I would have all my "models" retain any information involving textures, shaders, lighting, etc. I would then have a scene graph constructed of transform node, geometry nodes, and whatever other nodes that would be needed. When a geometry node is inserted into the scene graph, it gets associated with a model, the node doesn't care about anything specifics about the model, just that it is associated with that model. Now, as you travel down the tree, updating the transformations and so forth and you get to the model, you would then send that model and the current transformation to the renderer. The renderer would then set any shader constants (such as the current transformation matrix), textures, and any other data contained within the model. It would then set the active shader as the shader the model uses and render.

Quote:
Original post by lightbringer
Agreed. Unless we're missing something obvious, that last article oversimplifies the issue - the second approach described there does not deal with visibility culling at all, does it?


A scene graph doesn't have to do visibility culling at all, it is just that most people use it to.

I'm no expert at scene graphs, but from my understanding of them, this is a decent way to do it, and how I would implement it. Hope this helps.

Share this post


Link to post
Share on other sites
Quote:
Original post by JohnnyCasil
Quote:
Original post by roel
What is the advantage of having shader information in your scene graph? Why not having shader information per model/object/entity/whatever, and having only spatial relationships in your scenegraph? Just get the visible objects from the scenegraph, sort them (by shader/texture/etc), render them. Probably I'm missing something obvious.


You are correct. Most people try to make a scene graph into something it isn't. Scene graphs shouldn't, and don't care about shaders. They only care about the relationship between the objects in the scene. A shader node isn't a good idea in my opinion. If I were doing this, I would prefer a form of the second option.

My idea was just that if a lot of meshes use the same shader, it would save time to simply have a shader node with a bunch of children nodes that utilized the same shader. That way the shader would only iterate through its passes once.

Quote:

I would have all my "models" retain any information involving textures, shaders, lighting, etc. I would then have a scene graph constructed of transform node, geometry nodes, and whatever other nodes that would be needed. When a geometry node is inserted into the scene graph, it gets associated with a model, the node doesn't care about anything specifics about the model, just that it is associated with that model. Now, as you travel down the tree, updating the transformations and so forth and you get to the model, you would then send that model and the current transformation to the renderer. The renderer would then set any shader constants (such as the current transformation matrix), textures, and any other data contained within the model. It would then set the active shader as the shader the model uses and render.

I think I'm going to take that route to help get this working. Thanks for the tip.

Share this post


Link to post
Share on other sites
I'm having some more trouble. How do you set up the renderer-scene graph relationship? How do I transfer the data from the scene graph to the renderer? I think that using a pointer to the renderer as a parameter to the scene graph's render function should work, but I didn't know if there was a better way. Also, how do you store the render queue in the renderer? A vector wouldn't work because the data is all different.

Share this post


Link to post
Share on other sites
Quote:
Original post by NickGravelyn
I'm having some more trouble. How do you set up the renderer-scene graph relationship? How do I transfer the data from the scene graph to the renderer? I think that using a pointer to the renderer as a parameter to the scene graph's render function should work, but I didn't know if there was a better way.


This is really just a matter of taste. I would do it the way you mentioned though, the scene graph would have a pointer to the renderer, and when you hit geometry nodes, you can send the data to the renderer.

Quote:
Also, how do you store the render queue in the renderer? A vector wouldn't work because the data is all different.


All geometry will be composed of triangles when it boils down to it, just have your model class allow the renderer access to the underlying vertex buffer / index buffer.

Share this post


Link to post
Share on other sites
How should I manage the transformation nodes? I thought about simply using the IDirect3DDevice9* functions GetTransform() and SetTransform() to manipulate the transformation (world transformation) that way when I reached the geometry node, it would use the GetTransform() to pull the current world matrix to send along with it for the shader. This would also be a nice way because then my camera can use those functions for the view matrix as well.

Share this post


Link to post
Share on other sites
Quote:
Original post by NickGravelyn
How should I manage the transformation nodes? I thought about simply using the IDirect3DDevice9* functions GetTransform() and SetTransform() to manipulate the transformation (world transformation) that way when I reached the geometry node, it would use the GetTransform() to pull the current world matrix to send along with it for the shader. This would also be a nice way because then my camera can use those functions for the view matrix as well.


Yes, use those functions for the transformation nodes.

You might also consider ID3DXMATRIXStack in D3DX to manage this, this class makes managing transformation nodes a bit like OpenGL's glPushMatrix() and glPopMatrix() which can simplify a lot. Using this, you load the first transformation matrix, you draw what you need using this matrix, you push the matrix you modify the loaded matrix by the next transformation matrix (with a * like you would do anyway).

Otherwise, unless you keep the final transform in the transformation node (which would almost void what a scene graph is for), you will need to keep track of each matrices encountered while you traverse the graph (like the stack some uses for tree traversal). So in the end you will do mostly the same as the ID3DXMATRIXStack would do, so since its already done.

The only problem I would see with using it would be for porting to a platform that doesn't have any equivalent (but close to 100% of the other platforms will use OpenGL which as similar mechanism included directly in the API). If you don't want to use D3DX make yourself a quick matrix stack class to manage this for you :) since this approach is easy to implement and simple to use.

JFF

Share this post


Link to post
Share on other sites
Quote:
Original post by JohnnyCasil
Quote:
Original post by NickGravelyn
I'm having some more trouble. How do you set up the renderer-scene graph relationship? How do I transfer the data from the scene graph to the renderer? I think that using a pointer to the renderer as a parameter to the scene graph's render function should work, but I didn't know if there was a better way.


This is really just a matter of taste. I would do it the way you mentioned though, the scene graph would have a pointer to the renderer, and when you hit geometry nodes, you can send the data to the renderer.

Quote:
Also, how do you store the render queue in the renderer? A vector wouldn't work because the data is all different.


All geometry will be composed of triangles when it boils down to it, just have your model class allow the renderer access to the underlying vertex buffer / index buffer.


Another way is to model the Renderer as a singleton class, this way you avoid the pointer to the Renderer in the scene graph traversal methods. This is a good way to make the relationship beetween scene nodes and renderer less intrusive from a design point of view :), don't you ?



Share this post


Link to post
Share on other sites
Quote:
Original post by elevator2
Another way is to model the Renderer as a singleton class, this way you avoid the pointer to the Renderer in the scene graph traversal methods. This is a good way to make the relationship beetween scene nodes and renderer less intrusive from a design point of view :), don't you ?


Watch the word "singleton" draw negative opinions like a light draws moths :D
I don't see the benefit of a pointer to a global variable over a pointer to the renderer, and exposing the renderer to every other system in your program may not be a good idea. Limiting yourself to one renderer when you don't have to, may not be good either, but this last one is somewhat of a theoretical point.

Another possible system: I use a number of visitors, though the system is still in the early stages. They go down the scene graph and perform various tasks, like switching a node's rendering off if it's not supposed to be visible. I then add the nodes that are processed to a list which gets passed on to the renderer. A visitor is used to build this list also.

For an enlightening "article" about a rendering pipeline, check, among other things, Ysaneya's journal.

Share this post


Link to post
Share on other sites
Quote:
Original post by lightbringer
Quote:
Original post by elevator2
Another way is to model the Renderer as a singleton class, this way you avoid the pointer to the Renderer in the scene graph traversal methods. This is a good way to make the relationship beetween scene nodes and renderer less intrusive from a design point of view :), don't you ?


Watch the word "singleton" draw negative opinions like a light draws moths :D
I don't see the benefit of a pointer to a global variable over a pointer to the renderer, and exposing the renderer to every other system in your program may not be a good idea. Limiting yourself to one renderer when you don't have to, may not be good either, but this last one is somewhat of a theoretical point.

Another possible system: I use a number of visitors, though the system is still in the early stages. They go down the scene graph and perform various tasks, like switching a node's rendering off if it's not supposed to be visible. I then add the nodes that are processed to a list which gets passed on to the renderer. A visitor is used to build this list also.


Yes It's a matter of points of view where use a pointer or a singleton class for the renderer (e.g. the engine architecture)! I personally used both the methods in different demos and the difference is only a matter of mental stress :) :D!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this