mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-17 12:12:10 -04:00
render order documents
This commit is contained in:
parent
184ed3e96f
commit
eead112d48
@ -1,168 +0,0 @@
|
||||
NOTE: As of April 2002, we have rewritten the primary scene graph
|
||||
interface to Panda, which invalidates almost all of the contents of
|
||||
this document. We hope to be providing an updated document soon. In
|
||||
the meantime, this document remains, and may be useful for historical
|
||||
purposes.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Panda has two primary modes of rendering: with or without a separate
|
||||
"Cull" traversal.
|
||||
|
||||
In the simplest case, Panda renders directly, without a separate Cull
|
||||
traversal. In this case, the scene graph is traversed with a simple
|
||||
depth-first, left-to-right in-order traversal, and GeomNodes are sent
|
||||
to the graphics engine as they are encountered.
|
||||
|
||||
When operating in this mode, the only way to control render order is
|
||||
to adjust the order of nodes within the hierarchy. It is possible to
|
||||
do this by either building the hierarchy in a specific order (each
|
||||
reparenting operation in the scene graph appends the new node to the
|
||||
end of its parent's children list), or more explicitly, by setting a
|
||||
sort order on each arc as it is created or moved (the NodePath
|
||||
reparenting methods support an optional sort parameter, and the
|
||||
NodeRelation class has a set_sort() method). Normally the sort order
|
||||
on each arc is zero, but it may be explicitly set to any integer. A
|
||||
node's list of children will always be kept in order from lowest to
|
||||
highest sort order, and where siblings have an equal sort order, they
|
||||
will be arranged in the order in which they were added.
|
||||
|
||||
More commonly, Panda is operated using a Cull traversal. This
|
||||
traversal makes a complete pass through the scene graph before
|
||||
rendering anything, collecting together all the GeomNodes that are to
|
||||
be rendered and arranging them in a suitable order before passing them
|
||||
to the graphics engine. It is somewhat inappropriately named;
|
||||
although it does do view-frustum culling, so does the simpler direct
|
||||
traversal; it should more properly be called the State Sorting
|
||||
traversal.
|
||||
|
||||
When the Cull traversal is in use, the hierarchy order is irrelevant.
|
||||
Instead, the Cull traversal uses a binning system to support user
|
||||
control of the order in which things are rendered.
|
||||
|
||||
As the Cull traversal encounters GeomNodes, it assigns each one to a
|
||||
particular bin, identified by name. These bins are selected by
|
||||
setting a GeomBinTransition above the arc in question, or by calling
|
||||
NodePath::set_bin().
|
||||
|
||||
After all the GeomNodes have been identified, the various bins are
|
||||
sorted in order according to each bin's sort index, which is specified
|
||||
by GeomBin::set_sort(). This is an arbitary integer assigned to each
|
||||
bin, and the lower-number bins are drawn first. Each bin is then
|
||||
responsible for drawing its contents--the set of GeomNodes assigned to
|
||||
it--in whatever order it likes. The various kinds of bins render
|
||||
their GeomNodes in different ways:
|
||||
|
||||
GeomBinStateSorted -- collects together all GeomNodes that share a
|
||||
common state and renders them at once, before switching to the
|
||||
next group of GeomNode with a common state. Attempts to minimize
|
||||
the state changes between groups of GeomNodes. The goal of this
|
||||
bin is to minimize the number of state changes sent to the
|
||||
graphics engine, and so reduce rendering overhead.
|
||||
|
||||
GeomBinBackToFront -- renders everything in order from the furthest
|
||||
away to the closest. This is generally necessary for correct
|
||||
transparent and semitransparent rendering. The ordering is based
|
||||
on the center of each GeomNode's bounding volume, relative to the
|
||||
camera plane.
|
||||
|
||||
GeomBinNormal -- assigns each GeomNode to one of two sub-bins:
|
||||
transparent geometry is assigned to a GeomBinBackToFront, while
|
||||
opaque geometry is assigned to a GeomBinStateSorted. This is the
|
||||
kind of bin that 'default' is defined to be; it is the bin that
|
||||
all GeomNodes are assigned to when no other bin is explicitly
|
||||
specified.
|
||||
|
||||
GeomBinUnsorted -- renders everything in no particular order.
|
||||
|
||||
GeomBinFixed -- renders everything according to a user-specified
|
||||
order, potentially per GeomNode. Each GeomBinTransition that
|
||||
specifies a GeomBinFixed bin may also include an optional sort
|
||||
order (this is an optional second parameter to the
|
||||
GeomBinTransition constructor, as well as to NodePath::set_bin());
|
||||
the GeomBinFixed will render low-number nodes before high-number
|
||||
nodes.
|
||||
|
||||
If no bin is explicitly specified, each GeomNode is assigned to a bin
|
||||
named 'default', which is of type GeomBinNormal; this bin is created
|
||||
at startup and contains two sub-bins, one for transparent geometry and
|
||||
one for nontransparent geometry. The nontransparent bin is rendered
|
||||
first, with its contents in state-sorted order, followed by the
|
||||
transparent bin, with its contents in order from back to front. This
|
||||
usually provides correct behavior for transparent and semitransparent
|
||||
objects, which must generally be rendered after everything behind them
|
||||
has already been rendered.
|
||||
|
||||
However, this sometimes fails, particularly with large, flat polygons
|
||||
stacked closely in front of one another. In cases like these it may
|
||||
be necessary to explicitly specify an ordering.
|
||||
|
||||
There is another predefined bin available called 'fixed'. Nothing
|
||||
will ever be rendered in 'fixed' (or any other bin, other than
|
||||
'default') unless it is explicitly assigned to it. The 'fixed' bin is
|
||||
of type GeomBinFixed, and renders its objects according to a fixed
|
||||
ordering, specified as the second parameter to the GeomBinTransition
|
||||
constructor, or to NodePath::set_bin(). There is also another
|
||||
predefined bin called 'background', which is another bin of type
|
||||
GeomBinFixed.
|
||||
|
||||
The order of all the predefined bins (and their predefined sort
|
||||
orders) is as follows:
|
||||
|
||||
10 - 'background' : GeomBinFixed
|
||||
20 - 'opaque' : GeomBinNormal (opaque sub-bin of 'default')
|
||||
30 - 'transparent' : GeomBinNormal (transparent sub-bin of 'default')
|
||||
40 - 'fixed' : GeomBinFixed
|
||||
50 - 'unsorted' : GeomBinUnsorted
|
||||
|
||||
Thus, the 'fixed' bin can be used for things that must be rendered
|
||||
correctly relative to each other, but should render after all other
|
||||
things in the scene graph, while the 'background' bin can be used for
|
||||
things that must render before other transparent things in the scene
|
||||
graph (it's particularly useful for large, flat polygons on the
|
||||
horizon).
|
||||
|
||||
Other bins may easily be defined, either at run time or via a line in
|
||||
a Configrc file. It is also possible to redefine any of the
|
||||
predefined bins by defining a new bin with the same name.
|
||||
|
||||
To define a bin via the Configrc file, add a line beginning with
|
||||
"cull-bin" and consisting of three space-separated fields: the name of
|
||||
the bin, the bin sort order, and the type of bin. For example, to
|
||||
create an bin called 'shadow' to render shadows in no particular
|
||||
order, but before any other transparent objects are rendered, you may
|
||||
add the line:
|
||||
|
||||
cull-bin shadow 25 unsorted
|
||||
|
||||
The valid bin types are normal, unsorted, state-sorted, fixed, or
|
||||
back-to-front.
|
||||
|
||||
To define a bin at run time, you simply create a bin of the
|
||||
appropriate type using its constructor, and then assign it to the
|
||||
current render traverser via GeomBin::set_traverser(). To do this,
|
||||
you must get a pointer to the current traverser via
|
||||
GraphicsStateGuardian::get_render_traverser(). This will either be a
|
||||
DirectRenderTraverser or a CullTraverser.
|
||||
|
||||
A GeomBin may only be assigned to a CullTraverser. If the current
|
||||
render traverser is not a CullTraverser, then Panda is operating
|
||||
without a Cull traversal, and you cannot meaningfully assign things to
|
||||
GeomBins anyway.
|
||||
|
||||
For example, the following Python code creates the same GeomBin
|
||||
defined above:
|
||||
|
||||
shadowBin = GeomBinUnsorted('shadow')
|
||||
shadowBin.setSort(15)
|
||||
try:
|
||||
shadowBin.setTraverser(win.getGsg().getRenderTraverser())
|
||||
except:
|
||||
pass
|
||||
|
||||
The try .. except block is a good idea to protect against the case in
|
||||
which getRenderTraverser() does not return a CullTraverser.
|
||||
|
||||
|
126
panda/src/doc/howto.control_render_order.txt
Executable file
126
panda/src/doc/howto.control_render_order.txt
Executable file
@ -0,0 +1,126 @@
|
||||
HOW TO CONTROL RENDER ORDER
|
||||
|
||||
In most simple scenes, you can naively attach geometry to the scene
|
||||
graph and let Panda decide the order in which objects should be
|
||||
rendered. Generally, it will do a good enough job, but there are
|
||||
occasions in which it is necessary to step in and take control of the
|
||||
process.
|
||||
|
||||
To do this well, you need to understand the implications of render
|
||||
order. In a typical OpenGL- or DirectX-style Z-buffered system, the
|
||||
order in which primitives are sent to the graphics hardware is
|
||||
theoretically unimportant, but in practice there are many important
|
||||
reasons for rendering one object before another.
|
||||
|
||||
Firstly, state sorting is one important optimization. This means
|
||||
choosing to render things that have similar state (texture, color,
|
||||
etc.) all at the same time, to minimize the number of times the
|
||||
graphics hardware has to be told to change state in a particular
|
||||
frame. This sort of optimization is particularly important for very
|
||||
high-end graphics hardware, which achieves its advertised theoretical
|
||||
polygon throughput only in the absence of any state changes; for many
|
||||
such advanced cards, each state change request will completely flush
|
||||
the register cache and force a restart of the pipeline.
|
||||
|
||||
Secondly, some hardware has a different optimization requirement, and
|
||||
may benefit from drawing nearer things before farther things, so that
|
||||
the Z-buffer algorithm can effectively short-circuit some of the
|
||||
advanced shading features in the graphics card for pixels that would
|
||||
be obscured anyway. This sort of hardware will draw things fastest
|
||||
when the scene is sorted in order from the nearest object to the
|
||||
farthest object, or "front-to-back" ordering.
|
||||
|
||||
Finally, regardless of the rendering optimizations described above, a
|
||||
particular sorting order is required to render transparency properly
|
||||
(in the absence of specialized hardware support that few graphics
|
||||
cards provide). Transparent and semitransparent objects are normally
|
||||
rendered by blending their semitransparent parts with what has already
|
||||
been drawn to the framebuffer, which means that it is important that
|
||||
everything that will appear behind a semitransparent object must have
|
||||
already been drawn before the object itself is drawn. This implies
|
||||
that all semitransparent objects must be drawn in order from farthest
|
||||
away to nearest, or in "back-to-front" ordering, and furthermore that
|
||||
the opaque objects should all be drawn before any of the
|
||||
semitransparent objects.
|
||||
|
||||
Panda achieves these sometimes conflicting sorting requirements
|
||||
through the use of bins.
|
||||
|
||||
CULL BINS
|
||||
|
||||
The CullBinManager is a global object that maintains a list of all of
|
||||
the cull bins in the world, and their properties. Initially, there
|
||||
are five default bins, and they will be rendered in the following
|
||||
order:
|
||||
|
||||
Bin Name Sort Type
|
||||
-------------- ---- ----------------
|
||||
"background" 10 BT_fixed
|
||||
"opaque" 20 BT_state_sorted
|
||||
"transparent" 30 BT_back_to_front
|
||||
"fixed" 40 BT_fixed
|
||||
"unsorted" 50 BT_unsorted
|
||||
|
||||
When Panda traverses the scene graph each frame for rendering, it
|
||||
assigns each Geom it encounters into one of the bins defined in the
|
||||
CullBinManager. (The above lists only the default bins. Additional
|
||||
bins may be created as needed, using either the
|
||||
CullBinManager::add_bin() method, or the Config.prc "cull-bin"
|
||||
variable.)
|
||||
|
||||
You may assign a node or nodes to an explicit bin using the
|
||||
NodePath::set_bin() interface. set_bin() requires two parameters, the
|
||||
bin name and an integer sort parameter; the sort parameter is only
|
||||
meaningful if the bin type is BT_fixed (more on this below), but it
|
||||
must always be specified regardless.
|
||||
|
||||
If a node is not explicitly assigned to a particular bin, then Panda
|
||||
will assign it into either the "opaque" or the "transparent" bin,
|
||||
according to whether it has transparency enabled or not. (Note that
|
||||
the reverse is not true: explicitly assigning an object into the
|
||||
"transparent" bin does not automatically enable transparency for the
|
||||
object.)
|
||||
|
||||
When the entire scene has been traversed and all objects have been
|
||||
assigned to bins, then the bins are rendered in order according to
|
||||
their sort parameter. Within each bin, the contents are sorted
|
||||
according to the bin type.
|
||||
|
||||
The following bin types may be specified:
|
||||
|
||||
BT_fixed
|
||||
|
||||
Render all of the objects in the bin in a fixed order specified by
|
||||
the user. This is according to the second parameter of the
|
||||
NodePath::set_bin() method; objects with a lower value are drawn
|
||||
first.
|
||||
|
||||
BT_state_sorted
|
||||
|
||||
Collects together objects that share similar state and renders
|
||||
them together, in an attempt to minimize state transitions in the
|
||||
scene. Note: at the moment, this mode is not actually implemented
|
||||
in Panda, and defaults to the same behavior as BT_unsorted. This
|
||||
does limit the performance of Panda for extremely complex scenes
|
||||
on very high-end graphics cards, but has little impact on most
|
||||
consumer-level cards.
|
||||
|
||||
BT_back_to_front
|
||||
|
||||
Sorts each Geom according to the center of its bounding volume, in
|
||||
linear distance from the camera plane, so that farther objects are
|
||||
drawn first. That is, in Panda's default right-handed Z-up
|
||||
coordinate system, objects with large positive Y are drawn before
|
||||
objects with smaller positive Y.
|
||||
|
||||
BT_front_to_back
|
||||
|
||||
The reverse of back_to_front, this sorts so that nearer objects
|
||||
are drawn first.
|
||||
|
||||
BT_unsorted
|
||||
|
||||
Objects are drawn in the order in which they appear in the scene
|
||||
graph, in a depth-first traversal from top to bottom and then from
|
||||
left to right.
|
||||
|
83
panda/src/doc/howto.fix_transparency_issues.txt
Executable file
83
panda/src/doc/howto.fix_transparency_issues.txt
Executable file
@ -0,0 +1,83 @@
|
||||
HOW TO FIX TRANSPARENCY ISSUES
|
||||
|
||||
Usually transparency works as expected in Panda automatically, but
|
||||
sometimes it just seems to go awry, where a semitransparent object in
|
||||
the foreground seems to partially obscure a semitransparent object
|
||||
behind it. This is especially likely to happen with large flat
|
||||
polygon cutouts, or when a transparent object is contained within
|
||||
another transparent object, or when parts of a transparent object can
|
||||
be seen behind other parts of the same object.
|
||||
|
||||
The fundamental problem is that correct transparency, in the absence
|
||||
of special hardware support involving extra framebuffer bits, requires
|
||||
drawing everything in order from farthest away to nearest. This means
|
||||
sorting each polygon--actually, each pixel, for true correctness--into
|
||||
back-to-front order before drawing the scene.
|
||||
|
||||
It is, of course, too expensive to split up every transparent object
|
||||
into individual pixels or polygons for sorting individually, so Panda
|
||||
sorts objects at the Geom level, according to the center of the
|
||||
bounding volume. This works well 95% of the time.
|
||||
|
||||
You run into problems with large flat polygons, though, since these
|
||||
tend to have parts that are far away from the center of their bounding
|
||||
volume. The bounding-volume sorting is especially likely to go awry
|
||||
when you have two or more large flats close behind the other, and you
|
||||
view them from slightly off-axis. (Try drawing a picture, of the two
|
||||
flats as seen from the top, and imagine yourself viewing them from
|
||||
different directions. Also imagine where the center of the bounding
|
||||
volumes is.)
|
||||
|
||||
Now, there are a number of solutions to this sort of problem. No one
|
||||
solution is right for every situation.
|
||||
|
||||
First, the easiest thing to do is to use M_dual transparency. This is
|
||||
a special transparency mode in which the completely invisible parts of
|
||||
the object aren't drawn into the Z-buffer at all, so that they don't
|
||||
have any chance of obscuring things behind them. This only works well
|
||||
if the flats are typical cutouts, where there is a big solid part
|
||||
(alpha == 1.0) and a big transparent part (alpha == 0.0), and not a
|
||||
lot of semitransparent parts (0.0 < alpha < 1.0). It is also a
|
||||
slightly more expensive rendering mode than the default of M_alpha, so
|
||||
it's not enabled by default in Panda. But egg-palettize will turn it
|
||||
on automatically for a particular model if it detects textures that
|
||||
appear to be cutouts of the appropriate nature, which is another
|
||||
reason to use egg-palettize if you are not already.
|
||||
|
||||
Second, an easy thing to do is to chop up one or both competing models
|
||||
into smaller pieces, each of which can be sorted independently by
|
||||
Panda. For instance, you can split one big polygon into a grid of
|
||||
little polygons, and the sorting is more likely to be accurate for
|
||||
each piece (because the center of the bounding volume is closer to the
|
||||
pixels). You can draw a picture to see how this works. In order to
|
||||
do this properly, you can't just make it one big mesh of small
|
||||
polygons, since Panda will make a mesh into a single Geom of
|
||||
tristrips; instead, it needs to be separate meshes, so that each one
|
||||
will become its own Geom. Obviously, this is slightly more expensive
|
||||
too, since you are introducing additional vertices and adding more
|
||||
objects to the sort list; so you don't want to go too crazy with the
|
||||
smallness of your polygons.
|
||||
|
||||
A third option is simply to disable the depth write on your
|
||||
transparent objects. This is most effective when you are trying to
|
||||
represent something that is barely visible, like glass or a soap
|
||||
bubble. Doing this doesn't improve the likelihood of correct sorting,
|
||||
but it will tend to make the artifacts of an incorrect sorting less
|
||||
obvious. You can achieve this by using the transparency option
|
||||
"blend_no_occlude" in an egg file, or by explicitly disabling the
|
||||
depth write on a loaded model with node_path.set_depth_write(false).
|
||||
You should be careful only to disable depth write on the transparent
|
||||
pieces, and not on the opaque parts.
|
||||
|
||||
A final option is to make explicit sorting requests to Panda. This is
|
||||
often the last resort because it is more difficult, but it does have
|
||||
the advantage of not adding additional performance penalties to your
|
||||
scene. It only works well when the transparent objects can be sorted
|
||||
reliably with respect to everything else behind them. For instance,
|
||||
clouds in the sky can reliably be drawn before almost everything else
|
||||
in the scene, except the sky itself. Similarly, a big flat that is up
|
||||
against an opaque wall can reliably be drawn after all of the opaque
|
||||
objects, but before any other transparent object, regardless of where
|
||||
the camera happens to be placed in the scene. See
|
||||
howto.control_render_order.txt for more information about explicitly
|
||||
controlling the rendering order.
|
Loading…
x
Reference in New Issue
Block a user