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:

The instance counter tutorial scene

Creation of the initial scene

We start creating a box that is replicated 100 times using a simple instancing pattern:

A simple instancing pattern used in the tutorial

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 REDsdk 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:

Instance identification using different materials

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:

  1. One material in the rendered scene must be using the instance counter reference: RED::RenderShaderParameter::REF_INSTANCE_COUNTER.
  2. An 'instance counter callback' has to be set through the RED::IViewpoint API, using the RED::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:

Example of the instance counter callback workflow.

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:

  1. R / N1 / O
  2. R / N2 / O
  3. R / N3 / O
  4. 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 that 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:

Instancing groups of objects

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:

  1. A / N1 / B / C
  2. A / N1 / B / D
  3. A / N2 / B / C
  4. 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.