Scene graph design problems (rendering + frustum culling)

Started by
15 comments, last by GhostAce 18 years, 5 months ago
Hey, I've got my scene graph working quite nicely at the moment, with support for the visitor pattern and stuff making it quite extendable. I now want to make it more extendable and object orientated. My idea is to have "pipelines" that manipulate the scene graph somehow. For example... Frustum cull -> depth sort -> reflection render -> refraction render -> render to display. Things such as the reflection render and the refraction render are easy to create, because they don't manipulate the scene graph at all, they just perform an operation on it (in this case, rendering). But how does the frustum cull pipeline state tell the depth sort renderer, what to depth sort?! For example, a node is not visible - so do I flag the node as node visible (ie thisNode->visible = false), or do I create a scene graph at runtime and graduly remove needs from this scene graph. The latter sounds much better, because creating new pipeline stages does not require me to touch any existing node types - but it does double the memory of the entire scene graph. What are your thoughts?
Ollie"It is better to ask some of the questions than to know all the answers." ~ James Thurber[ mdxinfo | An iridescent tentacle | Game design patterns ]
Advertisement
It seems to me that the results of the frustum cull should be an intrinsic property of the resulting scene graph structure, not something individual nodes in the graph should have to worry about. As I was doing the frustum culling, I would make a copy of the parts of the scene graph that aren't culled. That way you aren't making a full copy of the tree and just pruning the culled parts, and you don't have to add a special variable just for visibility.

Also, take the case where the scene is rendered from multiple views. It wouldn't seem practical to have a visibility variable for every possible view.

[Edited by - Zipster on November 3, 2005 3:10:01 PM]
So you mean, for each render - I should create a new scene graph? So, the frustum culler would create the scene graph, and pass it on to something like a depth sorter?
Ollie"It is better to ask some of the questions than to know all the answers." ~ James Thurber[ mdxinfo | An iridescent tentacle | Game design patterns ]
The idea that I'm tossing around at the moment for my scene graph system is this:

I have a single iterator traversing the graph, mutliple visitors on the iterator, each visitor can maintain whatever kind of information it needs (stack, state objects, whatever). I'm also doing "weird" things like including user interface information and sound effect information in the scene graph, so when I hit a bounding box node with the GraphicsVisitor, I disable the graphics visitor, but push the bounding box node onto a stack so that I can enable the visitor on the "leaving" visit. I must continue to traverse the graph in case a sound effect was placed within the bounding box which might end up behind the viewpoint but still audible (a strange case which I might determine to be impossible, and decide later to skip child traversal).

Right now, I render objects during traversal (which I realize doesn't work with depth-sensetive stuff), but I am probably going to end up spitting out some kind of per-frame-scratch render command lists which I can depthsort (for the lists that contain depth-sensetive objects) and optimize for render-state-changes.


So you might use a similar approach - use the scene graph for persistent data storage and some throw-away lists of render actions generated from the traversal. I'm not sure what the advantages or disadvantages of having two scene graphs would be...


Remember, just an idea - I haven't implemented more than a rudimentary object oriented scene graph system so far.
Hmm I don't really understand what your problem is but here is what I would do (WILL do, when I have the time to work on that project ^^)

// this function recursively walk through the SG. We assume a previous// pass updated the BBs, and culling information// The frusum culling takes place here. If a node isn't visible,// we discard the whole sub-tree. We could also add a boolean to avoid testing// the visibiliy : if a BB is entirely inside the frustum, all the children are// inside the frustum, we don't need to test it.void OnRenderWalk(CNode *node){   if (CFrustumCulling::Cull(node) == INSIDE)   {      for (int i = 0; i < node->_nbChildren; i++)         OnRenderWalk(node->_child);      // in this function, the node send its data to a renderer.      // by "data", I mean all the informations that the renderer will need      // to render the node (buffers, shader, textures, render states, etc.)      node->OnRender();   }}



the important thing is that this pass don't touch the structure of the SG. It does not modify it, does not create redundant information. Each visible node only send pointers to its data to the renderer. And THEN, the renderer only has the visible entities, and can do its job : sorting by depth, shader, textures, or whatever criteria you want.
Quote:Original post by paic
Hmm I don't really understand what your problem is but here is what I would do (WILL do, when I have the time to work on that project ^^)

*** Source Snippet Removed ***


the important thing is that this pass don't touch the structure of the SG. It does not modify it, does not create redundant information. Each visible node only send pointers to its data to the renderer. And THEN, the renderer only has the visible entities, and can do its job : sorting by depth, shader, textures, or whatever criteria you want.


This is how I initially implemented my pipeline as well: passing on pointers to visible data to the next stage in the pipeline. Unfortunately, although elegant this approach has one big problem that also applies to the pipeline acid2 described. It effectively removes all culled data from the pipeline, which means that subsequent stages cannot access those anymore. Unless you have an incredibly fancy culler, the reflection render will not be able to access any parts of reflected scene that are not within the view frustum.

I personally went for the thisNode->visible = false approach (actually I have two flags visible and culled, to separate culling within the pipeline from user defined culling). It's ugly, but very simple to implement. It is true that this approach modifies data within the scene graph, which is undesirable. More advanced solutions that don't modify scene graph data are possible, but they require a separate mechanism for pipeline stages to communicate...

Tom
Hi,

Who said you need to only fill your renderer once per frame ? ^^
You can re-fill your renderer many times per frame. For example, take the shadow mapping. You would create a pass where the camera is at the light place, render your scene to a texture. Then, you would have a second pass (the renderer is cleared after the first pass) and you would re-render from the user's point of view.

For each pass, their is a visibility pass which re-fill the renderer. This way, you take advantage of your culling algorithms in any situation.

You could also have the possibility to pass everything to your renderer plus a boolean "bVisible". Then the renderer would consider only the visible entities, but still have all the infos about invisible entities. But I don't recommend that.
Quote:Original post by paic
Hi,

Who said you need to only fill your renderer once per frame ? ^^
You can re-fill your renderer many times per frame. For example, take the shadow mapping. You would create a pass where the camera is at the light place, render your scene to a texture. Then, you would have a second pass (the renderer is cleared after the first pass) and you would re-render from the user's point of view.

For each pass, their is a visibility pass which re-fill the renderer. This way, you take advantage of your culling algorithms in any situation.

You could also have the possibility to pass everything to your renderer plus a boolean "bVisible". Then the renderer would consider only the visible entities, but still have all the infos about invisible entities. But I don't recommend that.


Ok, that makes sense. I guess what you're saying is that in the acid2 pipeline the reflection render is misplaced:
frustum cull -> depth sort -> reflection render -> refraction render -> render to display

And that it should have its own pipeline, making two separate pipelines (to be executed in this order):
reflection frustum cull -> depth sort -> reflection render
camera frustum cull -> depth sort -> refraction render -> render to display

Tom
Yup, exactly. Sorry if I wasn't clear ^^
Separate pipelines seem like a smart way to do it. And as dimebolt hinted at, you could possibly include an initial culling stage for user-controlled visibility.

This topic is closed to new replies.

Advertisement