Scene Graph Rendering - Centralized, Decentralized?

Started by
14 comments, last by dog4 18 years ago
Okay, so we have lots of little nodelets, lol, I like that word, which ultimately call something's draw function. Is this draw function specific to the object we're working with? Do we send this object to a centralized renderer? How can you centralize a renderer without type switching? How can you decentralize a renderer and still have global effects like pixel shaders and global illumination? Pro's and cons of both? What would you suggest? What should I do?
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
Advertisement
No replies?

Personally, I treat nodes as data. In their draw() they take their specific data [model, texture, whatever] and push it to a central renderer which does the ordering/batching/rendering. Granted, my setup is pretty simple, and doesn't need stuff that requires that 'big picture' look at things.

I'm sure others have better advice, but maybe this can help get the ideas going until they offer it...
I was thinking about having all the nodes point to the data structures. All the nodes would contain a pointer to the object to draw and a transformation (and maybe more). That way I don't have to copy the data for each instance.

I'm still not sure what would be best... the data to have a pointer to the renderer and draw itself or send the object to the renderer. The most logical way seems to be the object that draws itself sing the renderer... but it adds a dependency in the architecture.



JFF
Quote:
Is this draw function specific to the object we're working with?


To the class, not the object instance itself. Presumably the draw method is a virtual function of the base node class, and subclass nodes override it as needed, though this is certainly not the only way to do it.

Quote:
Do we send this object to a centralized renderer?


Yes or no, whichever you prefer. I tend to prefer yes, for the purposes of abstraction.

Quote:
How can you centralize a renderer without type switching?


By properly employing polymorphic techniques provided by the language, such as virtuality of methods. Also, there are ultimately very few things a renderer actually needs to render (usually 2D geometry, 3D geometry, particles or other effects, et cetera). The renderer doesn't need to know that rendering a "missle" is different from rendering a "player" (indeed, it probably isn't different at all).

Quote:
How can you decentralize a renderer and still have global effects like pixel shaders and global illumination?


Rather easily. Shaders and illumination have nothing to do, really, with the "centralization" or lack thereof of a renderer. You don't render shaders or lighting, both are used to alter the appearance of what you are rendering. They are render state, much like textures, that belong with the geometry or scene graph node (whatever) and are set and cleared by the renderer as needed (prior to rendering a given chunk of geometry, usually).

Quote:
What would you suggest? What should I do?


As I mentioned in another thread of yours, experimentation is one of the best ways to learn. Pick a particular method and try it. See how it feels, see how it runs. Do not be afraid to refactor your code. I guarantee you will not write it "right" the first time anyway.

Google, or books, can help answer more specific questions about what particular techniques are "generally fast" and what their various advantages and disadvantages are. The forums or the #gamedev IRC channel are also a good resource, but you'll need to be a bit more specific than you have in this particular post. As it stands your question is far to general for me to answer, even if I did feel like typing that much.
Well, right now I have a system where each object I draw, simply draws itself, we kind of do some fake polymorphism techniques by generalizing a Draw() function in every renderable object, and then by using the pointer we used to create the geometry node, whatever it was, we call Draw()... Now I know this isn't a very elegant approach, and I like the idea of a centralized renderer where we send these geometry objects (or pieces of data).

The polymorphism involved in creating a centralized singleton renderer without type switching is what is really baffling me.

Do we need some kind of other data tree with virtualized render functions to keep track of the rendering methods for various objects?

Say, class renderableObj

Under that we might have class, ms3dmode, which is a public renderable object..

ETC ETC, until we get the level of polymorphism our engine needs.



For instance, we could have a Base scene graph node Geometry..

Geometry would have an update function that either A, virtualizes an empty rendering function (decentralized), or B, virtualizes a function that sends data to the renderer...(centralized)

Under geometry, we could have MS3DModel, which is a public Geometry..

This guy needs to override the virtual function of geometry, and draw itself in it's own fashion (decentralized), or override the functiont that sends data to the renderer, (what data? object info, what else, how does the renderer draw this thing properly) (Centralized)

Can you understand what I'm having trouble dealing with?

How does an abstract hierarchy of renderable objects translate into what rendering function gets called in the renderer?

