As a learning exercise I've written a deferred rendering engine. Now I'd like to add a scene graph to this engine but I'm a bit puzzled how to do this.
On a normal (forward rendering engine) I would just add all items (All implementing IDrawable and IUpdateAble) to my scene graph, than travel the scene-graph breadth first and call Draw() everywhere.
However in a deferred rendering engine I have to separate draw calls. First I have to draw the geometry, then the shadow casters and then the lights (all to different render targets), before I combine them all. So in this case I can't just travel over the scene graph and just call draw. The way I see it I either have to travel over the entire scene graph 3 times, checking what kind of object it is that has to be drawn, or I have to create 3 separate scene graphs that are somehow connected to each other. Both of these seem poor solutions, I'd like to handle scene objects more transparent.
One other solution I've thought of was traveling trough the scene graph as normal and adding items to 3 separate lists, separating geometry, shadow casters and lights, and then iterating these lists to draw the correct stuff, is this better, and is it wise to repopulate 3 lists every frame?
Answer
An approach I've used in a C++ project is that the scene-graph (which has the spatial index) fills up a 'visible' std::vector of hits based on the current viewing frustum. This visible list is managed by the scene-graph so is only recalculated when the camera moves - moving objects in the graph are moved in this list and using tombstones and unsorted change-lists that get sorted and merged back in as-needed.
The list of visible items is sorted by shader-ID first and within each type by distance from camera. The shader IDs are assigned such that terrain sorts first and then buildings and then units and then projectiles and then particles and so on - it being an RTS. Some models have more than one shader, but they only advertise their primary shader. When they are asked to draw, those which need bits drawn with another shader too add themselves to a next-pass single linked list.
So drawing goes through the visible array in one pass, and in that one pass a linked list of those items to revisit is created, and they get drawn a second pass and so on.
Drawing front-to-back and opaque-then-transparent helps keep everything sane.
This is perhaps not minimising the number of shader changes etc but it is fairly workable and straightforward to implement.
I have no idea about XNA and how applicable this is and how much of this low-level stuff you create I'm afraid. It would be most interesting to know what the veterans think of this approach for C++ RTSes however.
No comments:
Post a Comment