mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-18 20:53:50 -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