How Visual Rendering is Done
Matthew Leditschke
(Matthew.Leditschke@cselt.it)
Aim
To draw the objects which make up the scene, in back to front order,
performing the mimimal amount of drawing.
Proxy nodes
Before describing the scene traversal and rendering process in detail,
it is important to say a few words about how the scene tree is stored.
The scene tree, as constructed by the BifsDecoder, consists of a tree
of objects which derive from the MediaObject class. The Render() method
of these objects is empty.
Attached to every object in this tree is a proxy object, the description
of which is provided below. It is these proxy objects which contain the
necessary data and methods to perform the necessary traversals of the
scene which are required to render the scene.
The different types of proxy objects are defined using a class hierarchy.
The presenter controls the drawing of the scene
The main loop of the presenter (contained in the
PresenterCSELT::Main()
method) continuously draws the
scene at the specified frame rate. Each time through this loop, the
presenter does the following steps:
- Process events which have been received from the Executive (via
the
OnUserEvent()
method. Processing these events may
cause the fields of some nodes to be changed as a result of routes being
triggered.
- Update the time sensor nodes which are present in the scene.
This is done by traversing the tree, calling the
UpdateTimeSensor()
method of the root proxy object of
the scene. When a time sensor is updated, routes may be triggered
which affect the fields of other nodes.
At this point all routes have been triggered which result from user
interaction and time based events, and so the rendering of the scene
at this point in time can commence.
- The presenter now initiates the pre-render traversal of the
scene tree, by calling the
PreRender()
method of the
root proxy object. This traversal enables all objects to determine
where they will be displayed (cumulation of all of the Transform2D
nodes), and what there drawing order is (also given by the
Transform2D node).
During this traversal, nodes which need to be drawn (referred to as
drawable nodes) register
themselves with the VisualRenderer, using the
AddToDrawList()
method. Even if a node has not changed
since, it must still register, as the VisualRenderer will decide
what needs to be redrawn.
- The visual renderer is now told to render the nodes which have
registered themselves, in depth order. This is discussed in more
details below.
- A traversal of the scene tree is performed to determine if all
of the nodes have finished being rendered. This is done by calling
the
IsFinished()
method on the root proxy object. Once
this returns true, the presenter main loop terminates.
- At this point all of the rendering for this point in time has
finished. The presenter now determines how long it has to wait
until the next time the scene needs to be drawn, and sleeps for this
period of time.
How the visual renderer coordinates the drawing of the nodes
When instructed by the presenter, the visual renderer sorts
the list of drawable nodes based on their draw order, and then draws
the nodes in back-to-front order.
Rather than just drawing all nodes in the list, the visual renderer
determines the minimum amount of drawing which needs to be performed,
based on which nodes have changed, which ones are transparent, and
their bounding boxes. This is done as follows:
- Start with an empty rectangle which indicates what part of the
final output image has changed, and a second empty rectangle which
stores what part of the final output image is made up of
objects which are partly see-through.
- Go through the list of nodes, from front to back, doing the following:
- If this node has changed and hence needs to be redrawn, then add
its bounding rectangle to the overall changed area bounding rectangle.
- If this node is transparent (which means that it does not
completely fill its bounding rectangle), then add its bounding
rectangle to the overall transparent area bounding rectangle.
- Before drawing the nodes, some of the output frame needs to be set
to the black background before drawing. This area is indicated by
the overall transparent area bounding rectangle.
- Draw all of the node, in back-to-front order, only drawing the
parts of the objects which lie within the overall changed area
bounding rectangle.
Nodes which have not changed are only drawn if they intersect with
the transparent area, or if something behind them has been redrawn.
Using DrawableNode::Draw() and the visual renderer
drawing functions
The visual renderer has been designed in a manner which will support
the implementation of its functionality on a number of different
graphics systems.
When adding new nodes, some of the implementation of the drawing of
the node will reside in the Draw() method of the proxy object
for the node, and some will be in a node specific method in the visual
renderer. The aim is to include as much code as possible in the proxy
object, as this is the same for all graphics systems. Only those
things which are graphics system specific should be placed into the
visual renderer's node specific display methods.
More details
More details on the methods use to traverse the scene tree can be
found in the description of the following classes which build up the
hierarchy of proxy object classes: