Framebuffers — new strategies and syntax in mental ray 3.6 |
Home |
Chapter 25 demonstrates how rendering components can be saved into separate files using framebuffers. This page describes the simplified scene file syntax for the definition of framebuffers introduced in mental ray version 3.6 as well as the use of geometry shaders in framebuffer definition.
In releases of mental ray prior to 3.6, framebuffers defined by the
user are identified by integer indices, and named simply by preceding
the framebuffer index with the string “fb”. These names are defined
in the options block of the scene, for example, in lines 7-9 in this
set of options statements from scene file buffer_5.mi
from Chapter 25.
During rendering, shaders can access framebuffers using the API
functions mi_fb_put() and mi_fb_get(). For example,
Chapter 25 defines the shader
framebuffer_put that
simply passes along the color in the result pointer, but with
the side effect of storing the color in a framebuffer with
mi_fb_put() in line 7:
When rendering is done, framebuffers are written to the files defined by “output statements” in the camera:
Though this framebuffer structure is still valid in version 3.6, it continues to sufffer from two primary problems:
In the following sections, we’ll address problem #1 with geometry
shaders make_indexed_framebuffers and
make_named_framebuffers for framebuffer definition. The new
“named framebuffers” of version 3.6 will help with problem #2.
Framebuffer creation Framebuffer writing Indexed make_indexed_framebuffersframebuffer_put(from Chapter 25)Named make_named_framebuffersnamed_framebuffer_put
We typically think of geometry shaders as constructing geometric primitives to be added to the scene that mental ray will render. However, geometry shaders also have access to other elements in the scene database, and (with appropriate care) can modify those elements. Here we are depending upon the fact that geometry shaders only run once before actual rendering begins and primary rays are sent from the camera into the scene.
The geometry shader make_indexed_framebuffers doesn’t
make geometry at all—it modifies an option element by adding
framebuffers.
Because a scene may have more than one element of type options,
the parameter option_block_name in line 3 identifies the option
element to be modified. The second parameter is an example of
the combined use of two mechanism that allow multiple values to be
passed to a shader as a single parameter. The framebuffers
parameter, defined in lines 4-8, is an array of structs. Grouping all
the information required to create and use a single framebuffer in a
struct simplifies the definition of a list of framebuffers. (This is
similar to the array of colors that we developed for environment
shaders in Chapter 21.
Specifying the array of structs for make_indexed_framebuffers in
a scene file follows the same syntactic pattern for an array of any
type: a comma-separated list of items enclosed by square brackets. In
the case of a struct, each item is surrounded by curly braces, with
the fields of the struct defined by key/value pairs:
In the C shader code, a corresponding struct must be declared for the struct used in the list of framebuffers, duplicating the struct of the scene file and its field values specifying the framebuffer’s index, type and filename:
Now that we’ve declared the struct for a single framebuffer, we can declare the parameter struct that will include an array of the these individual framebuffers. This is the same type of parameter struct that is used to define array parameters, with the single scene file array parameter expanded in C to three fields that define the offset, count, and pointer to the initial element of the array. (See the description of light arrays in Chapter 12, section 1, and the on-line documentation section Using and Writing Shaders / Shader Parameter Declarations.)
However, the complexity of an array of structs suggests that we divide
the shader into two parts, separately evaluating the shader’s
parameters to create the arguments that will define the actions of the shader.
We’ll store the evaluated parameters into a separate struct,
fb_args:
For clarity, we’re using the convention that a shader’s inputs are called its parameters, whereas the inputs to a function are called its arguments.
Utility function get_indexed_framebuffer_args evaluates the
shader’s parameters. It assumes that the memory for an array of
fb_args has already been allocated. The array — a pointer to
fb_args — is passed as the first argument.
Notice that we are printing out the results of our parameter evaluation in lines 17-19. We can easily confirm the accuracy of reading the parameters from the scene file by viewing the progress messages. Now we can be sure that any errors we encounter after this point result from the use of the parameters in the shader and not simply from their acquisition.
Using get_indexed_framebuffer_args, we can now define the shader
function for make_indexed_framebuffers.
The size of the array of fb_args allocated in line 9 is based on
the number of framebuffers defined by parameter n_fb.
Notice that we use mi_mem_allocate in line 9 and
mi_mem_release in line 27, rather than
malloc and free, to allow for mental ray
memory management. We store the framebuffer creation arguments in array
args in line 10. Framebuffer creation then takes
place in two phases: addition of the framebuffers to the options
element in lines 12-16, and the actual creation of the framebuffers in
lines 18-26.
The construction of indexed framebuffers in the geometry shader of the
previous section consolidates the scene file definitions of
framebuffers into a single syntactic unit. However, it does not
address our other problem of a fixed set of framebuffer names. As of
mental ray release 3.6, framebuffer definition can instead be
consolidated in the scene file itself within the camera element. The
new framebuffer statement in the camera object defines all
attributes of each framebuffer. Framebuffers can also have arbitrary
names; these names are then used to determine the index for
mi_fb_put.
For example, the framebuffer definitions in the first section, above, could be replaced by these statements within the camera:
The full set of attributes for the framebuffer are described in the on-line documentation in Scene Description Language / Scene Entities / Camera / Frame Buffers.
The shader
framebuffer_put
defined in the book can be modified to use the new named framebuffer
mechanism. The declaration of the new shader
named_framebuffer_put has only been changed from
framebuffer_put in the use of a name instead of an index to
specify the framebuffer.
Newer mechanisms in mental ray use C++ namespaces and classes. In
named_framebuffer_put, access to framebuffers is provided by
class Access_fb in namespace mi::shader. (See Upgrading
/ Upgrading from mental ray 3.5 to 3.6 in the on-line mental ray
documentation.)
The syntax of line 7 may be unfamiliar to C programmers. It declares
a variable named framebuffers that is an instance of class
mi::shader::Access_fb (the Access_fb class in the
mi::shader namespace). The constructor function of
Access_fb that has a single miTag argument is then run
with state->camera->buffertag to
initialize the class instance. To use mi_fb_put in line 11 we
need the index of the framebuffer, so in line 10 we call the
get_index method of Access_fb on instance framebuffers.
But what about other framebuffers? When we defined the geometry shader
make_indexed_framebuffers that created framebuffers, we had the
shader also print out a description of the framebuffer list. We can
use an init function for named_framebuffer_put that will only
run a single time when the shaders is first used to display
information about all the framebuffers that have been defined.
Printing out diagnostic information before rendering begins can help verify that you have defined the framebuffers correctly, which is not as easily done once rendering begins.
Creating framebuffers in a geometry shader using the new
Access_fb class is very similar in structure to the
method shown above for indexed framebuffers. Once again, we have an
array of structs as an input to our geometry shader, but with a name
instead of a number to identify each one.
Using the shader in a scene file employs the same array of structs as with the indexed version.
We need a struct to define the information for a single framebuffer that is the C++ version of the structs in the scene file.
The parameter struct for make_named_framebuffers defines
the same array of structs for the framebuffers. However, we are not
modifying the options element, so we do not need to specify its name
as a parameter.
Evaluating the parameters for each framebuffer produces arguments
specific to the new framebuffer mechanism, stored in struct fb_args.
We’ll divide up the shader into two phases as before: evaluate all the parameters, then use those parameters to create framebuffers.
Notice that the list of framebuffer arguments are stored in the standard
library vector container defined in line 9 of
get_named_framebuffer_args. Once we’ve collected information for
all the framebuffers, we use the Edit_fb class to set the
attributes of the framebuffer list in the database.
These new mechanisms for framebuffers differ from version 3.5 in the way that framebuffers are defined and accessed in shaders, but the techniques of Chapter 25 for creating compositing layers are still applicable.

buffer_8.tif

buffer_8_diffuse.tif

buffer_8_specular.tif

buffer_8_indirect.tif
Source files (gzip tar) WMRS_framebuffers.tgzSource files (zip) WMRS_framebuffers.zip