[Edited by - Shamino on March 16, 2006 12:17:07 AM]
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
A problem which sooner or later arises is keeping state switches and batches to a minimum. For example you got 100 objects of which each one uses one of 3 different textures. If each object takes care of rendering itself, they'll probably switch textures far too often.

In this case, you'd want to render all objects using texture1 first, then those with texture2 and so on. By using a centralized renderer or renderqueue you can sort the objects by their properties and use as few batches as possible. Basically each worldbobject would have a virtual method like SendToQueue() which says: "Render me at this position using the tree texture, that dog mesh, some cool parallax shader! I don't care how u do it, just do it and i'm happy!" and the centralized renderer will take care of sorting and rendering.

Another possible way would be having a tree structure in parallel to the scene-tree where each worldobject is put in according to its visual appearance. But i'm not used to that.

Take my ramblings with a grain of salt. [smile] Oh, and i just assumed that you're using d3d.
----------------------#include "signature.h"
Quote:Original post by Shamino
How does an abstract hierarchy of renderable objects translate into what rendering function gets called in the renderer?

The central renderer may provide some few routines for rendering general geometry given in the one or other form. The Renderable::render() method is virtual polymorph and invokes the one Renderer::renderXY(...) method that is suitable for it effective type (say how its geometry is given). (Perhaps there are more than one method of the Renderer to invoke, but that doesn't play a role for the understanding.)

So you have both a central Renderer but also a decentral type dispatching. That doesn't contradict itself.
I think you're getting caught up in terminology and its confusing you.

Your MS3D model class shouldn't override its base (Geometry) class's Draw method, because drawing an MS3D model is not fundamentally different from drawing any other kind of model or geometric objects -- the Geometry base class should contain some vertex or index buffers and those should be submitted to the renderer, along with any state information carried around by the geometry.

I don't understand what you mean by "fake polymorphism" in your scene graph.
Can show provide code?

Quote:
Oh, and i just assumed that you're using d3d.

IIRC he's using OpenGL but nothing in your post is really D3D-specific. *shrug*

Quote:
centralized singleton renderer without type switching is what is really baffling me.


Naughty! The render does not need to be, and should not be, a singleton.
Also, beware: You don't want to perform any type switching manually at all, ever. There are not many cases where you need to; certainly not in a renderer.
What I mean my "manually" is using lots of dynamic_casts and/or writing code like:
switch(myObject->getType()){  case fooType: ... break;  case barType: ... break;}


I'm out of time, I'll continue my discussion after work.
Okay, so we don't want to do type switching, no cases, no state machine.

But we want something centralized, I'm having a hard time figuring out how this is possible..

I can understand the decentralized method perfectly.

But it seems to me like centralizing it requires some type of polymorphism I am not yet familiar with, or some type of case/switch/if/else pile of mess in the renderer...

The main reason I think of seperating my ms3d geometry, and making it a subclass to geometry, is because ms3d model files are indexed, and they don't repeat vertices or anything.. I'm not sure if this lends itself well to vertex buffer objects or vertex arrays..

I think I'm going to go with a decentralized method anyways, it seems overall less confusing.

A few questions about decentralization.

What should the base renderable object node look like? I heard something about a vertex array or something, what what huh? I really never got into opengl this seriously before lol.. I know how to glbegin and glend, but vertex arrays and vbo's are foreign to me.

What should I derive from the base class? Under the base renderable object node, what should be under that? Examples? Anything?

By fake polymorphism, I mean this...

	void Update()	{		if(geom)		{					geom->Draw();		}		else		{			// eep! all shared_ptr's deleted!			//			// Reload Geometry, or take other measures		}		CSceneNode::Update();	}


This is just assuming that geom has a draw method in it, whatever geom is, it doesn't have a common base class, it isn't overriding any virtual methods, etc etc.. It is kinda faking polymorphism. I also heard it being called parametric polymorphism.

But anyways, my main questions have to do with the setup of geometry nodes in a decentralized renderer, I think this is the way I want to do it.
----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
I'm totally supposed to be at work, but I wanted to drop in and mention that one of the benefits of centralizing the renderer (so that its the renderer that draws things versus the objects drawing themselves) it that it reduces the number of places you'll need to refactor if you change your rendering "style" as a whole (i.e., from glBeing/glEnd to using VBOs, which you MUST do eventually because glBegin/glEnd is too slow.

This topic is closed to new replies.

Advertisement