Using Instance Counters
This tutorial focuses on using a special RED::RenderShaderParameter
called the instance counter reference. This reference gives a shader the ability to retrieve a value that is unique for each objects instance in a scene. We’ll detail the construction and rendering of the scene illustrated below:
Creation of the Initial Scene
We start creating a box that is replicated 100 times using a simple instancing pattern:
We create intermediate transform nodes that receive each a transformation matrix and the box geometry as unique child. We link these nodes to the root of the scene so that we create our array of boxes.
Note that at this stage, all boxes are using the same material (which is the HOOPS Luminate default material) and are rendered with the same color: it’s a grey looking material.
Identifying Instances using Materials
We have several possibilities to identify instances. One simple solution could be to remove the teapot’s material and to override all nodes materials with different materials each using a specific color. In this case, we would construct the following scene architecture:
Here, we set a material with a different color on each node, we remove the teapot’s material and that’s it: thanks to the scene graph inheritance rules for materials, each instance will be rendered using a different color.
If we have a large number of instances we easily see that this method will require the creation of one different material for each object instance; therefore increasing the memory cost and resource usage of the application. Furthermore, the rendering performance of the assembly may suffer as the engine will have to change the material setup before rendering every instance.
Identifying Instances using the Instance Counter
We’ll be able to reach the same instance identification objective using a single material if we take advantage of the instance counter reference. The instance counter is activated by the setup of two mechanisms:
One material in the rendered scene must be using the instance counter reference:
RED::RenderShaderParameter::REF_INSTANCE_COUNTER
An ‘instance counter callback’ has to be set through the
RED::IViewpoint
API, using theRED::IViewpoint::SetInstanceCounterCallback
method
The value of the instance counter reference is a RED::Vector4
that is defined for each object instance by the instance counter callback using the mechanism detailed below: The engine internally parses a scene to figure out what has to be rendered. It has the knowledge of which objects are instances and which shape path in the scene graph must be followed to access each of these instances. This is illustrated here:
In the scene whose scene graph is on the top part of the illustration above, an object O is seen 4 times: it’s rendered once (using the path R => O) and it’s rendered 3 more times (using a path R => Nx => O).
The engine creates a serialized view of this scene, shown by the bottom part of the illustration above. This serialized view identifies each unique instance of M and calls the instance counter callback for each. In our example above, we’ll be called 4 times then, for each path leading to an instance of O:
R / N1 / O
R / N2 / O
R / N3 / O
R / O
The engine assigns an unique UID value to each instance (a strictly positive integer number). This UID value will remain unmodified for the entire life of the object instance assuming that it’s definition path is not changed.
The way the engine selects UID values is internal to the engine and should not be guessed from the application. The point is that every object has an Unique ID should be enough to do a mapping between this UID and an application’s value to use for each instance; for example using a RED::Map
< > class.
Note
All objects that are using the instance counter reference receive an UID and are called by the instance counter callback. In our example above, O has no specific instance node on the R => O path. We could remove N1, N2 and N3 and still see O called with UID = 4 by the callback.
Performances
Instancing is meant to display the same data several times using different transform matrices. Therefore, before the engine is to render an instanced object that has a given transform matrix, it has to setup its transform matrix. Adding the instance counter reference simply adds the setup of an extra shader parameter to the rendering pipeline: this is not a costly operation and the effect on rendering performances is quite low.
Instancing Groups
We have seen the behavior of the instance counter used on instanced geometries (points, lines, meshes or texts). Now, this behavior extends to the instancing of groups of objects. If we consider the scene below with B instanced twice through nodes N1 and N2:
The instance counter will be assigned for each instance of C and each instance of D that are geometry leaves. Therefore, we’ll get 4 calls:
A / N1 / B / C
A / N1 / B / D
A / N2 / B / C
A / N2 / B / D
Editing Instanced Geometry
The instance callback is called once for each instance. It’s updated for an instance whenever something has changed on the path of the instance.
If we consider the example above, a modification of N1 (matrix, layerset, material, etc…) cause an update of the UID values 1 and 2. It does not affect UID values 3 and 4, because for these, N1 does not appear in the path that identify the instance. Similarly, a change in B will cause the update through the callback for all 4 objects because B appears in each path that identifies each instance.
The last tutorial step illustrates an example of geometry edition: all colors are removed from the map that was used to define the color for each instance and only one single object gets the red color instead. As we only provoke a modification on the path to that single object, other objects are not changed and the engine keep the previous colors it had stored for other instances.
Note
The RED::OPTIONS_REFRESH_INSTANCE_COUNTERS
can be turned on to force the refresh of all instance counters values and images in a scene graph. This can be used in specific configurations where there are material changes and no easy way to refresh the scene graph accordingly. Turning the option once to get the update and disabling it after is enough to ensure that all values are properly refreshed until other changes occur.