To be able to render with good performance and display effects like transparency correctly, Panda3D automatically sorts the scene geometry and puts it into "cull bins", so vertices that share the same texture, for example, are sent to the graphics card in one batch.
Panda3D allows you to change the rendering order manually, to achieve custom scene sorting, which is what you will learn in this recipe.
This recipe requires the base code created in Setting up the game structure found in Chapter 1, to which the following sample code will be added.
Let's get started with this recipe's tasks:
Application.py:
from direct.showbase.ShowBase import ShowBase from direct.actor.Actor import Actor class Application(ShowBase): def __init__(self): ShowBase.__init__(self) self.panda = Actor("panda", {"walk": "panda-walk"}) self.panda.reparentTo(render) self.panda.loop("walk") self.panda.setBin("fixed", 40, 0) self.teapot = loader.loadModel("teapot") self.teapot.reparentTo(render) self.teapot.setBin("fixed", 40, 1) self.teapot.setDepthTest(False) self.teapot.setDepthWrite(False) self.smiley = loader.loadModel("smiley") self.smiley.reparentTo(render) self.smiley.setPos(0, 50, 6) self.smiley.setScale(30) self.smiley.setBin("background", 10) self.smiley.setDepthTest(False) self.smiley.setDepthWrite(False) self.cam.setPos(0, -30, 6)
The quintessential parts of this recipe are the highlighted lines in the sample code. The setBin()
method adds the affected scene node to the specified cull bin. The panda and the teapot are added to the"fixed"
bin, which is rendered in the order given by the third parameter. To illustrate the results of manually ordering scene objects, we turned off depth writes and depth testing for the teapot. Normally, the teapot would appear between the panda's feet, but using the third parameter we force the panda to be drawn first, followed by the teapot. Because we do not use the depth buffer for rendering the teapot and we requested that drawing order, the teapot is drawn in front of the panda.
The same principle applies to the"background"
bin. With a priority value of 10 it is drawn before the"fixed"
bin, which causes the smiley to be overdrawn by the panda and the teapot.
At this point we can see the principle of multiple bins unravel itself. Panda3D's rendering subsystem always processes these bins from lowest to highest priority. Summing up, this creates the following render order for our sample code:
"background"
bin has a priority value of 10—the lowest in our scene. Therefore, the smiley model it contains is rendered first."fixed"
bin with a priority of 40. This bin allows us to manually control the render order of the contained models and actors based on another priority value. The scene object with the lowest priority value is rendered first. This means that the panda is rendered next."fixed"
bin and has a sub-priority value of 1. This is the highest value for all objects in the scene, causing it to be the last object to be rendered.The sample code only showed you a part of Panda3D's scene sorting features, so let's take a deeper look!
If we take a look at the BinType
enumeration found in Panda3D's API, we can see five different types of cull bins:
BTUnsorted:
An unsorted bin just sends geometry to the graphics card in the order it is encountered while traversing the scene graph.BTStateSorted:
A state sorted cull bin sorts geometry by material, texture, and shader, among others, to minimize the switching of render states to increase drawing performance.BTBackToFront:
This type of cull bin will cause the parts of a model that are the furthest away from the point of view to be drawn first. This is necessary for drawing semi-transparent models, for example: Because we need to properly blend the colors of the translucent parts of a model and the colors of the surfaces behind these see-through parts. For further information on this topic, read up on alpha blending.BTFrontToBack:
This is the reversal of BTBackToFront
. Geometry that is nearer to the camera is drawn first.BTFixed:
The order of rendering is completely user defined and needs to be specified as the third parameter of the setBin()
method. Objects with lower order values are drawn first.By default, Panda3D creates the following bins ready to be used by your code. Bins with a lower priority value are processed first.
Name |
Type |
Priority |
---|---|---|
background |
BTFixed |
10 |
opaque |
BTStateSorted |
20 |
transparent |
BTBackToFront |
30 |
fixed |
BTFixed |
40 |
unsorted |
BTUnsorted |
50 |
gui-popup |
BTUnsorted |
60 |
It's very easy to add new cull bins at runtime. Consider the following code snippet:
from panda3d.core import CullBinManager cbm = CullBinManager.getGlobalPtr() cbm.addBin("mybin", CullBinManager.BTFixed, 80)
All you need to do is import the CullBinManager
class, get the global singleton instance, and pass the new bin's name, type, and order value to the addBin()
method. The bin type is one out of the types presented previously. The order value can be any positive or negative integer, but should not interfere with the priorities of the default cull bins.
You can also add custom cull bins using the Config.prc
file. All you need to do is add lines similar to the ones shown in the following code:
cull-bin nameA 80 unsorted cull-bin nameB 90 state_sorted cull-bin nameC 100 back_to_front cull-bin nameD 110 front_to_back cull-bin nameE 120 fixed
As you might have guessed already, the arguments to the cull-bin
variable are the bin's name, the sort order, and the cull bin's type. Different to the names of the bin types shown in the preceding sections, this uses a slightly different naming convention.