All color, displacement, contour, and other computation in mental ray is based on shaders. There are various types of shaders for different situations, such as material shaders to evaluate the material properties of a surface, light shaders to evaluate the light-emitting properties of a light source, lens shaders to specify camera properties other than the default pinhole camera, and so on.
There are built-in shaders that support SOFTIMAGE, Wavefront, and Alias compatibility. Much of the power of mental ray relies on the possibility to write custom shaders and link them dynamically to mental ray at runtime to replace some or all of the built-in shaders. Custom shaders are written in C or C++, using the full language and library support available in these languages.
Here are the steps necessary to create a new shader:
Shaders are written as C or C++ subroutines, stored in files with the extension ``.c''. To use these shaders in a scene, they must be dynamically linked into mental ray at runtime. mental ray accepts shaders in three forms:
The commands to create a DSO depend on the operating system type. To create a DSO named example.so from a source file example.c, use the following commands. Insert the -g command line option after -c to insert debugging information, or insert -O to compile an optimized version. On most systems -g and -O cannot be combined. Refer to the compiler documentation for details.
SGI offers a range of processors, with later processors having more instruction set features than earlier ones. While newer processors and newer operating system versions can execute older programs (as long as they all use the ELF object format), programs and shaders should be compiled for newer instruction sets to improve performance. Four instruction sets have been defined by MIPS, called mips1 through mips4. mips3 is about 30% faster than mips2 on the same system. mips4 should be used if a very large virtual address space (4 terabytes) is required to keep large numbers of large textures. Choose the opt options string according to the following table:
IRIX instr set CPU word size opt
5.x mips2 R4k and up 32 bits 6.x mips2 R4k and up 32 bits -32 6.x mips3 R4k and up 32 bits -n32 6.x mips4 R8k and up 64 bits -64
In any case, the options must agree with the version of mental ray. See the abi(5) manual page for more details. Use the Unix file command on mental ray and the shader library and make sure that they both report the same number of bits and the same MIPS version. If there is a mismatch, the run-time linker will print a message like:
/my/shader.so: 1234:ray: rld: Fatal Error: cannot successfully map soname '/my/shader.so' under any of the filenames /my/shader.so
If the filename of the DSO given as an absolute pathname mental ray only tries to load that particular file. If it is given as a relative path, mental ray searches the DSO in a list of paths that can be supplied via the command line parameter (-ld_path) or the environment variable (MI_LIBRARY_PATH). There is also a default search path (/usr/local/mi/lib;.). A search list is a colon-separated (Unix) or semicolon-separated (Unix or Windows NT) list of paths to be searched. (Windows NT uses colons for drive letters.)
Note that source code (.c extension) is normally portable, unless nonportable system features (such as fsqrt on SGIs) are used. This means that the shader will run on all other vendors' systems unchanged. If the compilation fails and the shaders therefore are undefined, mental ray will --- when calls of the undefined shaders are attempted --- return miFALSE, which will generally leave the pixel sample black.
Neither object files (.o extension) nor DSOs (.so extension) are portable. They must be compiled separately for each platform and, usually, for each major operating system release. For example, a Hewlett-Packard object file will not run on an SGI system, and an SGI IRIX 4.x object file cannot be used on an IRIX 6.x system, and vice versa. Also note that pointers are 32-bit values on some systems and 64-bit values on others, and that most but not all (IBM) processors require that 64-bit values such as doubles are stored at memory addresses evenly divisible by 8.
On SGI systems, a shader can be debugged after it has been called for the first time, which attaches it to the program and makes its symbols available to the debugger. For this to work, the -g option must be given to the cc and ld commands in all stages - compilation, linking, and shared-library building. Also, only DSO shaders are debuggable, not shaders loaded in object (.o) or source (.c) form.
On non-SGI systems, debugging shaders is, unfortunately, difficult. The reason is that most debuggers cannot deal with parts of a program that have been dynamically linked. In general, the debugger will refuse to set breakpoints in dynamically linked shaders, and will step over calls to these shaders as if they were a single operating system call. Some vendors are working on fixing these problems, but at this time the only option on non-SGI systems is using mi_info or mi_debug statements in the shader sources. Avoid printf because it is lost on slave hosts and because it can cause problems at runtime.
When a shared library is loaded that contains a function module_init, that function is called just after the library was loaded, before the first shader in it is called. Conversely, if it contains a function module_exit, that function is called just before the library is unloaded, after the last call to a shader in this library. Note that in the case of irrecoverable errors at any time after the library was loaded module_exit is not guaranteed to be called. Neither module_init nor module_exit may rely on any shader interface services that assume that a rendering operation is in progress. Message functions such as mi_info are available.
Note: this feature is not supported on IBM systems and should not be used for portable shaders.
Internal space is the coordinate system mental ray uses to present intersection points and other points and vectors to shaders. All points and vectors in the state except bump basis vectors (which are in object space) are presented in internal space, namely, org, dir, point, normal, normal_geom, motion and derivs. The actual meaning of internal space is left undefined, it varies between different versions of mental ray and depends on the space statement in the options block in the .mi file. A shader must not assume that internal space is identical to world space, even though this is true in most scenes.
World space is the coordinate system in which modeling and animation takes place.
Object space is a coordinate system relative to the object's origin. The modeler that created the scene defines the object's origin; for example, the SOFTIMAGE translator uses the center of the bounding box of the object as the object origin.
Camera space is a coordinate system in which the camera is at the coordinate origin (0, 0, 0) with an up vector of (0, 1, 0) and looking down the negative Z axis.
In addition to these 3D coordinate spaces, raster space is a two-dimensional pixel location on the screen bounded by (0, 0) in the lower left corner of the image, and the rendered image resolution. The center of the pixel in the lower left corner of raster space has the coordinates (0.5, 0.5).
Screen space is defined such that (-1, -1/a) is in the lower left corner of the screen and (1, 1/a) is in the the upper right, where a is the aspect ratio of the screen (the relation between its width and height).
Most shaders never need to transform between spaces. Texture shaders frequently need to operate in object space; for example, in order to apply bump basis vectors to state->normal, the normal must be transformed to object space before the bump basis vectors are applied, and back to internal space before the result is passed to any mental ray function such as mi_trace_reflection. mental ray offers 18 functions to convert points, vectors and normals between coordinate spaces:
(see mi_point_to_world) (see mi_point_to_camera) (see mi_point_to_object) (see mi_point_from_world) (see mi_point_from_camera) (see mi_point_from_object) (see mi_vector_to_world) (see mi_vector_to_camera) (see mi_vector_to_object) (see mi_vector_from_world) (see mi_vector_from_camera) (see mi_vector_from_object) (see mi_normal_to_world) (see mi_normal_to_camera) (see mi_normal_to_object) (see mi_normal_from_world) (see mi_normal_from_camera) (see mi_normal_from_object)
function operation
mi_point_to_world(s,pr,p) internal point to world space mi_point_to_camera(s,pr,p) internal point to camera space mi_point_to_object(s,pr,p) internal point to object space mi_point_from_world(s,pr,p) world point to internal space mi_point_from_camera(s,pr,p) camera point to internal space mi_point_from_object(s,pr,p) object point to internal space mi_vector_to_world(s,vr,v) internal vector to world space mi_vector_to_camera(s,vr,v) internal vector to camera space mi_vector_to_object(s,vr,v) internal vector to object space mi_vector_from_world(s,vr,v) world vector to internal space mi_vector_from_camera(s,vr,v) camera vector to internal space mi_vector_from_object(s,vr,v) object vector to internal space mi_normal_to_world(s,vr,v) internal normal to world space mi_normal_to_camera(s,vr,v) internal normal to camera space mi_normal_to_object(s,vr,v) internal normal to object space mi_normal_from_world(s,vr,v) world normal to internal space mi_normal_from_camera(s,vr,v) camera normal to internal space mi_normal_from_object(s,vr,v) object normal to internal space
Point and vector transformations are similar, except that the vector versions ignore the translation part of the matrix. Normal transformations are similar to vector transformations, except that the transpose of the inverse transformation matrix is used. In this way it is ensured that if a vector and a normal are orthogonal in one coordinate system they remain orthogonal after they have been transformed to a different coordinate system. This holds for arbitrary, not necessarily orthogonal transformations.
The length of vectors is preserved only if the transformation matrix does not scale. The mi_point_transform and mi_vector_transform functions are also available to transform points and vectors between arbitrary coordinate systems given by a transformation matrix. mi_vector_transform_T transforms with the transpose of the matrix and can be used for the transformation of normals.
There are many types of shaders, all of which can be substituted by user-written shaders:
The annotations set in italics are numbered; the events described happen in the sequence given by the numbers.
Since material shaders may do inside/outside calculations based on the surface normal or the parent state chain (see below), the volume shaders are marked (1) and (2), depending on whether the volume shader left by A or by T/D in the refraction volume field of the state. The default refraction volume shader is the one found in the material definition, or the standard volume shader if the material defines no volume shader. For details on choosing volume shaders, see the section on writing material and volume shaders. Note that the volume shaders in this diagram are called immediately after the material shader returns; this behavior can be changed using the ``smart volume'' option in the volume shader declaration. Smart volume shaders are called instead of the material shader when a ray hits the object, and are responsible for calling the material shader.
The next two diagrams depict the situation when the material shader at the intersection point M requests a light ray from the light source at L, by calling a function such as mi_sample_light. This results in the light shader of L to be called. No intersection testing is done at this point. Intersection testing takes place when shadows are enabled and the light shader casts shadow rays (see shadow ray) by calling mi_trace_shadow. This function is called only once but may result in more than one shadow shader call. There are four different modes for shadow casting, listed in the order of increased computational cost:
The first diagram shows the ray casting order and the ray directions for the shadow on and shadow sort modes:
(see shadow segments) The next diagram shows the same situation in shadow segment mode:
The following diagram illustrates the path of a photon shot from the light source in the caustics
preprocessing phase. First a photon is traced from the light source. It hits object A, and the photon material shader of object A is called. The photon material shader stores energy at the intersection point and determines how much energy is reflected and how much is refracted, and the directions of reflection and transmission. It then traces a new photon from A, in the reflection direction, or in the transmission direction, or both. The reflected photon hits object B, and the photon material shader of object B is called. The photon material shader of object B stores energy at the intersection point and shoots a new photon.
The remainder of this chapter describes how to write all types of shaders. First, the concepts of ray tracing state parameter passing common to all shaders are presented, followed by a detailed discussion of each type of shader.
Every shader needs to access information about the current state of mental ray, and information about the intersection that led to the shader call. This information is stored in a single structure known as the state. Not all information in the state is of interest or defined for all shaders; for example, lens shaders are called before an intersection is done and hence have no information such as the intersection point or the normal there. It is recommended to call the state parameter that shaders receive as a formal parameter state because some macros provided in the mi_shader.h include file that require access to the state rely on this name (namely, the typed mi_eval_* (see mi_eval) variants). The state, and everything else needed to write shaders, is defined in mi_shader.h, which must be included by all shader source files.
Before a shader is called, mental ray prepares a new state structure that provides global information to the shader. This state may be the same data structure that was used in the previous call (this is the case for shaders that modify another shader's result, like lens, shadow, and volume shaders); or it may be a new state structure that is a copy of the calling shader's state with some state variables changed (this is done if a secondary ray is cast with one of the tracing functions provided by mental ray). For example, if a material shader that is using state A casts a reflected ray, which hits another object and causes that object's material shader to be called with state B, state B will be a copy of state A except for the ray and intersection information, which will be different in states A and B. State A is said to be the parent of state B. The state contains a parent pointer that allows sub-shader to access the state of parent shaders. If a volume shader is called after the material shader, the volume shader modifies the color calculated by the material shader, and gets the same state as the material shader, instead of a fresh copy.
The state also contains a child pointer that, together with the parent pointer, forms a double-linked list of states. After a shader casts a ray, the state used by the ray remains available after the trace call returns. This means that the shader has full access to the intersection information, label value, and all other state variables used by the child shader. For example, the shader for a completely transparent object may decide to copy state->child-> label to state->label after mi_trace_transparency returns. Only the most recent discarded child state is retained; state->child->child is undefined.
This means that it is possible to pass information from one shader to another in the call tree for a primary ray, by one of two methods: either the parent (the caller) changes its own state that will be inherited by the child, or the child follows the parent pointer. The state contains a user pointer that a parent can store the address of a local data structure in, for passing it to sub-shaders. Since every sub-shader inherits this pointer, it may access information provided by its parent. A typical application of this are inside/outside calculations performed by material shaders to find out whether the ray is inside a closed object to base the interpretation of parameters such as the index of refraction on.
Note that the state can be used to pass information from one shader to sub-shaders that are lower in the call tree. Care must be taken not to destroy information in the state because some shaders (shadow, volume, and the first eye shader) re-use the state from the previous call. In particular, the state cannot be used to pass information from one primary (camera) ray to the next. Static variables can be used in the shader for this purpose, but care must be taken to avoid multiple access on multiprocessor shared-memory machines. On such a machine, all processors share the same set of static variables, and every change by one processor becomes immediately visible to all other processors, which may be executing the same shader at the same time. Locking facilities are available in mental ray to protect critical sections that may execute only once at any time.
Here is a complete list of state variables usable by shaders. Variables not listed here are for internal use only and should not be accessed or modified by shaders. Some variables are available only in some types of shaders; see the ``State Variables by Shader Type'' section below. The first table lists all state variables that remain unchanged for the duration of the frame:
type name content
int version shader interface version miTag camera_inst tag of camera instance miCamera * camera camera information miRc_options * options general rendering options
(see camera) The camera data structure pointed to by camera has the following fields. None of these may be written to by a shader.
type name content
miBoolean orthographic orthographic rendering float focal focal length of the camera float aperture aperture of the camera float aspect aspect ratio y /x miRange clip Z clipping distances int x_resolution image width in pixels int y_resolution image height in pixels int window.xl left image margin int window.yl bottom image margin int window.xh right image margin int window.yh top image margin miTag volume camera volume (atmosphere) miTag environment camera environment shader miTag lens lens shader or lens shader list miTag output output shader or output shader list int frame frame number float frame_time frame time in seconds
The option data structure pointed to by option has the following format. The option structure may not be written to by shaders.
type name content
miBoolean trace ray tracing turned on? miBoolean scanline scanline mode turned on? miBoolean motion motion blur turned on? char shadow no, normal, segmented, sorted char filter nonlocal sampling filter char acceleration ray tracing algorithm char face front, back, or both faces char field odd or even fields, or off int reflection_depth max reflection trace depth int refraction_depth max refraction trace depth int trace_depth max combined trace depth int photon_reflection_depth max photon reflection trace depth int photon_refraction_depth max photon refraction trace depth int photon_trace_depth max combined photon trace depth miBoolean caustic caustics on/off
miPointer image [ ] frame buffers for output shaders
The state variables in the next table describe an eye (primary) ray. There is one eye ray for every sample that contributes to a pixel in the output image. If a material shader that evaluates a material hit by a primary ray casts secondary reflection, refraction, transparency, light, or shadow rays, all shaders called as a consequence will inherit these variables unmodified:
type name content
miScalar raster_x X coordinate of image pixel miScalar raster_y Y coordinate of image pixel miFunction * shader current shader miLock global_lock lock shared by all shaders (see locking) short thread current thread
Whenever a ray is cast the following state variables are set to describe the ray:
type name content
miState * parent state of parent shader miState * child state of child shader miRay_type type type of ray: reflect, light... miCBoolean scanline from scanline algorithm void * cache RC intersection cache miVector org start point of the ray miVector dir direction of the ray float time shutter interval time miTag volume volume shader of primitive miTag environment environment shader int reflection_level current reflection ray depth int refraction_level current refraction ray depth
The variables in the next table are closely related to the previous. They describe the intersection of the ray with an object, and give information about that object and how it was hit.
type name content
miTag refraction_volume volume shader for refraction miUint label object label for label file miTag instance instance of object miTag light_instance instance of light miScalar [4] bary barycentric coordinates miVector point intersection (ray end) point miVector normal interpolated normal at point miVector normal_geom geometry normal at point miCBoolean inv_normal true if normals were inverted miScalar dot_nd dot prod of normal and dir double dist length of the ray void * pri identifies hit box int pri_idx identifies hit primitive in box double shadow_tol safe zone to prevent self-shadows miScalar ior index of refraction of medium miScalar ior_in index of r. of previous medium
(see texture) The following table is an extension to the previous. These variables give information about the intersection point for texture mapping. They are defined when the ray has hit a textured object:
type name content
miVector * tex_list list of texture coordinates miVector * bump_x_list list of X bump basis vectors miVector * bump_y_list list of Y bump basis vectors miVector tex texture coord (tex shaders) miVector motion interpolated motion vector miVector * derivs[5] list of surface derivatives
Finally, the user field allows a shader to store a pointer to an arbitrary data structure in the shader. Subsequent shaders called as a result of operations done by this shader (such as casting a reflection ray or evaluating a light or a texture) inherit the pointer and can read and write this shader's local data. Sub-shaders can also find other parent's user data by following state parent pointers, see above. With this method, extra parameters can be provided to and extra return values received from sub-shaders. The user variables are initialized to 0.
type name content
void * user user data pointer int user_size user data size (optional)
The shader pointer can be used to access the shader parameters, as state->parameters. This is redundant for the current shader because this pointer is also passed as the third shader argument, but it can be used to find a parent shader's parameters.
State Variables by Shader Type
The following table describes which state variables are available in which types of shaders:
G geometry shader D displacement shader P photon shader and photon volume shader E photon emitter shader Le lens shader M material shader and texture shader T texture shader V volume shader Lg light shader S shadow shader O output shader
In the following table,
Here is the table:
variable G D P E Le M V Lg S O
version R R R R R R R R R R camera_inst R R R R R R R R R R camera R R R R R R R R R R options R R R R R R R R R R global_lock # -- rw rw rw rw rw rw rw rw -- raster_x -- -- -- -- R R R R R -- raster_y -- -- -- -- R R R R R -- parent -- -- R R R R R R R -- type R R R R R R R r r r scanline -- -- -- -- r r r r r -- inv_normal -- -- r -- r r r r r -- reflection_level -- -- rw rw rw rw rw rw rw -- refraction_level -- -- rw rw rw rw rw rw rw -- org -- -- rw rw rw rw rw rw rw -- dir -- -- rw rw rw rw rw rw rw -- dist -- -- rw rw rw rw rw rw rw -- time -- -- R R R R R R R -- ior -- -- x x x x x x x -- ior_in -- -- x x x x x x x -- material -- r r r -- r r r r -- volume -- -- rw rw rw rw rw rw rw -- environment -- -- -- -- rw rw rw -- -- -- refraction_volume -- -- rw rw -- rw rw -- -- -- label -- r r r rw rw rw rw rw -- instance R R R -- R R R R R -- light_instance -- -- -- R -- -- R * R R -- pri *** -- -- r -- rw rw rw rw rw -- pri_idx -- -- r -- r r r r r -- bary[4] -- -- r -- r r r r r -- point -- r r -- rw rw rw rw rw -- normal -- r r -- rw rw rw rw rw -- normal_geom -- -- r -- rw rw rw rw rw -- dot_nd -- -- r -- rw rw rw rw rw -- shadow_tol -- -- r -- r r r r r -- tex_list -- rw rw -- rw rw rw rw rw -- bump_x_list -- -- rw -- rw rw rw rw rw -- bump_y_list -- -- rw -- rw rw rw rw rw -- motion -- -- rw rw rw rw rw rw rw -- derivs ** -- -- rw -- rw rw rw rw rw -- tex x x x x x x x x x x shader -- R R R R R R R R -- child -- -- R R R R R R R -- thread R R R R R R R R R -- user x x x x x x x x x x user_size x x x x x x x x x x
* If state->type is miSHADER_SHADOW ** If the object contains surface derivatives *** Set to 0 to cast rays from empty space. Do not set to other values. # global_lock is accessed with mi_lock and mi_unlock only.
In addition to the state variables that are provided by mental ray and are shared by all shaders, every shader has user parameters. In the .mi scene file, shader references look much like a function call: the shader name is given along with a list of parameters. Every shader call may have a different list of parameters. mental ray does not restrict or predefine the number and types of user parameters, any kind of information may be passed to the shader. Typical examples for user parameters are ambient, diffuse, and specular colors for material shaders, attenuation parameters for light shaders, and so on. An empty parameter list in a shader call (as opposed to a shader declaration) has a special meaning; see the note at the end of this chapter.
In this manual, the term ``parameters'' refers to shader parameters in the .mi scene file; the term ``arguments'' is used for arguments to C functions.
Shaders need both state variables and user parameters. Generally, variables that are computed by mental ray, or whose interpretation is otherwise known to mental ray, and that are useful to different types or instances of shaders are found in state variables. Variables that are specific to a shader, and that may change for each instance of the shader, are found in user variables. mental ray does not access or compute user variables in any way, it merely passes them from the .mi file to the shader when it is invoked.
(see shader declaration) (see declaration) To interpret these parameters in the .mi file, mental ray needs a declaration of parameter names and types that is equivalent to the C struct that the shader later uses to access the parameters. The declaration in the .mi file must be exactly equivalent to the C struct, or the shader will mis-interpret the parameter data structure constructed by mental ray. This means that three parts are needed to write a shader: the C source of the shader, the C parameter struct, and the .mi declaration. The latter is normally stored in a separate file that is included into the .mi scene file using a $include statement.
The syntax of .mi shader declarations is fully described in the Shader section of the scene grammar description chapter, which describes the .mi language in detail. Here, only a brief overview is given. Shaders must be declared with shader name, return type, and the types and names of all parameters.
Options such as the shader version may be specified also:
declare shader [type] "shader_name" ( type "parameter_name", type "parameter_name", ... type "parameter_name" ) [ options ] end declare
For example, a simple material shader containing ambient, diffuse, and specular colors, transparency, an optional array of bump map textures, and an array of lights could be declared in the .mi file as:
declare shader color "my_material" ( color "ambient", color "diffuse", color "specular", scalar "shiny", scalar "reflect", scalar "transparency", scalar "ior", vector texture "bump", array light "lights" ) version 1 end declare
If there is only one array, there is a small efficiency advantage in listing it last. It is recommended that the largest array (arrays of large structs are larger than arrays of primitives) is given as the last parameter. The material shader declared in this example can be used in a material statement like this:
material "mat1" "my_material" ( "specular" 1.0 1.0 1.0, "ambient" 0.3 0.3 0.0, "diffuse" 0.7 0.7 0.0, "shiny" 50.0, "bump" "tex1", "lights" [ "light1", "light2", "light3" ], "reflect" 0.5 ) end material
Note that the parameters can be defined in any order that does not have to agree with the order in the declaration, and that parameters can be omitted. Omitted parameters default to 0. This example assumes that the texture tex1 and the three lights have been defined prior to this material definition. Again, be sure to use the names of the textures and lights, not the names of the texture and light shaders. All names in the above two examples were written as strings enclosed in double quotes to disambiguate names from reserved keywords, and to allow special characters in the names that would otherwise be illegal.
When choosing names, avoid double colons and periods, which have a special meaning when accessing structured shader return values and interface parameters in phenomenon subshaders.
When the shader my_material is called, its third argument will be a pointer to a data structure built by mental ray from the declaration and the actual parameters in the .mi file. In order for the C source of the shader to access the parameters, it needs an equivalent declaration in C syntax that must agree exactly with the .mi declaration. The type names can be translated according to the following table:
.mi syntax C syntax
boolean miBoolean integer miInteger scalar miScalar vector miVector transform miMatrix color miColor shader miTag of a miFunction color texture miTag of a miFunction or miImg_image scalar texture miTag of a miFunction or miImg_image vector texture miTag of a miFunction or miImg_image light miTag of a miInstance for a light material miTag of a miMaterial geometry miTag of a miObject, miGroup, or miInstance struct struct array int, int, type[1]
It is strongly recommended to use the same parameter names in the C declaration as in the .mi declaration. Also, structures in either declaration should be reflected in the other, even if not enclosed in arrays, to ensure that mental ray inserts the same padding as the C compiler.
Arrays are more complicated than the types in this table because the size of the array is not known at declaration time. The C declaration of an array consists of a start index prefixed with i_, the size of the array prefixed with n_, and the array itself, declared as an array with one element. mental ray will allocate the structure as large as required by the actual array size at call time. To access array element i in the range 0 ... n_array-1, the C expression array[i + i_array] must be used. This expression allows mental ray to store the user parameters in virtual shared memory regardless of the base address of the user parameter structure, which is different on every host on the network.
For the above example .mi declaration, the equivalent C structure declaration using mental ray types looks like this:
struct my_material { miColor ambient; miColor diffuse; miColor specular; miScalar shiny; miScalar reflect; miScalar transparency; miScalar ior; miTag bump; int i_lights; int n_lights; miTag lights[1]; };
Note that here the order of structure members must match the order in the .mi declaration exactly. For example, suppose a shader has a .mi declaration containing an array of integers:
declare shader color "myshader" ( array integer "list" ) version 1 end declare
The C declaration for the shader's parameters is:
struct myshader { int i_list; int n_list; miInteger list[1]; };
A shader that needs to operate on this array, for example printing all the array elements to stdout, would use a loop like this:
(see mi_eval_integer)
int *list = mi_eval_integer(paras->list); int i_list = *mi_eval_integer(¶s->i_list); int n_list = *mi_eval_integer(¶s->n_list); int i; for (i=0; i < n_list; i++) mi_info("%d", list[i_list + i]);
assuming that paras is the third shader argument and has type struct myshader *. The use of the i_list parameter may seem strange to C programmers, who may wish to hide it in a macro like
#define EL(array,nel) array[i_##array + nel]
This macro requires an ANSI C preprocessor; K&R preprocessors do not support the ## notation and should use /**/ instead. This macro is not predefined in shader.h. The reason for this peculiar way of accessing arrays is improved performance. The array list[1] has space for only one element, because the actual number of array elements depends on the shader instance in the .mi file, which may list an arbitrary number of elements. Since mental ray is based on a virtual shared database that moves pieces of data such as shader parameters transparently from one host to another, no such piece of data may contain a pointer. Pointers would not be valid in another host's virtual address space. Adjusting the pointer on the other host is impractical because it would significantly reduce performance for some scenes, and would require knowledge of the structure layout for finding the pointers that may not be available in versions of mental ray not based on a .mi front-end parser. Therefore, the array is appended to the parameter structure, so the entire block can be moved to another host in a single network transfer. It is safe to access the first element of the array, because space for it is always allocated by declarations such as list[1], but the second is a problem because in a C declaration like
struct myshader { int i_list; int n_list; miInteger list[1]; miScalar factor; miBoolean bool; };
the second element, list[1], occupies the same address as factor, and the third overlays bool. The situation becomes more complex for arrays of structures. The solution is to put the value of the first element after the last ``regular'' shader parameter, bool in this example, followed by the other element values. This means that the first few C array elements that overlay other parameters must be skipped. The i_ variable tells the shader exactly how many. In the example, i_list would be 3. Assuming the following shader instance, used as part of a material, texture, or some other definition requiring a shader call:
"myshader" ( "factor" 1.4142136, "list" [ 42, 123, 486921, 777 ], "bool" on )
mental ray would arrange the values in memory like this:
This diagram assumes that a miScalar uses four bytes; this may not be true in all versions. If it used eight bytes, four bytes of padding would be inserted before it by mental ray and the C compiler, and i_list would have the value 5.
(see shader assignment)
Parameter Assignments and mi_eval
(see interface parameter) The chapter on the .mi scene description language explains assignment of shaders and phenomenon interface parameters to shader parameters. Assignments replace the constant value of a parameter with a link to a shader that gets called automatically when the shader asks for the value of the parameter, or a link to the phenomenon interface which is looked up, respectively.
Shader assignments simplify the development of small, simple shaders called base shaders that can be combined into larger shaders or phenomena. For example, assignments make it possible to apply a texture to any shader parameter simply by replacing the constant value of a parameter with an assignment to a texture shader. It is no longer necessary for shader writers to anticipate which parameters should be texturable, and adding additional shader parameters of type color texture to the parameter list, just in case.
For example, consider this shader assignment example from the .mi language chapter:
declare shader color "phong" (color "ambient", color "diffuse", color "specular") version 1 end declare declare shader struct {color "a", color "b"} "texture" (color texture "picture") version 1 end declare color texture "fluffy" "/tmp/image.pic" shader "map" "texture" ( "picture" "fluffy") shader "mtlsh" "phong" ( "ambient" 0.3 0.3 0.3, "diffuse" = "map.a", "specular" = "map.b")
The author of the phong shader can focus on the purpose of the shader (Phong illumination) without having to consider texturing at all. The texture is applied later as a shader assignment to the parameters of phong. This example further illustrates that shaders may have structured return values, in this case in the shader texture which returns both a and b, which can be picked up individually using the dot-member notation.
Frequently, shaders do not access all their parameters. Consider a multiplexing material shader that calls one of two material shaders depending on whether the incident ray hit the front face or the back face of the material. It would be very inefficient to evaluate both material shaders and then choose one of the two returned colors. For this reason, mental ray does not pre-evaluate all assignments before calling a shader. Instead, the shader itself must ask for the value of one of its parameters explicitly. This is done with the mi_eval function. The signature of mi_eval is
(see mi_eval)
void *mi_eval (miState *state, void *param) miBoolean *mi_eval_boolean (miBoolean *param) miInteger *mi_eval_integer (miInteger *param) miScalar *mi_eval_scalar (miScalar *param) miVector *mi_eval_vector (miVector *param) miScalar *mi_eval_transform (miScalar *param) miColor *mi_eval_color (miColor *param) miTag *mi_eval_tag (miTag *param)
The typed variants of mi_eval are implemented as macros that perform all necessary typecasting, as a convenience for shader writers. There are three cases handled by mi_eval:
For example, the phong shader in the example above would be implemented as:
struct phong { miColor ambient; miColor diffuse; miColor specular; }; int phong_version(void) {return(1);} void phong( miColor *result, /* returned illum color */ miState *state, /* ray tracer state */ struct phong *paras) /* phong parameters */ { miColor *diffuse; /* final diffuse param */ miColor *specular; /* final specular param */ int n, i; /* light counter */ miTag *lights; /* global light list */ miInteger samples; /* # of samples taken */ miColor color; /* color from light */ miColor sum; /* summed sample colors */ miVector dir; /* direction to light */ miScalar dot_nl; /* normal <dot> dir*/ miScalar spec_f; /* specular factor */ *result = *mi_eval_color(¶s->ambient); diffuse = mi_eval_color(¶s->diffuse); specular = mi_eval_color(¶s->specular); mi_query(miQ_NUM_GLOBAL_LIGHTS, state, 0, &n); mi_query(miQ_GLOBAL_LIGHTS, state, 0, &lights); for (i=0; i < n; i++) { sum.r = sum.g = sum.b = 0; samples = 0; while (mi_sample_light(&color, &dir, &dot_nl, state, lights[i], &samples)) { spec_f = mi_phong_specular(m->shiny, state, &dir); sum.r += color.r * (dot_nl * diffuse->r + spec_f * specular->r); sum.g += color.r * (dot_nl * diffuse->g + spec_f * specular->g); sum.b += color.r * (dot_nl * diffuse->b + spec_f * specular->b); } if (samples) { result->r += sum.r / samples; result->g += sum.g / samples; result->b += sum.b / samples; } } result->a = 1; }
Note that mi_eval_color is called early to obtain pointers to parameters once. If there is a large number of lights, this saves a large number of mi_eval_color calls later. Although mi_eval et. al. employ caching and fast lookups, this makes the shader faster than it were if the code were written like this:
dot_nl * mi_eval_color(¶s->diffuse)->r
in the inner loop.
The parameter set of a shader must always be correctly described by its declaration in the .mi scene file. It is a common source of errors to have a mismatch between the .mi and the .c shader parameters. This can lead to incorrect rendering results but can also cause shader crashes, for example if the shader mistakes a floating-point number for an array length.
For this reason it is highly recommended to attach version numbers to both the .mi declaration and the shader itself. Version numbers are positive integers, and should begin with 1 (0 is the default if the version number is missing). For example, the .mi declaration of a shader myshader might be defined as:
declare shader color "myshader" (parameters) version 42 end declare
There are two ways to ensure that the version number is correct. The recommended method is writing a pseudo-shader that provides the version number:
int myshader_version(void) {return(42);} miBoolean myshader( miColor *result, miState *state, struct myshader *paras) { ... }
(see shader initialization) Before mental ray calls a shader for the first time, just before looking for its initialization shader (whose name would be myshader_init), mental ray checks whether a function exists whose name is the shader name followed by _version. If it does, it is called and the version number is compared with the version number in the .mi declaration. If there is a mismatch, an error message is printed. If the version shader does not exist, a warning message is printed.
The second method for shader version comparison is an explicit comparison in the shader using mi_query:
#define MYSHADER_VERSION 42 miBoolean myshader( miColor *result, miState *state, struct myshader *paras) { int version; mi_query(miQ_DECL_VERSION, state, 0, &version); if (version != MYSHADER_VERSION) mi_error("myshader: wrong declaration"); ... }
It is actually preferable to do this in the initialization shader to reduce the overhead of checking the version number every time the shader is called.
It is very important to always increment the version number every time the shader parameters change. Problems introduced by mismatches are hard to find, especially if the shader versions on master and slave hosts do not agree.
The following sections discuss the various types of shaders, and how to write custom shaders of those types. Basic concepts are introduced step by step, including supporting functions and state variables supplied by mental ray. All support functions are summarized at the end of this chapter.
Material shaders are the primary type of shaders. All materials defined in the scene must at least define a material shader. Materials may also define other types of shaders, such as shadow, volume, and environment shaders, which are optional and of secondary importance.
When mental ray casts a visible ray, such as those cast by the camera (called primary rays) or those that are cast for reflections and refractions (collectively called secondary rays), mental ray determines the next object in the scene that is hit by that ray. This process is called intersection testing. For example, when a primary ray cast from the camera through the viewing plane's pixel (100,100) intersects with a yellow sphere, pixel (100, 100) in the output image will be painted yellow. (The actual process is slightly complicated by supersampling, which can cause more than one primary ray to contribute to a pixel.)
The core of mental ray has no concept of ``yellow.'' This color is computed by the material shader attached to the sphere that was hit by the ray. mental ray records general information about the sphere object, such as point of intersection, normal vector, transformation matrix etc. in a data structure called the state, and calls the material shader attached to the object. More precisely, the material shader, along with its parameters (called user parameters), is part of the material, which is attached to the polygon or surface that forms the part of the object that was hit by the ray. Objects are usually built from multiple polygons and/or surfaces, each of which may have a different material.
The material shader uses the values provided by mental ray in the state and the variables provided by the .mi file in the user parameters to calculate the color of the object, and returns that color. In the above example, the material shader would return the color yellow. mental ray stores this color in the frame buffer and casts the next primary ray. Note that if the material shader has a bug that causes it to return infinity or NaN (Not a Number) in the result color, the infinity or NaN is stored as 1.0 in integer color frame buffers. This usually results in white pixels in the rendered image. This is true for subshaders such as texture shaders also.
With an appropriate output statement (see the scene description chapter), mental ray computes depth, label, normal-vector, and motion vector frame buffers in addition to the standard color frame buffer. The color returned by the first-generation material shader is stored in the color frame buffer (unless a lens shader exists; lens shaders also have the option of modifying colors). The material shader can control what gets stored in the depth, label, normal-vector, and motion-vector frame buffers by storing appropriate values into state->point.z, state->label, state->normal, and state->motion, respectively. Depth is the negative Z coordinate.
Material shaders normally do quite complicated computations to arrive at the final color of a point on the object:
(see texture)
(see mi_eval_color) (see mi_eval_integer) (see mi_eval_scalar) (see mi_eval_tag) (see mi_call_shader) (see mi_sample_light) (see mi_reflection_dir) (see mi_refraction_dir) (see mi_trace_reflection) (see mi_trace_refraction) (see mi_trace_environment)
#include <stdio.h> #include <mi/shader.h> int my_material_version(void) {return(1);} miBoolean my_material( miColor *result, miState *state, struct my_material *paras) { miColor *diffuse, *specular; miVector bump, dir; miColor color; int num, i_lights, n_lights; miTag *lights; miScalar factor; /* * bump map */ state->tex = state->tex_list[0]; mi_call_shader((miColor *)&bump, miSHADER_TEXTURE, state, *mi_eval_tag(¶s->bump)); if (bump.x != 0 || bump.y != 0) { mi_vector_to_object(&state->normal, &state->normal); state->normal.x+=bump.x * state->bump_x_list->x +bump.y * state->bump_y_list->x; state->normal.y+=bump.x * state->bump_x_list->y +bump.y * state->bump_y_list->y; state->normal.z+=bump.x * state->bump_x_list->z +bump.y * state->bump_y_list->z; mi_vector_from_object(&state->normal, &state->normal); mi_vector_normalize(&state->normal); state->dot_nd = mi_vector_dot(&state->normal, &state->dir); } /* * illumination */ *result = *mi_eval_color (¶s->ambient); diffuse = mi_eval_color (¶s->diffuse); specular = mi_eval_color (¶s->specular); i_lights = *mi_eval_integer(¶s->i_lights); n_lights = *mi_eval_integer(¶s->n_lights); lights = mi_eval_tag ( paras->lights); for (num=0; num < n_lights; num++) { miColor color, sum; miInteger samples = 0; miScalar dot_nl; sum.r = sum.g = sum.b = 0; while (mi_sample_light(&color, &dir, &dot_nl, state, lights[i_lights + num], &samples)) { sum.r += dot_nl * diffuse->r * color.r; sum.g += dot_nl * diffuse->g * color.g; sum.b += dot_nl * diffuse->b * color.b; factor = mi_phong_specular( *mi_eval_scalar(¶s->shiny), state, &dir); sum.r += factor * specular->r * color.r; sum.g += factor * specular->g * color.g; sum.b += factor * specular->b * color.b; } if (samples) { result->r += sum.r / samples; result->g += sum.g / samples; result->b += sum.b / samples; } } result->a = 1; /* * reflections */ factor = *mi_eval_scalar(¶s->reflect); if (factor > 0) { miScalar f = 1 - factor; result->r *= f; result->g *= f; result->b *= f; mi_reflection_dir(&dir, state); if (mi_trace_reflection (&color,state,&dir) || mi_trace_environment(&color,state,&dir)) { result->r += factor * color.r; result->g += factor * color.g; result->b += factor * color.b; } } /* * refractions */ factor = *mi_eval_scalar(¶s->transparency); if (factor > 0) { miScalar ior = *mi_eval_scalar(&state->ior); miScalar f = 1 - factor; result->r *= f; result->g *= f; result->b *= f; result->a = f; if (mi_refraction_dir(&dir, state, 1.0, ior) && mi_trace_refraction (&color, state, &dir) || mi_trace_environment(&color, state, &dir))){ result->r += factor * color.r; result->g += factor * color.g; result->b += factor * color.b; result->a += factor * color.a; } } return(miTRUE); }
Four steps are required for computing the material color in this shader. First, the normal is perturbed by looking up a vector in the vector texture, and using the bump basis vectors to determine the orientation of the perturbation (the lookup always returns an XY vector). The second step loops over all light sources in the light array parameter, adding the contribution of each light according to the Phong equation. In the case of area lights, the light is sampled more than once, until the light sampling function is satisfied.
Finally, reflection and refraction rays are cast if the appropriate parameters are nonzero. In both cases, first the direction vector dir is computed using a built-in function, and a ray is cast in that direction. If either trace function returns miFALSE, indicating that no object was hit, the material's environment map that forms a sphere around the entire scene is evaluated. (Note that if the material has no environment map, the environment map in the state defaults to the environment shader from the camera, if present.) When all computations are finished, the calculated color, including the alpha component, is returned in the result parameter. The shader returns miTRUE indicating that the computation succeeded.
Texture shaders evaluate a texture and return a color, scalar, or vector. Textures can either be procedural, for example evaluating a 3D texture based on noise functions or calling other shaders, or they can do an image lookup. The .mi format provides different texture statements for these two types, one with a function call and one with a texture file name. Refer to the scene description for details.
Texture shaders are not first-class shaders, mental ray never calls one by itself and provides no special support for them. Texture shaders are called exclusively by other shaders. There are four ways of calling a texture shader from a material shader or other shaders: either by simply calling the shader by name like any other C function, or by requesting the value of a shader parametrer using mi_eval (see below), or by using a built-in convenience function like mi_lookup_color_texture, or with a statement like
(see mi_call_shader)
mi_call_shader(result, miSHADER_TEXTURE, state, tag);
The tag argument references the texture function. The texture function is a data structure in the scene database that contains a reference to the C function itself, plus a user parameter block that is passed to the texture shader when it is called. All textures listed in the .mi scene description file are installed as texture shaders callable only with the mi_call_shader method, because only then can user parameters be passed. Although the texture shader could also be called directly with a statement such as
soft_color(result, state, &soft_color_paras);
the caller would have to write the required arguments into the user argument structure soft_color_paras itself; it would not have access to user parameters specified in the .mi file. Also, this call (see shader call tree) does not copy the state, as mi_call_shader does.
Unlike material shaders, texture shaders return a simple color or scalar or other return value. There are no lighting calculations or secondary rays. This greatly simplifies the task of changing a textured surface. For example, a simple texture shader that does a simple, non-antialiased lookup in a texture image could be written as:
(see mi_eval_tag) (see mi_query) (see mi_db_access) (see mi_img_get_color) (see mi_db_unpin)
#include <stdio.h> #include <mi/shader.h> int mytexture_version(void) {return(1);} miBoolean mytexture( register miColor *result, register miState *state, struct image_lookup *paras) { miImg_image *image; int xs, ys; miTag texture; texture = *mi_eval_tag(¶s->texture); image = mi_db_access(texture); mi_query(miQ_IMAGE_WIDTH, state, texture, &xs); mi_query(miQ_IMAGE_HEIGHT, state, texture, &ys); mi_img_get_color(image, result, state->tex.x * xs, state->tex.y * ys); mi_db_unpin(texture); return(miTRUE); }
This shader assumes that the texture coordinate can be taken from state->tex, where the caller (usually a material shader) has stored it, probably by selecting a texture coordinate from state->tex_list. The caller can use mi_query to determine the number of valid textures in state->tex_list. A more complicated shader that better anti-aliases image textures with a simple box filter might look like this:
(see tag type) (see mi_eval_tag) (see mi_db_type) (see mi_call_shader) (see mi_db_access) (see mi_img_get_color) (see mi_db_unpin)
#include <stdio.h> #include <mi/shader.h> int mytexture2_version(void) {return(1);} miBoolean mytexture2( register miColor *result, register miState *state, struct image_lookup *paras) { miTag texture; miImg_image *image; int xs, ys; miColor col00, col01, col10, col11; register int x, y; register miScalar u, v, nu, nv; texture = *mi_eval_tag(¶s->texture); image = mi_db_access(texture); mi_query(miQ_IMAGE_WIDTH, state, texture, &xs); mi_query(miQ_IMAGE_HEIGHT, state, texture, &ys); x = u = state->tex.x * (xs - 1); y = v = state->tex.y * (ys - 1); u -= x; v -= y; nu = 1 - u; nv = 1 - v; mi_img_get_color(image, &col00, x, y); mi_img_get_color(image, &col01, x+1, y); mi_img_get_color(image, &col10, x, y+1); mi_img_get_color(image, &col11, x+1, y+1); result->r = nv * (nu * col00.r + u * col01.r) + v * (nu * col10.r + u * col11.r); result->g = nv * (nu * col00.g + u * col01.g) + v * (nu * col10.g + u * col11.g); result->b = nv * (nu * col00.b + u * col01.b) + v * (nu * col10.b + u * col11.b); result->a = nv * (nu * col00.a + u * col01.a) + v * (nu * col10.a + u * col11.a); mi_db_unpin(texture); return(miTRUE); }
(see texture pyramid) The implementation of the body of this shader is similar to the built-in mi_lookup_color_texture function if called with paras->texture, except that this function also recognizes if the texture is a shader (and calls mi_call_shader in this case), and unlike the example above it supports filtered multi-level pyramid texture filtering. Also, both examples above will crash if any texture coordinate is less than 0 or equal to or greater than 1.
This shader can further be extended by applying texture transformations to state->tex before it is used for the lookup, for example for rotated, scaled, repeating, or cropped textures. The shader may also decide that a scaled-down texture was missed, and return miFALSE. The material shader must then skip this texture if mi_call_shader returns miFALSE; the SOFTIMAGE material shader does this.
Both of the above shaders have user parameters that consist of a single texture. Textures always have type miTag. Image file textures are read in by the translator and provided as a tag of type miSCENE_IMAGE.
A texture shader using higher quality elliptical filtering might look similar to this:
(see tag type) (see mi_lookup_filter_color_texture) (see mi_texture_filter_project) (see mi_texture_filter_transform) (see mi_eval_tag)
#include <stdio.h> #include <mi/shader.h> int mytexture3_version(void) {return(1);} miBoolean mytexture3( register miColor *result, register miState *state, struct image_lookup *paras) { miTag texture; miVector p[3], t[3]; miMatrix ST; texture = *mi_eval_tag(¶s->texture); mi_texture_filter_project(p, t, state, 0.5, paras->space); my_remap_parameter(&t[0], &t[0], state, paras); my_remap_parameter(&t[1], &t[1], state, paras); my_remap_parameter(&t[2], &t[2], state, paras); mi_texture_filter_transform(ST, p, t); return(mi_lookup_filter_color_texture(result, state, texture, ¶s->filter_options, ST)); }
This example assumes that paras contains the definition of the elliptical filter options elliptical filter options and the texture space index. The implementation of the function my_remap_parameter is not given here, it would transform the texture coordinates according to some parameters in paras, for example perform texture replication, rotation or valid range checking. Note that the checking for miFALSE return values of mi_texture_filter_project and mi_texture_filter_transform is missing in this example.
Volume shaders may be attached to the camera or to a material. They modify the color returned from an intersection point to account for the distance the ray traveled through a volume. The most common application for volume shaders is atmospheric fog effects; for example, a simple volume shader may simulate fog by fading the input color to white depending on the ray distance. By definition, the distance dist given in the state is 0.0 and the intersection point is undefined if the ray has infinite length.
(see shader call tree) Volume shaders are normally called in three situations. When a material shader returns, the volume shader that the material shader left in the state->volume variable is called, without copying the state, as if it had been called as the last operation of the material shader. Copying the state is not necessary because the volume shader does not return to the material shader, so it is not necessary to preserve any variables in the state.
Unless the shadow segment mode is in effect, volume shaders are also called when a light shader has returned; in this case the volume shader state->volume is called once for the entire distance from the light source to the illuminated point (i.e., to the point that caused the material shader that sampled the light to be called). In shadow segment (see shadow segments) mode, volume shaders are not called for light rays but for every shadow ray segment from the illuminated point towards the light source. Some volume shaders may decide that they should not apply to light rays; this can be done by returning immediately if the state->type variable is miRAY_LIGHT.
Finally, volume shaders are called after an environment shader was called. Note that if a volume shader is called after the material, light, or other shader, the return value of that other shader is discarded and the return value of the volume shader is used. The reason is that a volume shader can substitute a non-black color even the original shader has given up. Volume shaders return miFALSE if no light can pass through the given volume, and miTRUE if there is a non-black result color.
(see material shader) Material shaders have two separate state variables dealing with volumes, volume and refraction_volume. If the material shader casts a refraction or transparency ray, the tracing function will copy the refraction volume shader, if there is one, to the volume shader after copying the state. This means that the next intersection point finds the refraction volume in state->volume, which effectively means that once the ray has entered an object, that object's interior volume shader is used. However, the material shader is responsible to detect when a refraction ray exits an object, and overwrite state->refraction_volume with an appropriate outside volume shader, such as state->camera->volume, or a volume shader found by following the state->parent links.
Since volume shaders modify a color calculated by a previous material shader, environment shader, or light shader, they differ from these shaders in that they receive an input color in the result argument that they are expected to modify. A very simple fog volume shader could be written as:
(see mi_eval_scalar)
#include <stdio.h> #include <mi/shader.h> int myfog_version(void) {return(1);} miBoolean myfog( register miColor *result, register miState *state, register struct myfog *paras) { register miScalar fade; register miColor *fogcolor; register miScalar max; if (state->type == miRAY_LIGHT) return(miTRUE); max = *mi_eval_scalar(&state->maxdist); fogcolor = mi_eval_scalar(&state->fogcolor); fade = state->dist > max ? 1.0 : state->dist / max; result->r = fade * fogcolor->r + (1-fade) * result->r; result->g = fade * fogcolor->g + (1-fade) * result->g; result->b = fade * fogcolor->b + (1-fade) * result->b; result->a = fade * fogcolor->a + (1-fade) * result->a; return(miTRUE); }
This shader linearly fades the input color to state->fogcolor (probably white) within state->maxdist internal space units. (see atmosphere) Objects more distant are completely lost in fog. The length of the ray to be modified can be found in state->dist, its start point in state->org, and its end point in state->point. This example shader does not apply to light rays, light sources can penetrate fog of any depth because of the miRAY_LIGHT check. In this case, the shader returns miTRUE anyway because the shader did not fail; it merely decided not to apply fog.
If this shader is attached to the camera, the atmosphere surrounding the scene will contain fog. Every state->volume will inherit this camera volume shader, until a refraction or transparency ray is cast. (see shader call tree) The ray will copy the material's volume shader, state->refraction_volume, if there is one, to state->volume, and the ray is now assumed to be in the object. If the material has no volume shader to be copied, the old volume shader will remain in place and will be inherited by subsequent rays.
Some volume shaders employ ray marching techniques to sample lights from empty space, to achieve effects such as visible light beams. Before such a shader calls mi_sample_light, it should store 0 in state->pri to inform mental ray that there is no primitive to illuminate, and to not attempt certain optimizations such as backface elimination. Shaders other than volume shaders may do this too, but must restore pri before returning. Some mi_query modes do not work if pri has been modified.
Environment shaders provide a color for rays that leave the scene entirely, and for rays that would exceed the trace depth limit. Environment shaders are called automatically by mental ray if a ray leaves the scene, or when a ray exceeds the trace depth. It can also be done explicitly by shaders using the mi_trace_environment function:
(see mi_reflection_dir) (see mi_trace_reflection) (see mi_trace_environment)
mi_reflection_dir(&dir, state); if (/* do ray tracing? */) mi_trace_reflection (&color, state, &dir); else mi_trace_environment(&color, state, &dir); /* use the returned color */
Environment shaders, like any other shader, may return miFALSE to inform the caller that the environment lookup failed. If mental ray falls back on calling the environment shader, it returns the value returned by the environment shader, not necessarily miFALSE.
In both the explicit case and the automatic case (when a ray cast by a function call such as mi_trace_reflection leaves the scene without intersecting with any object) mental ray calls the environment shader found in state->environment. In primary rays, this variable is initialized with the global environment shader in the camera (also found in state->camera->environment). (see shader call tree) Subsequent material shaders get the environment defined in the material if present, or the camera environment otherwise. Material shaders never inherit the environment from the parent shader, they always use the environment in the material or the camera. All other types of shaders inherit the environment from the parent shader.
Here is an example environment shader that uses a texture that covers an infinite sphere around the scene:
(see mi_eval_tag)
#include <stdio.h> #include <mi/shader.h> int myenv_version(void) {return(1);} miBoolean myenv( register miColor *result, register miState *state, register struct myenv *paras) { register miScalar theta; miVector coord; theta = fabs(state->dir.z)*HUGE < fabs(state->dir.x) ? state->dir.x > 0 ? 1.5*M_PI : 0.5*M_PI : state->dir.z > 0 ? 1.0*M_PI + atan(state->dir.x / state->dir.z) : 2.0*M_PI + atan(state->dir.x / state->dir.z); if (theta > 2 * M_PI) theta -= 2 * M_PI; coord.x = 1 - theta / (2 * M_PI); coord.y = 0.5 * (state->dir.y + 1.0); coord.z = 0; state->tex = coord; return(mi_call_shader(result, miSHADER_TEXTURE, state, *mi_eval_tag( ¶s->texture))); }
This shader gets a single parameter in its user parameter structure, a miTag for a texture shader. The texture is evaluated by storing the texture coordinate in state->tex and calling the texture shader with mi_call_shader. For a description of texture shaders and how to call them, see the Texture section above.
Light shaders are called from other shaders by sampling a light using the mi_sample_light or mi_trace_light functions, which perform some calculations and then call the given light shader, or directly if a ray hits a (see visible area light) source. mi_sample_light may also request that it is called more than once if an area light source is to be sampled, at locations chosen by the sampling algorithm selected with the -smethod command-line option. For an example for using mi_sample_light, see the section on material shaders above. mi_trace_light performs less exact shading for area lights, and is provided for backwards compatibility only.
The light shader computes the amount of light contributed by the light source to a previous intersection point, stored in state->point. The calculation may be based on the direction state->dir to that point, and the distance state->dist from the light source to that ray. There may also be user parameters that specify directional and distance attenuation. Directional lights have no location; state->dist is undefined in this case.
Light shaders are also responsible for shadow casting. Shadows are computed by finding all objects that are in the path of the light from the light source to the illuminated intersection point. This is done in the light shader by casting ``shadow rays'' after the standard light color computation including attenuation is finished. Shadow rays are cast from the light source back towards the illuminated point (or vice veersa if shadow segment mode is enabled), in the same direction of the light ray. Every time an occluding object is found, that object's shadow shader is called, if it has one, which reduces the amount of light based on the object's transparency and color. If an occluding object is found that has no shadow shader, it is assumed to be opaque, so no light from the light source can reach the illuminated point. For details on shadow shaders, see the next section.
Here is an example for a simple point light that supports no attenuation, but casts shadows:
(see mi_trace_shadow) (see mi_eval_color)
#include <stdio.h> #include <mi/shader.h> int mypoint_version(void) {return(1);} miBoolean mypoint( register miColor *result, register miState *state, register struct mypoint *paras) { *result = *mi_eval_color(¶s->color); return(mi_trace_shadow(result, state)); }
The user parameters are assumed to contain the light color. The shadows are calculated simply by giving the shadow shaders of all occluding objects the chance to reduce the light from the light source by calling mi_trace_shadow. The shader returns miTRUE if some light reaches the illuminated point.
The point light can be turned into a spot light by adding directional attenuation parameters for the inner and outer cones and a spot direction parameter to the user parameters, and change the shader to reduce the light intensity if the illuminated point falls between the inner and outer cones, and turns the light off if it does not fall into the outer cone at all:
(see mi_eval_color) (see mi_eval_scalar) (see mi_eval_vector) (see mi_trace_shadow)
#include <stdio.h> #include <mi/shader.h> int myspot_version(void) {return(1);} miBoolean myspot( register miColor *result, register miState *state, register struct soft_light *paras) { register miScalar d, t; register miScalar inner, outer; *result = *mi_eval_color (¶s->color); inner = *mi_eval_scalar(¶s->inner); outer = *mi_eval_scalar(¶s->outer); d = mi_vector_dot(&state->dir, mi_eval_vector(¶s->direction)); if (d <= 0) return(miFALSE); if (d < outer) return(miFALSE); if (d < inner) { t = (outer - d) / (outer - inner); result->r *= t; result->g *= t; result->b *= t; } return(mi_trace_shadow(result, state)); }
Again, miFALSE is returned if no illumination takes place, and miTRUE otherwise. Note that none of these light shaders takes the normal at the illuminated point into account; the light shader is merely responsible for calculating the amount of light that reaches that point. The material shader (or other shader) that sampled the light must use the dot_nd value returned by mi_sample_light, and its own user parameters such as the diffuse color, to calculate the actual fraction of light reflected by the material.
If the light shader is also to be used for a visible area light source, it needs to check whether state->type is different from miRAY_LIGHT. If so, a ray has intersected the the area light source directly. In this case, no shadow rays have to be checked, and the light color can directly be assigned to result.
As described in the previous section, light shaders may trace shadow rays between the light source and the point to be illuminated. When this ray hits an occluding object, that object's shadow shader is called, if present. (If the object has no shadow shader, the object is assumed to block all light.) Shadow shaders accept an input color that is dimmed according to the transparency and color of the occluding object.
If there is more than one occluding object between the light source and the illuminated point, the order in which the shadow shaders of the occluding objects are called is undefined, unless shadow sorting is turned on. Shadow shaders that rely on being called in the correct order, the one closest to the light source first, should check that state->shadow is 'l', and abort with a fatal error message otherwise, telling the user to turn on shadow sorting. Shadow shaders should also check for shadow segment mode if they depend on shadow ray length or direction. Such requirements should be noted in the shadow shader declaration using a shadow sort statement or similar.
If shadow segments are turned on, shadow shaders must behave rather differently, similar to computing transparency in material shaders. The shadow ray points from the previous intersection (or the illumination point for the initial segment) to the current intersection point. It should set up state->refraction_volume for the next shadow segment, obtain the illumination by calling mi_trace_shadow_seg, and then apply the usual modifications.
Shadow shaders should distinguish two cases: (see shadow segments) (see mi_trace_shadow_seg)
The following shadow shader is a separate shader that attenuates the light that passes through the object based on two user parameters, the diffuse color and the transparency. Material shaders usually also have ambient and specular colors, but the best approach is to pass the diffuse color to shadow shaders because it describes the ``true color'' of the object best. Note that the scene can be arranged such that although the shadow shader is separate from the material shader, (see shader parameters) it still gets a copy of the material shader's user parameters so the shadow shader can access the ``true'' material parameters. In a .mi file, this is done by declaring the shadow shader with no parameters and naming none in the shadow statement in the material definition (just give ()). This sharing of parameters even if the shader itself is not shared can save duplicating a large set of parameters.
(see mi_eval_scalar) (see mi_trace_shadow_seg)
#include <stdio.h> #include <mi/shader.h> int myshadow_version(void) {return(1);} miBoolean myshadow( register miColor *result, register miState *state, register struct myshadow *paras) { register miScalar opacity; register miScalar f, omf; if (state->options->shadow == 's') { set up state->refraction_volume; if (!mi_trace_shadow_seg(result, state)) { result->r = result->g = result->a = 0; } } opacity = 1 - *mi_eval_scalar(¶s->transp); if (opacity < 0.5) { f = 2 * opacity; result->r *= f * diffuse.r; result->g *= f * diffuse.g; result->b *= f * diffuse.b; } else { f = 2 * (opacity - 0.5); omf = 1 - f; result->r *= f + omf * diffuse.r; result->g *= f + omf * diffuse.g; result->b *= f + omf * diffuse.b; } return(result->r != 0 || result->g != 0 || result->b != 0); }
In non-segmented shadow mode, the org variable in the state always contains the position of the light source responsible for casting the shadow rays; the point state variable contains the point on the shadow-casting object. The dist state variable is the distance to the light source (except for directional lights, which have no origin). In shadow segment mode, shadow rays travel in the opposite direction; org is the previous intersection (or the illumination point for the initial segment), and dir points towards the light source.
Photon shaders (see photon shader) are used in the photon tracing phase to compute a photon map which is used to simulate
caustics.
Like shadow shaders, they are specified in materials, and can share the material shader's parameters, or its implementation. Photon shaders need to use the mi_photon_reflection_* and mi_photon_transmission_* functions to reflect or transmit photons. They can also, if desired, use the built-in functions to generate new photon directions: mi_reflection_dir_* and mi_transmission_dir_*.
The following example is a simple photon shader example that handles the interaction between a photon and a simple transmitting material. Notice how the incoming energy is modified before the new photon is transmitted; this is according with the fact that photons move in the opposite direction of the rays in the raytracing phase.
(see mi_eval_scalar) (see mi_refraction_dir) (see mi_photon_transmission_specular)
#include <mi/shader.h> struct ptparm { miScalar trans; /* fraction of light transmitted */ miScalar ior; /* index of refraction */ }; int ptrans_photon_version(void) {return(1);} miBoolean ptrans_photon( miColor *result, miState *state, struct ptparm *paras) { miVector dir; miColor new_energy; miScalar trans; mi_refraction_dir(&dir, state, 1.0, *mi_eval_scalar(¶s->ior)); trans = *mi_eval_scalar(¶s->trans); new_energy.r = result->r * trans; new_energy.g = result->g * trans; new_energy.b = result->b * trans; mi_photon_transmission_specular(&new_energy, state, &dir); return(miTRUE); }
This photon shader only handles transmission and it only generates one photon. It is recommended to produce only one photon in a photon shader. If the photon shader supports several types of reflection or transmission, it should use mi_choose_scatter_type to select only one of these per photon interaction such that only one photon is propagated. If several photons are generated in a photon shader the number of photons in the photon map might grow very quickly without obtaining the necessary quality.
Photons should be stored on materials with a diffuse surface. This is done by the photon shader, it should store the incoming photons using mi_store_photon.
To benefit from the photon map (consisting of the photons stored by the photon shaders), the material shaders should include the illumination from the photon map using mi_compute_irradiance. This is illustrated in the following example of a simple diffuse surface simulated using a material shader and its corresponding photon shader.
pdif_photon is a simple example of a photon shader that stores a photon; pdif is material shader that displays the photon map. Since the photon shader only computes caustics, it does not reflect the photon off the diffuse surface. The material shader does not compute direct illumination from the light sources and only takes the caustic into account.
(see mi_eval_color) (see mi_store_photon) (see mi_compute_irradiance)
#include <mi/shader.h> struct pdparm { miColor diffuse; }; int pdif_photon_version(void) {return(1);} miBoolean pdif_photon( miColor *result, miState *state, struct pdparm *paras) { mi_store_photon(result, state); return(miTRUE); } int pdif_version(void) {return(1);} miBoolean pdif( miColor *result, miState *state, struct pdparm *paras) { miColor irrad, *diffuse; mi_compute_irradiance(&irrad, state); diffuse = mi_eval_color(¶s->diffuse); result->r = irrad.r * diffuse->r; result->g = irrad.g * diffuse->g; result->b = irrad.b * diffuse->b; result->a = 1.0; return(miTRUE); }
In a scene consisting of a glass lens that creates a caustic on a ground plane, four shaders need to be attached:
Photon emitter shaders (see photon emitter) are used in the photon tracing phase to control the emission of photons from the light sources. There exists a number of built-in photon emitters that handle the following types of light sources:
If more complex light sources are needed, it can be necessary to write a specialized shader. For example, this could be necessary for a spot light that should support intensity fading near the edges of the cone.
An example of a simple point light that emits photons uniformly in all directions around the current point is given in the following shaderThis is just a simple example shader --- the builtin point light shader is much more efficient.. The shader receives information about the light source from state->light_instance.
(see mi_mem_allocate) (see mi_query) (see mi_point_transform) (see mi_mem_release) (see mi_vector_normalize) (see mi_photon_light) (see shader initialization) (see shader cleanup)
#include <stdio.h> #include <math.h> #include "shader.h" int point_emitter_version() {return 1;} void point_emitter_init( miState *state, void *p, miBoolean *inst_init_req) { miTag light_tag; miMatrix *T; if (!p) *inst_init_req = miTRUE; else { miVector org, *torg; torg = mi_mem_allocate(sizeof(miVector)); mi_query(miQ_INST_ITEM, state, state->light_instance, &light_tag); mi_query(miQ_INST_LOCAL_TO_GLOBAL, state, state->light_instance, &T); mi_query(miQ_LIGHT_ORIGIN, state, light_tag, &org); mi_point_transform(torg, &org, *T); state->shader->user.p = torg; } } void point_emitter_exit( miState *state, void *p) { if (state->shader->user.p && p) { mi_mem_release(state->shader->user.p); state->shader->user.p = NULL; } } miBoolean point_emitter( miColor *energy, miState *state, void *p) { state->org = *(miVector *)state->shader->user.p; do { state->dir.x = 2.0 * drand48() - 1.0; state->dir.y = 2.0 * drand48() - 1.0; state->dir.z = 2.0 * drand48() - 1.0; } while ((state->dir.x * state->dir.x + state->dir.y * state->dir.y + state->dir.z * state->dir.z) > 1.0); mi_vector_normalize(&state->dir); mi_photon_light(energy, state); return(miTRUE); }
This shader does not take any parameters. Instead it extracts all information about the light source with mi_query. This shader can be attached to a light definition using the photon statement.
(see camera) Lens shaders are called for primary rays from the camera. The camera is normally a simple pinhole camera. A lens shader modifies the origin and direction of a primary ray from the camera. More than one lens shader may be attached to the camera; each modifies the origin and direction calculated by the previous one. By convention, all rays up to and including the one leaving the last lens are called ``primary rays''. The origin and direction input parameters can be found in the state, in the origin and dir variables. The outgoing ray is cast with mi_trace_eye, whose return color may be modified before the shader itself returns. Lens shaders are called recursively; a call to mi_trace_eye will call the next lens shader if there is another one.
Here is a sample lens shader that implements a fish-eye lens:
(see mi_trace_eye) (see mi_vector_to_camera)
#include <stdio.h> #include <mi/shader.h> int fisheye_version(void) {return(1);} miBoolean fisheye( register miColor *result, register miState *state, register void *paras) { register miVector camdir, dir; register miScalar x, y, r, t; mi_vector_to_camera(state, &camdir, &state->dir); t = state->camera->focal / -camdir.z / (state->camera->aperture/2); x = t * camdir.x; y = t * camdir.y * state->camera->aspect; r = x * x + y * y; if (r < 1) { dir.x = camdir.x * r; dir.y = camdir.y * r; dir.z = -sqrt(1 - dir.x*dir.x - dir.y*dir.y); return(mi_trace_eye(result, state, &state->org, &dir)); } else { result->r = result->g = result->b = result->a = 0; return(miFALSE); } }
This shader does not take the image aspect ratio into account, and is not physically correct. It merely bends rays away from the camera axis depending on their angle to the camera axis. Rays that fall outside the circle that touches the image edges are set to black (note that alpha is also set to 0). The rays are bent according to the square of the angle, which approaches the physically correct deflection for small values of . This example shader has no user parameters, which is why the type of the paras parameter is void *.
Be sure to derive the ray origin from state->org, not state->point, which is undefined in lens shaders and can cause incorrect and empty or extremely noisy pictures.
Output shaders are functions that are run after rendering has finished. They modify the resulting image or images. Typical uses are output filters and compositing operations. Output shaders can directly access all rendered frame buffers and make modifications to these frame buffers, but they do not return a result like other shaders do. They still get a result pointer as their first argument, for symmetry with the other types of shaders, but this pointer should not be used.
Until version 2.0.20 of mental ray, output shaders had a different prototype (there was no result pointer argument) and a different state called miOutstate, which contained frame buffers but very few other state variables, so output shaders could not use any shader interface function that required an argument of type miState. For backwards compatibility, these shaders are still supported, but not recommended and not documented in this manual. mental ray distinguishes old-style output shaders by a null or missing version number in the declaration. Old-style output shaders did not support shader initialization and cleanup and the mi_eval family of functions; new ones do.
It is important to specify a non-null shader version number in the declaration of output shaders! If none was specified, mental ray issues a warning and calls the output shader with an miOutstate, which will probably crash it.
All output shaders must be declared like any other type of shader, and the same types of arguments can be declared. This includes textures and lights. Nonprocedural textures can be looked up using functions like mi_lookup_color_texture and mi_query. Lights can also looked up with mi_query. Since rendering has completed, it is not possible to look up procedural textures or to use tracing functions such as mi_sample_light.
Here is a simple output shader that depth-fades the rendered image towards total transparency:
(see mi_img_get_color) (see mi_img_put_color) (see mi_img_get_depth) (see mi_eval_scalar) (see mi_par_aborted)
#include <stdio.h> #include <mi/shader.h> struct out_depthfade { miScalar near; /* no fade closer than this */ miScalar far; /* farther objects disappear */ }; int out_depthfade_version(void) {return(1);} miBoolean out_depthfade( void *result, register miState *state, struct out_depthfade *paras) { register int x, y; miColor color; miScalar depth, fade; miScalar near, far; miImg_image *fb_color, *fb_depth; near = *mi_eval_scalar(¶s->near); far = *mi_eval_scalar(¶s->far); fb_color = state->options->image[miRC_IMAGE_RGBA].p; fb_depth = state->options->image[miRC_IMAGE_Z].p; for (y=0; y < state->camera->y_resolution; y++) if (mi_par_aborted()) break; for (x=0; x<state->camera->x_resolution; x++) { mi_img_get_color(fb_color, &color, x, y); mi_img_get_depth(fb_depth, &depth, x, y); if (depth >= far || depth == 0.0) color.r=color.g=color.b=color.a = 0; else if (depth > near) { fade = (far - depth) / (far - near); color.r *= fade; color.g *= fade; color.b *= fade; color.a *= fade; } mi_img_put_color(fb_color, &color, x, y); } return(miTRUE); }
Note the call to mi_par_aborted, which stops the shader if the user has instructed mental ray to stop whatever it is doing. This call was inserted in the line loop because it should be called periodically, but not too often to avoid slowing it down.
This shader is stored in a file out_depthfade.c and installed in the .mi file with a code statement and a declaration:
code "out_depthfade.c" declare shader "out_depthfade" (scalar "near", scalar "far") version 1 end declare
It is actually more efficient to precompile the shader, store it in a DSO file, anmd use the link statement instead of code. See the beginning of the ``Using and Writing Shaders'' chapter for details. The declaration should appear before the first reference to the shader in a camera statement. Note the non-null version statement. The shader is referenced in an output statement in the camera:
camera "cam" output "rgba,z" "out_depthfade" ("near" 10.0, "far" 100.0) output "pic" "filename.pic" samples 0 1 ... end camera
Note that the output shader statement appears before the output file statement. The output shader must get a chance to change the output image before it is written to the file filename.pic. It is possible to insert another file output statement before the output shader statement; in this case two files would be written, one with and one without depth fading.
Note also that the output shader has a type string "rgba,z". This string tells mental ray to render both an RGBA and a Z (depth) frame buffer. The RGBA buffer would have been rendered anyway because the file output statement requires it, but the depth buffer would not have been rendered without the z in the type string. In this case, all depth values returned by mi_img_get_depth would be 0.0.
By default, depth buffers are not interpolated; instead the min depth within the pixel is used. If the depths should be interpolated, use the output type "rgba,+z". Refer to the Functionality chapter for more information on frame buffer interpolation.
This shader does not anti-alias very well because there is only one depth value per pixel. The shader makes pixels for which a depth of 0.0 is returned totally transparent to fade edges of objects correctly that have no other object behind them. By definition, mi_img_get_depth returns 0.0 for a position x, y if no object was hit at that pixel.
Displacement shaders are called during geometry tessellation if the material of a polygonal or free-form surface object specifies a displace shader. Whenever the tessellator introduces or copies a vertex, the displacement shader is called and expected to return a scalar value that tells the tessellator to move the vertex by this distance along its normal vector. If 0 is returned, the vertex remains unchanged. If the object specifies displacement approximations, curvature introduced by displacement can lead to further subdivision. Here is an example:
(see mi_eval_tag) (see mi_eval_scalar) (see mi_lookup_color_texture)
#include <stdio.h> #include <mi/shader.h> struct mydisplace { miTag tex; miScalar factor; }; int mydisplace_version(void) {return(1);} miBoolean mydisplace( miScalar *result, miState *state, struct mydisplace *paras) { miColor color; mi_lookup_color_texture(&color, state, *mi_eval_tag(¶s->tex), &state->tex_list[0]); *result += (color.r + color.g + color.b) / 3 * *mi_eval_scalar(¶s->factor); return(miTRUE); }
Note that the shader adds its displacement to the result instead of storing it. This allows chaining displacement shaders in shader lists. Shaders in shader lists get called in sequence, each adding its contribution to the result of the previous. mental ray calls the first displacement shader of the list with a result that is initialized to 0.
Geometry shaders are functions that procedurally create geometric objects. They are different from most of the shaders described here because they do not return a color, but a miTag reference to the object or instance group created by the shader.
The geometry shader is called with a miState containing valid fields for camera, options, current shader, and version. The shader parameters are passed as the third argument. The geometry shader is expected to return the tag of the object or instance group it creates.
Here is a simple example for a geometry shader that creates a single triangle. A more robust shader would check the return values of all function calls made here.
Geometry shaders use a special set of shader interface functions to create geometry. All these functions begin with mi_api_.
(see mi_eval_tag)
#include <mi/shader.h> typedef struct { miTag mtl; } GeoTriangle; static void add_vector(miScalar x, miScalar y, miScalar z) { miVector v; v.x = x; v.y = y; v.z = z; mi_api_vector_xyz_add(&v); } int geotriangle_version(void) {return(1);} miBoolean geotriangle( miTag *result, miState *state, GeoTriangle *paras) { mi_api_object_begin(NULL, miTRUE, miTRUE, miTRUE, 0, 0, 0); mi_api_basis_list_clear(); mi_api_object_group_begin(0, 0.0); add_vector(-1., 0., 0.); add_vector( 1., 0., 0.); add_vector( 0., 0., 1.); mi_api_vertex_add(0); mi_api_vertex_add(1); mi_api_vertex_add(2); mi_api_poly_begin_tag(1, *mi_eval_tag(¶s->mtl)); mi_api_poly_index_add(0); mi_api_poly_index_add(1); mi_api_poly_index_add(2); mi_api_poly_end(); mi_api_object_group_end(miTRUE, miFALSE); *result = mi_api_object_end(); return(miTRUE); }
In this example an unnamed object is created with the flags visible, shadow and trace set to miTRUE, a caustic mode set to 0, and a label 0. In the mandatory object group a triangle is constructed with API calls. The material of the triangle is assigned from the shader parameters. The object group definition is finalized with the polygonal information argument set to miTRUE.
The use of flexible, user-defined contour shaders opens up the possibility of many new effects. User-specified functions and shaders determine where the contours should be and compute contour colors and widths. These computations can be based on any information about the geometry, illumination, and materials of the scene, for example Z depth, Z depth difference, orientation, curvature, light source directions, material color, fog, etc.
The list of possible contour shaders is endless. A few additional ideas for contour shaders are: different styles of contour lines such as hand-drawn pencil, chalk, calligraphic pen, brush, wiggly lines, dashed lines, etc; contours between areas in shadow and areas not in shadow; contour parameters depending on motion information; highlights on contours to indicate curving edges.
The following is a detailed description of how contours are computed, and how the contour store function, the contour contrast function, and the contour shaders are called.
Note that contour shaders do not follow the normal shader argument conventions and do not support initialization and exit shaders (see shader initialization) .
A regular color image is created by recursively sampling the scene, i.e.casting rays into the geometry. The same samples are used to generate the contours. With each sample, some user-specified information is stored by the user-specified contour store function, information that is used later for determining the locations, colors, and widths of the contours. The contour store function is called right after the material shader and saves information such as ray intersection point, the normal at the intersection point, object tag, material color, refraction level, etc.
During recursive sampling, the decision whether to take additional samples is based on the comparison of two adjacent samples. New samples are taken if the color contrast is sufficiently large (as defined by the regular contrast and time contrast settings), or if a user-specified contour contrast function returns miTRUE. The contour contrast function can base its decision on any information that was stored by the contour store function for the two sample points. For example, the contour contrast function could return miTRUE if their depths or orientations differ much.
When two samples are at minimum distance (according to the value of the max samples parameter), and their contour contrast is still high (i.e. the contour contrast function returns miTRUE, mental ray assumes that a contour must be drawn between the two samples and calls a user-specified contour shader. The contour shader computes the contour color and width depending on, for example, curvature (orientation difference), Z depth, Z depth difference, material color, or anything other information saved by the contour store function. The contour shader of the object closest to the camera is used. If no contour shader is specified for the material, it does not get a contour. The computed contour point data (contour color, width, etc.) for each point on the contour is stored in a temporary data structure. mental ray merges these contour points into contour lines. (The contours are not yet written into the color frame buffer.)
These contour point data are used by a contour output shader in a postprocessing step. The contour output shader can read the contour endpoints and generate for example an image or a PostScript file.
The contour store function stores various information needed for contour computations. The input is the regular state after ray intersection and the call of material shader, and the color resulting from the material shader call. The output is the information the user deems necessary to compute contours, and the size of this information. There is only one global contour store function for a scene. The contour store function's job is to collect all information that the contour contrast function needs to decide whether to oversample and eventually draw a contour.
To give an example of a contour store function, assume you have decided that to compute contours, you need the ray intersection point, the normal vector, the material, and the color at that point as computed by the material shader (which means the color is not just the color of the material with the given illumination; it also includes reflected and refracted light). Then define the data type MyInfo as
typedef struct MyInfo { miVector point; /* ray intersection point */ miVector normal; /* ray intersection normal */ miTag material; /* material tag */ miColor color; /* from material shader */ } MyInfo;
A contour store function that fills in these fields of MyInfo is
miBoolean my_contour_store_function( void *info_void, int *info_size, miState *state, miColor *color) { struct MyInfo *info = (MyInfo *)info_void; info->point = state->point; info->normal = state->normal; info->material = state->material; info->color = *color; *info_size = sizeof(MyInfo); return(miTRUE); }
mental ray will store a MyInfo data structure with every sample until the contour contrast function is called. The number of stored MyInfo data structures grows up to the number of samples taken in a screen rectangle (typically a 16 *16 pixel block).
The contour contrast function decides where the contours should be. For example, it might decide that there should be a contour when the difference in depth or orientation is too large. The decision is based on the contour information for two points (the information saved by the contour store function), the reflection or refraction level of the two points, the state, and its user parameters. The output is a boolean value indicating whether there should be a contour between the two points. mental ray may decide to draw the contour there by calling the material's contour shader, or take some additional samples to get a better approximation of the contour. There is only one contour contrast function for the scene.
As an example for a user-defined contour contrast function, consider the following function with parameters zdelta and ndelta:
miBoolean my_contour_contrast_function( MyInfo *info1, MyInfo *info2, int level, miState *state, My_Contour_Contrast_Parameters *paras) { /* * Contour if one ray intersected an object and one * ray hit background */ if (!info1 || !info2) return (miTRUE); /* * Contour if sufficiently large difference in depth */ if (fabs(info1->point.z - info2->point.z) > paras->zdelta) return (miTRUE); /* * Contour if sufficiently large change in normal */ if (dot(info1->normal, info2->normal) < cos(paras->ndelta * M_PI/180.0)) return (miTRUE); /* * No contour otherwise */ return (miFALSE); }
The contour shaders compute contour color and width (and optionally object label, material tag, motion, and normal). Their input is two contour information blocks for two adjacent samples on each side of a contour (as stored by the contour store function), the state, and user parameters. Each material can have a separate contour shader. If no contour shader is specified for a material, that material gets no contours.
The contour shader to call is selected based on which object is in front. If the difference in depth is small, the selection is based on which object faces the camera the most. (This is necessary to avoid ``randomly'' mixing contours along an edge between two different materials.)
As an example, consider the following very simple contour shader. It makes the contours white and half a percent (of the minimum of the image X resolution and Y resolution) wide:
miBoolean my_first_contour_shader( miContour_endpoint *result, MyInfo *info_near, MyInfo *info_far, miState *state, void *paras) { result->color.r = result->color.g = result->color.b = result->color.a = 1.0; result->width = 0.5; return (miTRUE); }
The type of the result is
typedef struct { miVector point; miColor color; float width; miVector motion; miVector normal; miTag material; int label; } miContour_endpoint;
The point is automatically filled in, the contour shader does not have to do that. point.x and point.y are in screen coordinates, while point.z is in camera coordinates.
An example of a slightly more complex shader, where the contour color is a factor of the material color and the width is a parameter, is
miBoolean my_colordependent_contour_shader( miContour_endpoint *result, MyInfo *info_near, MyInfo *info_far, miState *state, Factorcolor_Parameters *paras) { /* * Set contour color to a factor times material color. * The opacity color->a is set to 1.0, otherwise the * material will shine through the contour. */ float f = paras->factor; result->color.r = f * info_near->color.r; result->color.g = f * info_near->color.g; result->color.b = f * info_near->color.b; result->color.a = 1.0; /* * Contour width given by a parameter */ result->width = paras->width; return (miTRUE); }
An appropriate data type must be defined for a contour shader. In this case, the data type of the parameters, Factorcolor_Parameters, contains the fields factor and width.
Output shaders are called after the image has been computed. An output shader can use the function mi_get_contour_line to get endpoints of a contour:
miBoolean mi_get_contour_line( miContour_endpoint *p1, miContour_endpoint *p2)
When mi_get_contour_line returns miFALSE, there are no more contour lines.
Here is a very simple output shader that prints the screen-space coordinates of the contour endpoints.
miBoolean my_contour_output_shader( miColor *result, /* unused */ miState *state) /* no parameters */ { miContour_endpoint p1; miContour_endpoint p2; printf("Contour endpoints:\n"); /* Get and write the contour endpoints */ while (mi_get_contour_line(&p1, &p2)) printf("%f %f --- %f %f\n", p1.point.x, p1.point.y, p2.point.x, p2.point.y); return (miTRUE); }
mental ray makes a range of functions available to shaders that can be used to access data, cast rays, looking up images, and performing standard mathematical computations. The functions are grouped by the module that supplies them. The shader writer may also use C library functions, but it is mandatory to include <stdio.h> and <math.h> if printing functions such as fprintf (use mi_info or similar for console debugging messages) or math functions such as sin are used. Not including these headers may abort rendering at runtime, even though the compiler did not complain. All shaders must include the standard mental ray header file, shader.h.
Here is a summary of functions provided by mental ray:
Shader Calls
type name arguments
miBoolean mi_call_shader *result, type, *state, tag miBoolean mi_call_shader_x *result, type, *state, tag, *arg miBoolean mi_call_material *result, *state void * mi_eval *state, *param miBoolean * mi_eval_boolean *param miInteger * mi_eval_integer *param miScalar * mi_eval_scalar *param miVector * mi_eval_vector *param miScalar * mi_eval_transform *param miColor * mi_eval_color *param miTag * mi_eval_tag *param void mi_flush_cache *state
DB
type name arguments
int mi_db_type tag void * mi_db_access tag void mi_db_unpin tag void mi_db_flush tag
RC Functions
type name arguments
miBoolean mi_trace_eye *result, *state, *org, *dir miBoolean mi_trace_reflection *result, *state, *dir miBoolean mi_trace_refraction *result, *state, *dir miBoolean mi_trace_transparent *result, *state miBoolean mi_trace_environment *result, *state, *dir miBoolean mi_trace_light *result, *dir, *nl, *st, i miBoolean mi_sample_light *result, *dir, *nl, *st,i,*s miBoolean mi_trace_shadow *result, *state miBoolean mi_trace_shadow_seg *result, *state miBoolean mi_compute_irradiance *result, *state miBoolean mi_compute_volume_irradiance *result, *state
RC Photon Functions
type name arguments
miBoolean mi_photon_light *energy, *state miBoolean mi_photon_reflection_specular *energy, *state, *dir miBoolean mi_photon_reflection_glossy *energy, *state, *dir miBoolean mi_photon_reflection_diffuse *energy, *state, *dir miBoolean mi_photon_transmission_specular *energy, *state, *dir miBoolean mi_photon_transmission_glossy *energy, *state, *dir miBoolean mi_photon_transmission_diffuse *energy, *state, *dir miBoolean mi_photon_transparent *energy, *state *energy, *state, *dirvoid mi_reflection_dir_specular *dir, *state void mi_reflection_dir_glossy *dir, *state, shiny void mi_reflection_dir_anisglossy *dir, *state, u, v, shiny_u, shiny_v void mi_reflection_dir_diffuse *dir, *state miBoolean mi_transmission_dir_specular *dir, *state, *in, *out miBoolean mi_transmission_dir_glossy *dir, *state, *in, *out, shiny miBoolean mi_transmission_dir_anisglossy *dir, *state, *in, *out, u, v, shiny_u, shiny_v void mi_transmission_dir_diffuse *dir, *state void mi_scattering_dir_diffuse *dir, *state void mi_scattering_dir_directional *dir, *state, directionality miScalar mi_scattering_pathlength *state, density void mi_store_photon *energy, *state
IMG Functions
type name arguments
void mi_img_put_color *image, *color, x, y void mi_img_get_color *image, *color, x, y void mi_img_put_scalar *image, scalar, x, y void mi_img_get_scalar *image, *scalar, x, y void mi_img_put_vector *image, *vector, x, y void mi_img_get_vector *image, *vector, x, y void mi_img_put_depth *image, depth, x, y void mi_img_get_depth *image, *depth, x, y void mi_img_put_normal *image, *normal, x, y void mi_img_get_normal *image, *normal, x, y void mi_img_put_label *image, label, x, y void mi_img_get_label *image, *label, x, y
Math Functions
type name arguments
void mi_vector_neg *r void mi_vector_add *r, *a, *b void mi_vector_sub *r, *a, *b void mi_vector_mul *r, f void mi_vector_div *r, f void mi_vector_prod *r, *a, *b miScalar mi_vector_dot *a, *b miScalar mi_vector_norm *a void mi_vector_normalize *r void mi_vector_min *r, *a, *b void mi_vector_max *r, *a, *b miScalar mi_vector_det *a, *b, *c miScalar mi_vector_dist *a, *b void mi_matrix_ident r miBoolean mi_matrix_invert r, a void mi_matrix_prod r, a, b void mi_matrix_rotate a, x, y, z
void mi_point_transform *r, *v, m void mi_vector_transform *r, *v, m void mi_vector_transform_T *r, *v, m void mi_point_to_world *state, *r, *v void mi_point_to_camera *state, *r, *v void mi_point_to_object *state, *r, *v void mi_point_to_raster *state, *r, *v void mi_point_from_world *state, *r, *v void mi_point_from_camera *state, *r, *v void mi_point_from_object *state, *r, *v void mi_vector_to_world *state, *r, *v void mi_vector_to_camera *state, *r, *v void mi_vector_to_object *state, *r, *v void mi_vector_from_world *state, *r, *v void mi_vector_from_camera *state, *r, *v void mi_vector_from_object *state, *r, *v void mi_normal_to_world *state, *r, *v void mi_normal_to_camera *state, *r, *v void mi_normal_to_object *state, *r, *v void mi_normal_from_world *state, *r, *v void mi_normal_from_camera *state, *r, *v void mi_normal_from_object *state, *r, *v
Auxiliary Functions
type name arguments
double mi_random double mi_srandom seed double mi_erandom seed[3] double mi_par_random *state void mi_reflection_dir dir, state miBoolean mi_refraction_dir dir, state, ior_in, ior_out double mi_fresnel n1, n2, t1, t2 double mi_fresnel_reflection *state, *i, *o double mi_phong_specular spec, *state, *dir void mi_fresnel_specular *ns, *ks, s, *st, *dir, *in, *out double mi_blinn_specular spec, *state, *dir double mi_cooktorr_specular *result, *di, *dr, *n, roughness, *ior double mi_ward_glossy *di, *dr, *n, shiny double mi_ward_anisglossy *di, *dr, *n, *u, *v, shiny_u, shiny_v double mi_schlick_scatter *di, *dr, directionality miRay_type mi_choose_scatter_type *state, transp, *specular, *glossy, *diffuse miInteger mi_choose_lobe *state, r double mi_spline t, n, *ctl double mi_noise_1d p double mi_noise_2d u, v double mi_noise_3d *p double mi_noise_1d_grad p, *g double mi_noise_2d_grad u, v, *gu, *gv double mi_noise_3d_grad *p, *g miBoolean mi_lookup_color_texture *col, *state, tag, *v miBoolean mi_lookup_scalar_texture *scal, *state, tag, *v miBoolean mi_lookup_vector_texture *vec, *state, tag, *v miBoolean mi_lookup_filter_color_texture *col, *state, tag, *paras, ST miBoolean mi_texture_filter_project p[3], t[3], *state, disc_r, space miBoolean mi_texture_filter_transform ST, p[3], t[3] miBoolean mi_tri_vectors *state, wh, nt, **a, **b, **c miBoolean mi_query query, *state, tag, *result, ... miBoolean mi_geoshader_add_result query, *state, tag, *result, ... miBoolean mi_geoshader_tesselate *state, *leaves, source miBoolean mi_geoshader_tesselate_end leaves
Obsolete Auxiliary Functions
These functions are obsolete; use mi_query for future implementations.
type name arguments
void mi_light_info tag, *org, *dir, **paras int mi_global_lights_info **tag void mi_texture_info tag, *xres, *yres, **paras void * mi_shader_info *state int mi_instance_info *state, **paras, **p1, **p2, **p3
Contour Functions
miBoolean mi_get_contour_line *p1, *p2
Memory Allocation
type name arguments
void * mi_mem_allocate size void * mi_mem_reallocate mem, size void mi_mem_release mem void mi_mem_check void mi_mem_dump mod
Thread Parallelism and Semaphores
type name arguments
void mi_init_lock *lock void mi_delete_lock *lock void mi_lock lock void mi_unlock lock int mi_par_localvpu int mi_par_nthreads int mi_par_aborted
Messages and Errors
type name arguments
void mi_fatal *message, ... void mi_error *message, ... void mi_warning *message, ... void mi_info *message, ... void mi_progress *message, ... void mi_debug *message, ... void mi_vdebug *message, ...
Note that many of these functions return double instead of miScalar, or have double parameters. This allows these functions to be used from shaders written in classic (K&R) C, which always promotes floating-point arguments to double.
Shader Calls
Shaders can be called either explicitly, or implicitly by evaluating a shader parameter that may be assigned to the output of another shader.
miBoolean mi_call_shader( miColor * const result, miShader_type type, miState * const state, miTag shader)
miBoolean mi_call_shader_x( miColor * const result, miShader_type type, miState * const state, miTag shader, void *arg)
These functions call the shader specified by the tag shader. The extended version passes the arg parameter as a fourth argument to the shader. The other version passes a NULL pointer as fourth argument to the shader. mi_call_shader_x is slightly more efficient than mi_call_shader.
The tag is normally a texture shader or light shader or some other type of shader found in the calling shader's parameter list. The caller must pass its own state and the shader type, which must be one of miSHADER_LENS, miSHADER_MATERIAL, miSHADER_LIGHT, miSHADER_SHADOW miSHADER_ENVIRONMENT miSHADER_VOLUME, and miSHADER_TEXTURE. The sequence of operations is:
miBoolean mi_call_material( miColor *result, /* result */ miState *state); /* state */
Call the material shader in state->material. This function is intended for smart volume shaders, which get called instead of the material shader. Smart volume shaders decide whether the volume effect they create is less than completely opaque, and only then call the material shader and blend its return color with the volume color. Regular volume shaders are called after the material shader returns, which is less efficient if the volume shader creates opaque effects because the result of the material shader is discarded in this case.
void *mi_eval( miState *state, void *param) miBoolean *mi_eval_boolean( miBoolean *param) miInteger *mi_eval_integer( miInteger *param) miScalar *mi_eval_scalar( miScalar *param) miVector *mi_eval_vector( miVector *param) miScalar *mi_eval_transform( miScalar *param) miColor *mi_eval_color( miColor *param) miTag *mi_eval_tag( miTag *param)
(see shader assignment) Return the value of a shader parameter. If the shader parameter value is a constant, mi_eval returns its argument. If the parameter is assigned to another shader, call that shader and return a pointer to its return value (or the cached return value if it has been called recently). If the parameter is assigned to a phenomenon interface, return a pointer to the value in the interface. The latter two cases let shaders operate in the contexts of shader assignments and phenomena without needing knowledge of the context, which is automatically handled. Shaders should always access their parameters with mi_eval. If parameters are accessed without mi_eval and the parameter is assigned to another shader or an interface, the shader will read garbage.
The typed variants of mi_eval are implemented as macros, for the convenience of shader writers. They cast their argument to a void pointer, call mi_eval, and recast the returned pointer to the appropriate type. They all make the assumption that the shader has a local variable state of type miState * in scope. Experience has shown that the typed variants make source code look much cleaner. Note that there is only one macro mi_eval_tag that can be used for parameters of type shader, color texture, vector texture, scalar texture, light, material, and geometry. All typed variants have been designed to return the same type they accept as an argument.
void mi_flush_cache( miState *state)
Prevents the cache from being used by the next call to mi_eval. Notice that this applies to the entire sub-tree of shaders evaluated by mi_eval. The result cache is still active for shaders which call mi_flush_cache.
This is useful if the code calls a another shader more than once, such as a texture shader to sample a texture at three different locations to compute bump mapping gradients. Although the shader, if called with mi_call_shader or mi_lookup_color_texture, will get called each time, its sub-shaders inside the same phenomenon will not if these subshaders are called as the result of mi_eval. (Remember that mi_eval results are cached.) However, if mi_flush_cache is used before the second and every subsequent shader call, the called shader's mi_eval calls will cause a fresh subshader call.
Database access functions can be used to convert pointers into tags, and to get the type of a tag. The scene database contains only tags and no pointers at all, because pointers are not valid on other hosts. All DB functions are available in all shaders, including output shaders.
int mi_db_type( const miTag tag)
Return the type of a database item, or 0 if the given tag does not exist (this is an error). Valid types that are of interest in shaders are:
miSCENE_FUNCTION Function to call, such as a shading function miSCENE_MATERIAL Material containing shaders and flags miSCENE_LIGHT Light source miSCENE_IMAGE Image in memory
The most important are functions and images, because general-purpose texture shaders need to distinguish procedural and image textures. (see procedural texture) (see image texture) See the texture shader example above.
void *mi_db_access( const miTag tag)
Look up the tag in the database, pin it, and return a pointer to the referenced item. Pinning means that the database item is guaranteed to stay in memory at the same location until the item is explicitly unpinned. Rendering aborts if the given tag does not exist. mi_db_access always returns a valid pointer. If an item is accessed twice, it must be unpinned twice; pinned is a counter, not a flag. The maximum number of pins is 65535.
void mi_db_unpin( const miTag tag)
Every tag that was accessed with mi_db_access must be unpinned with this function when the pointer is no longer needed. Failure to unpin can cause a pin overflow, which aborts rendering. After unpinning, the pointer may not be used any more.
void mi_db_flush( const miTag tag)
Normally, a shader does not use a pointer obtained with mi_db_access to write to a database item. If it does, other hosts on the network may still hold stale copies, which must explicitly be deleted by calling this function. This function must be used with great care; it is an error to flush an item that another shader has pinned. For this reason, it is not generally possible to pass information back and forth between shaders or hosts by writing into database items and then flushing them. Geometry shaders are normally the only place where database entries are written, and there are more powerful function libraries available for geometry shaders that do not require mi_db_flush.
These are the functions supplied by the render module of mental ray, RC. All following trace functions return miTRUE if any subsequent call of a shader returned miTRUE to indicate presence of illumination. Otherwise no illumination is present and miFALSE is returned. (see shader call tree) All trace functions derive the state of the ray to be cast from the given state of the parent ray. This state becomes the ``child state'' that is passed to subsequent shaders that are called by the trace function. The state is always copied, and the given state is not modified except for the state fields label, point, normal, dist and motion. These fields are copied from the child state so that the lens shader can access them in the state modified by mi_trace_eye. After the trace function returns, the caller may examine the child state (but not the grandchild, which is gone if it ever existed). Regular volume shaders get the same state as the previous (material) shader; smart volume shaders get their own state because no material shader is called (and mi_call_material doesn't copy it, as a reversal of the normal order). Note that all point and direction vectors passed as arguments to tracing functions must be in internal space.
miBoolean mi_trace_eye( miColor *result, miState *state, miVector *origin, miVector *direction)
casts an eye ray from origin in direction, or calls the next lens shader. The allowed origin and direction values are restricted when using ray classification. If scanline is turned on and state->scanline is not zero, origin and direction must be the same as in the initial call of mi_trace_eye. Lens shaders may not modify them. Lens shaders that depend on modifying ray origin and direction should be declared with the trace on option. Origin and direction must be given in internal space. This function may be used only in lens shaders. Note that mi_trace_eye stores origin and direction in state->org and state-> dir, respectively, overwriting the previous values.
(see reflection)
miBoolean mi_trace_reflection( miColor *result, miState *state, miVector *direction)
casts a reflection ray from state->point to direction. It returns miFALSE if the trace depth has been exhausted. If no intersection is found, the optional environment shader is called. The direction must be given in internal space. This function may be used only in shaders called during image rendering, but not in displacement, geometry, photon, or output shaders.
miBoolean mi_trace_refraction( miColor *result, miState *state, miVector *direction)
casts a refraction ray from state->point to direction. It returns miFALSE if the trace depth has been exhausted. If no intersection is found, the optional environment shader is called. Before this functions casts the refraction ray, after copying the state, it copies state->refraction_volume to state->volume because the ray is now assumed to be ``inside'' the object, so the volume shader that describes the inside should be used to modify the ray while travelling inside the object. It is the caller's responsibility to set state->refraction_volume to the camera's volume shader state->camera->volume or some other volume shader if it determines that the ray has now left the object. The direction must be given in internal space.
If ray tracing has been disabled, mi_trace_refraction cannot modify the ray direction and operates like mi_trace_transparent. This function may be used only in shaders called during image rendering, but not in displacement, geometry, photon, or output shaders.
miBoolean mi_trace_transparent( miColor *result, miState *state)
does the same as mi_trace_refraction with dir == state->dir (that is, no change in the ray direction) but may be executed faster if the parent ray is an eye ray. It also works when ray tracing is turned off. If the ray direction does not change (because no index of refraction or similar modification is applied), it is more efficient to cast a transparency ray than a refraction ray. Like mi_trace_refraction, this function copies the refraction volume volume shader because the ray is now assumed to be inside the object. This function may be used only in shaders called during image rendering, but not in displacement, geometry, photon, or output shaders.
miBoolean mi_trace_environment( miColor *result, miState *state, miVector *direction)
casts a ray into the environment. The trace depth is not incremented or checked. The environment shader in the state is called to evaluate the returned color. The direction must be given in internal space.
miBoolean mi_sample_light( miColor *result, miVector *dir, miScalar *dot_nl, miState *state, miTag light_inst, miInteger *samples)
(see light shader) casts a light ray from the light source to the intersection point, causing the light source's light shader to be called. The light shader may then calculate shadows by casting a shadow ray to the intersection point. This may cause shadow shaders of occluding objects to be called, and will also cause the volume shader of the state to be called, if there is one. Before the light is sampled, the direction from the current intersection point in the state to the light and the dot product of this direction and the normal in the state are calculated and returned in dir and dot_nl if these pointers are nonzero. The direction is returned in internal space. The light instance to sample must be given in light_inst. samples must point to an integer that is initialized to 0. mi_sample_light must be called in a loop until it returns miFALSE. *samples will then contain the total number of light samples taken; it may be larger than 1 for area light sources.
For every call in the loop, a different dir and dot_nl is returned because the rays go to different points on the area light source. The caller is expected to use these variables, the returned color, and other variables such as diffuse and specular colors from the shader parameters to compute a color. These colors are accumulated until mi_sample_light returns miFALSE and the loop terminates. The caller then divides the accumulated color by the number of samples (*samples) if it is greater than 0, effectively averaging all the intermediate results.
Multiple samples are controlled by the -smethod command-line option. See the section on material shaders for an example. When casting light rays with mi_sample_light, mental ray may check whether the primitive's normal is pointing away from the light and ignore the light in this case. For this reason some shaders, such as ray-marching volume shaders, should assign 0 to state->pri first, and restore it before returning. Some mi_query modes do not work if pri has been modified.
Light instance tags to call this function with can be found either in shader parameters of type light or array light, or in the global light list obtained from mi_query.
miBoolean mi_trace_light( miColor *result, miVector *dir, miScalar *dot_nl, miState *state, miTag light_inst)
(see light shader) is a simpler variation of mi_sample_light that does not keep a sample counter, and is not called in a loop. It is equivalent to mi_sample_light except for area light sources. Area light sources must be sampled multiple times with different directions, which is not supported accurately by this function. This function is provided for backwards compatibility with mental ray 1.9 only, do not use it for new projects.
miBoolean mi_trace_shadow( miColor * const result, miState * const state)
(see shadow ray) (see light shader) (see material shader) computes shadows for the given light ray. This function is normally called from a light shader to take occluding objects that prevent some or all of the light emitted by the light source to reach the illuminated point (whose material shader has probably called the light shader). The result color is modified by the shadow shaders that are called if occluding objects are found.
(see shadow segments)
miBoolean mi_trace_shadow_seg( miColor * const result, miState * const state)
recursively calls the shadow shader for the next shadow segment and returns its result, or the result of the light shader if there is no more shadow intersection. It does nothing if shadow segments are turned off. It is used by shadow shaders only; light shaders always use mi_trace_shadow. See the introduction to shader types and the shadow shader explanation for details on shadow modes.
miBoolean mi_compute_irradiance( miColor *result, miState *state)
mi_compute_irradiance computes the irradiance corresponding to indirect diffuse illumination at the intersection point given in state (state->point). This function is used in material shaders to add indirect illumination such as
caustics.
If state->pri is zero, miFALSE is returned.
miBoolean mi_compute_volume_irradiance( miColor *result, miState *state)mi_compute_volume_irradiance computes the irradiance corresponding to indirect diffuse illumination at the point given in state (state->point). This function is used in volume shaders to add indirect illumination such as
The functions in this section implement photon tracing. They are to be used in photon shaders. If caustics
are enabled, rendering distinguishes two phases: photon tracing and ray tracing. Photon tracing is done first; it sends photons from certain light sources into the scene. When a photon hits an object, the object's photon shader is called, which then gets the opportunity to absorb the photon or use one of the eight mi_photon_* functions to let the photon continue. The six photon functions can be used only in photon shaders. Photon shaders may not use regular ray tracing functions such as mi_trace_reflection.
There are three main categories of photon tracing: specular, glossy, and diffuse. These terms define the distribution of the secondary photons in terms of the amount of scattering. Polished surfaces like chrome or clear glass are specular; unpolished surfaces such as aluminum or lightly frosted glass are glossy, and surfaces with no directional reflection or refraction such as paper are diffuse.
Photon tracing and ray tracing are similar in many respects, and use similar function calls. Although the operation underneath is quite different (tracing photons vs. tracing rays), a table of the operations in each phase shows the similarity as far as the shaders are concerned:
photon tracing ray tracing
emanate from light sources emanate from the camera call photon material shaders call material shaders call photon volume shaders call volume shaders call photon emission shaders call light shaders --- call lens shaders --- call environment shaders mi_photon_light mi_trace_eye mi_reflection_dir_specular mi_reflection_dir mi_reflection_dir_glossy --- mi_reflection_dir_anisglossy --- mi_reflection_dir_diffuse --- mi_transmission_dir_specular mi_refraction_dir mi_transmission_dir_glossy --- mi_transmission_dir_anisglossy --- mi_transmission_dir_diffuse --- mi_scattering_dir_diffuse --- mi_scattering_dir_directional --- mi_scattering_pathlength --- mi_photon_reflection_specular mi_trace_reflection mi_photon_reflection_glossy --- mi_photon_reflection_diffuse --- mi_photon_transmission_specularmi_trace_refraction mi_photon_transmission_glossy --- mi_photon_transmission_diffuse --- mi_photon_transparent mi_trace_transparent mi_photon_volume_scattering --- mi_store_photon mi_compute_irradiance mi_compute_volume_irradiance
Photon shaders, photon volume shaders, and photon emission shaders are optional; if one is missing mental ray uses built-in defaults. The default photon shader absorbs all photons, the default photon volume shader behaves like empty space, and the default photon emission shader behaves like a point light source emitting photons uniformly in all directions.
Photon shaders can use the function mi_choose_scatter_type to select which type of reflection or transmission the photon should undergo next.
In both photon and ray tracing, shaders normally first compute a direction for secondary photons or rays using one of the functions with _dir in the name, and then call the corresponding photon or ray tracing function with the resulting direction.
The functions mi_choose_scatter_type, mi_reflection_dir_*, mi_transmission_dir_*, and mi_scattering_dir_* can also be used in other contexts than photon tracing, but they are listed in the photon tracing column above because they are most often used for that purpose.
The connection between the photon tracing and ray tracing phases is the mi_compute_irradiance function. It allows material shaders (during image rendering) to get the caustic
color using the photons that were stored in the photon tracing phase. The function mi_compute_volume_irradiance is similar, but computes irradiance in a volume using the photons stored in the volume.
This distinction is still valid if ray tracing is disabled and mental ray is reduced to scanline rendering in the second phase. For the purposes of photon tracing, scanline rendering can be considered a ``ray tracing emulation'' mode that achieves similar effects but never actually traces a ray, at the cost of not being able to control the ray direction. For example, if the trace off statement or -trace off command-line option is specified, it is still possible to generate a caustic, but the second phase will be unable to compute raytraced reflections and refractions.
void mi_photon_light( miColor *energy, miState *state)
traces a photon from a light source into the scene. The photon origin is state!>org and the direction is state!>dir. This function should be used only in photon emitting shaders.
(see specular reflection)
miBoolean mi_photon_reflection_specular( miColor *energy, miState *state, miVector *direction)
traces a specularly reflected photon with energy in direction direction. This function may be used only in photon shaders.
(see glossy reflection)
miBoolean mi_photon_reflection_glossy( miColor *energy, miState *state, miVector *direction)
traces a glossily reflected photon with energy in direction direction. This function may be used only in photon shaders.
(see diffuse reflection)
miBoolean mi_photon_reflection_diffuse( miColor *energy, miState *state, miVector *direction)
traces a diffusely reflected photon with energy in direction direction. This function may be used only in photon shaders.
(see specular transmission)
miBoolean mi_photon_transmission_specular( miColor *energy, miState *state, miVector *direction)
traces a specularly transmitted photon with energy in direction direction. This function may be used only in photon shaders.
miBoolean mi_photon_transmission_glossy( miColor *energy, miState *state, miVector *direction)
traces a glossily transmitted photon with energy in direction direction. This function may be used only in photon shaders.
(see diffuse transmission)
miBoolean mi_photon_transmission_diffuse( miColor *energy, miState *state, miVector *direction)
traces a diffusely transmitted photon with energy in direction direction. This function may be used only in photon shaders.
miBoolean mi_photon_transparent( miColor *energy, miState *state)
traces a specularly transmitted photon with energy in the direction indicated by the state (the same direction as the previous direction). This function may be used only in photon shaders.
(see volume scattering)
miBoolean mi_photon_volume_scattering( miColor *energy, /* energy carried by photon */ miState *state, miVector *dir);
traces a photon, scattered by a volume, with energy in direction direction. This function may be used only in photon shaders.
(see specular reflection)
void mi_reflection_dir_specular( miVector *dir, miState *state);
Same as mi_reflection_dir: computes the mirror direction. Created for symmetry with the similar functions for glossy and diffuse reflection.
(see glossy reflection)
void mi_reflection_dir_glossy( miVector *dir, miState *state, double shiny);
Choose a direction near the direction of ideal specular reflection (mirror direction). If shiny is low (for example 5), a wide distribution of directions results; if shiny is high (for example 100), a narrow distribution results.
void mi_reflection_dir_anisglossy( miVector *dir, miState *state, miVector *u, miVector *v, miScalar shiny_u, miScalar shiny_v);
Like mi_reflection_dir_glossy, but with different shinynesses in different directions. The u and v vectors specify the local surface orientation.
void mi_reflection_dir_diffuse( miVector *dir, miState *state)
Choose a direction with a distribution according to Lambert's cosine law for diffuse reflection.
(see specular transmission)
miBoolean mi_transmission_dir_specular( miVector *dir, miState *state, double ior_in, double ior_out);
Same as mi_refraction_dir, since specular transmission is in the refraction direction. Created for symmetry with the similar functions for glossy and diffuse transmission.
(see glossy transmission)
miBoolean mi_transmission_dir_glossy( miVector *dir, miState *state, double ior_in, double ior_out, double shiny);
Choose a direction near the direction of ideal specular transmission (the refraction direction). If shiny is low, a very wide distribution of directions results; if shiny is high a narrow distribution results.
miBoolean mi_transmission_dir_anisglossy( miVector *dir, miState *state, miScalar ior_in, miScalar ior_out, miVector *u, miVector *v, miScalar shiny_u, miScalar shiny_v);
Choose a direction for anisotropic glossy transmission. The u and v vectors specify the local surface orientation.
void mi_transmission_dir_diffuse( miVector *dir, miState *state)
Choose a direction with a distribution according to Lambert's cosine law for diffuse transmission (also known as ``diffuse translucency'').
void mi_scattering_dir_diffuse( miVector *dir, miState *state)Choose a direction with a uniform probability over the whole sphere. Useful for volume scattering.
void mi_scattering_dir_directional( miVector *dir, miState *state miScalar directionality)Choose a direction with a probability determined by directionality. For values between -1 and 0 it models volume backscattering (with -1 being the most directional), for a value of 0 it models diffuse (isotropic) volume scattering, and for values between 0 and 1 it models forward volume scattering.
miScalar mi_scattering_pathlength( miState *state, miScalar k);Based on probability and exponential falloff, select a path length for a photon in a participating medium with density k.
void mi_store_photon( miColor *energy, miState *state)
mi_store_photon stores a photon with the given energy in the photon map at the intersection point given in state state->point). Only photons with non-zero energy are stored. Photons should only be stored at surfaces which have a diffuse component in order to limit the storage costs and reduce the rendering time. This function may be used only in photon shaders.
The IMG module of mental ray provides functions that deal with images. There are functions to read and write image files in various formats, and to (see texture) access in-core frame buffers such as image textures. First, the functions that access frame buffers are listed. These functions are (see texture shader) typically used by texture shaders, which can obtain an image pointer by calling mi_db_access with the image tag as an argument. All these functions do nothing or return defaults if the image pointer is 0, or if the x or y coordinate is out of bounds. They do not check whether the frame buffer has the correct data type. All these functions are available in all shaders, including displacement, geometry, and output shaders.
void mi_img_put_color( miImg_image *image, miColor *color, int x, int y)
Store the color color in the color frame buffer image at coordinate x y, after performing desaturation or color clipping, gamma correction, dithering, and compensating for premultiplication. This function works with 1, 2, or 4 components per pixel, and with 8, 16, or 32 (float) bits per component. The normal range for the R, G, B, and A color components is [0, 1] inclusive.
void mi_img_get_color( miImg_image *image, miColor *color, int x, int y)
This is the reverse function to mi_img_put_color. It returns the color stored in a frame buffer at the specified coordinates. Gamma compensation and premultiplication, if enabled by mi_img_mode, are applied in reverse. The returned color may differ from the original color given to mi_img_put_color because of color clipping and color quantization.
void mi_img_put_scalar ( miImg_image *image, float scalar, int x, int y)
Store the scalar scalar in the scalar frame buffer image at coordinate x y, after clipping to the range [0, 1]. Scalars are stored as 8-bit or 16-bit unsigned values. This function is intended for scalar texture files of type miIMG_S or miIMG_S_16.
void mi_img_get_scalar ( miImg_image *image, float *scalar, int x, int y)
This is the reverse function to mi_img_put_scalar. It returns the scalar stored in a frame buffer at the specified coordinates, converted to a scalar in the range [0, 1]. If the frame buffer pointer is 0, or if the x or y coordinate is out of bounds, the scalar is set to 0.
void mi_img_put_vector ( miImg_image *image, miVector *vector, int x, int y)
Store the X and Y components of the vector vector in the vector frame buffer image at coordinate x y, after clipping to the range [-1, 1]. Vectors are stored as 16-bit signed values. This function is intended for vector texture files of type miIMG_VTA or miIMG_VTS.
void mi_img_get_vector ( miImg_image *image, miVector *vector, int x, int y)
This is the reverse function to mi_img_put_vector. It returns the UV vector stored in a frame buffer at the specified coordinates, with coordinates converted to the range [-1, 1]. The Z component of the vector is always set to 0. If the frame buffer pointer is 0, or if the x or y coordinate is out of bounds, all components are set to 0.
void mi_img_put_depth( miImg_image *image, float depth, int x, int y)
Store the depth value depth in the frame buffer image at the coordinates x y. The depth value is not changed in any way. The standard interpretation of the depth is the (positive) Z distance of objects relative to the camera. mental ray uses this function internally to store -state->point.z (in camera space) if the depth frame buffer is enabled with an appropriate output statement. By convention, the value 0.0 signifies infinite distance.
void mi_img_get_depth( miImg_image *image, float *depth, int x, int y)
Read the depth value to the float pointed to by depth from frame buffer image at the coordinates x y. If the image pointer is 0, or if the x or y coordinate is out of bounds, return the MAX_FLT constant from limits.h.
void mi_img_put_normal( miImg_image *image, miVector *normal, int x, int y)
Store the normal vector normal in the frame buffer image at the coordinates x y. The normal vector is not changed in any way. This function can also be used for the motion vector frame buffer.
void mi_img_get_normal( miImg_image *image, miVector *normal, int x, int y)
Read the normal vector normal from frame buffer image at the coordinates x y. If the image pointer is 0, or if the x or y coordinate is out of bounds, return a null vector. This function can also be used for the motion vector frame buffer.
void mi_img_put_label( miImg_image *image, miUint label, int x, int y)
Store the label value label in the frame buffer image at the coordinates x y. The label value is not changed in any way.
void mi_img_get_label( miImg_image *image, miUint *label, int x, int y)
Read the label value to the unsigned integer pointed to by label from frame buffer image at the coordinates x y. If the image pointer is 0, or if the x or y coordinate is out of bounds, return 0.
Math functions include common vector and matrix operations. More specific rendering functions can be found in the next section, Auxiliary Functions.
void mi_vector_neg( miVector *r)
r := -r
void mi_vector_add( miVector *r, miVector *a, miVector *b)
r := a + b
void mi_vector_sub( miVector *r, miVector *a, miVector *b)
r := a - b
void mi_vector_mul( miVector *r, double f)
r := r *f
void mi_vector_div( miVector *r, double f)
r := r *1 /f (If f is zero, leave r unchanged.)
void mi_vector_prod( miVector *r, miVector *a, miVector *b)
r := a *b
double mi_vector_dot( miVector *a, miVector *b)
a *b
double mi_vector_norm( miVector *a)
|a |
void mi_vector_normalize( miVector *r)
r := r /|r | (If r is a null vector, leave r unchanged.)
void mi_vector_min( miVector *r, miVector *a, miVector *b)
r := ax < bx ? ax : bx ay < by ? ay : by az < bz ? az : bz
void mi_vector_max( miVector *r, miVector *a, miVector *b)
r := ax > bx ? ax : bx ay > by ? ay : by az > bz ? az : bz
double mi_vector_det( miVector *a, miVector *b, miVector *c)
ax bx cx ay by cy az bz cz
double mi_vector_dist( miVector *a, miVector *b)
|a - b|
void mi_matrix_ident( miMatrix r)
R := 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
miBoolean mi_matrix_invert( miMatrix r, miMatrix a)
R := A-1 (Returns miFALSE if the matrix cannot be inverted.)
void mi_matrix_prod( miMatrix r, miMatrix a, miMatrix b)
R := A *B
void mi_matrix_rotate( miMatrix a, const double xrot, const double yrot, const double zrot)
Create a rotation matrix a rotating by xrot, then yrot, then zrot, in radians.
All the following fifteen transformation functions may be called with identical pointers r and v. The vector is transformed in-place in this case. If the matrix m is a null pointer, no transformation is done and v is copied to r. If the result of a transformation is a point in homogeneous coordinates with a w component that is not equal to 1.0, the result vector's x, y, and z components are divided by w. For point transformations, a w component of 1.0 is implicitly appended to the v vector at the vector-matrix multiplication. For vector transformations the w component is implicitly zero.
void mi_point_transform( miVector *r, miVector *v, miMatrix m)
r := v *M
void mi_vector_transform( miVector *r, miVector *v, miMatrix m)
r := v *M Only the upper left 3-by-3 submatrix is used since this is a vector transform. The translation row in the matrix is ignored as w is implicitly assumed to be 0.
void mi_vector_transform_T( miVector *r, miVector *v, miMatrix m)
r := v *MT The transpose of the upper left 3-by-3 submatrix is used for the vector transformation. The w component of v is implicitly assumed to be 0. This function is required for transformation of normals.
void mi_point_to_world( miState * const state, miVector * const r, miVector * const v)
Convert internal point v in the state to world space, r.
void mi_point_to_camera( miState * const state, miVector * const r, miVector * const v)
Convert internal point v in the state to camera space, r.
void mi_point_to_object( miState * const state, miVector * const r, miVector * const v)
Convert internal point v in the state to object space, r. For a light, object space is the space of the light, not the illuminated object.
void mi_point_to_raster( miState * const state, miVector * const r, miVector * const v)
Convert internal point v in the state to 2D raster space, r. Raster space dimension is defined by the camera resolution.
void mi_point_from_world( miState * const state, miVector * const r, miVector * const v)
Convert point v in world space to internal space, r.
void mi_point_from_camera( miState * const state, miVector * const r, miVector * const v)
Convert point in camera space v to internal space, r.
void mi_point_from_object( miState * const state, miVector * const r, miVector * const v)
Convert point v in object space to internal space, r. For a light, object space is the space of the light, not the illuminated object.
void mi_vector_to_world( miState * const state, miVector * const r, miVector * const v)
Convert internal vector v in the state to world space, r. Vector transformations work like point transformations, except that the translation row of the transformation matrix is ignored. The resulting vector is not renormalized. Vector transformations transform normals correctly only if there is no scaling. For correct transformation of normals use the normal transformations described below.
void mi_vector_to_camera( miState * const state, miVector * const r, miVector * const v)
Convert internal vector v in the state to camera space, r.
void mi_vector_to_object( miState * const state, miVector * const r, miVector * const v)
Convert internal vector v in the state to object space, r. For a light, object space is the space of the light, not the illuminated object.
void mi_vector_from_world( miState * const state, miVector * const r, miVector * const v)
Convert vector v in world space to internal space, r.
void mi_vector_from_camera( miState * const state, miVector * const r, miVector * const v)
Convert vector in camera space v to internal space, r.
void mi_vector_from_object( miState * const state, miVector * const r, miVector * const v)
Convert vector v in object space to internal space, r. For a light, object space is the space of the light, not the illuminated object.
void mi_normal_to_world( miState * const state, miVector * const r, miVector * const v)
Convert internal normal v in the state to world space, r. Normal transformations work like vector transformation, except that the transpose of the inverse transformation matrix used. The resulting vector is not renormalized. This ensures that if a vector and a normal are orthogonal in one coordinate system they remain orthogonal after they have been transformed to a different coordinate system. This holds for arbitrary, not necessarily orthogonal transformations. The vector transformations described above transform normals correctly only if there is no scaling.
void mi_normal_to_camera( miState * const state, miVector * const r, miVector * const v)
Convert internal normal v in the state to camera space, r.
void mi_normal_to_object( miState * const state, miVector * const r, miVector * const v)
Convert internal normal v in the state to object space, r. For a light, object space is the space of the light, not the illuminated object.
void mi_normal_from_world( miState * const state, miVector * const r, miVector * const v)
Convert normal v in world space to internal space, r.
void mi_normal_from_camera( miState * const state, miVector * const r, miVector * const v)
Convert normal in camera space v to internal space, r.
void mi_normal_from_object( miState * const state, miVector * const r, miVector * const v)
Convert normal v in object space to internal space, r. For a light, object space is the space of the light, not the illuminated object.
The following functions are provided for support of shaders, to simplify common mathematical operations required in shaders:
miScalar mi_random(void)
Return a random number in the range [0, 1). This is similar to drand48 in the standard Unix libraries, but it is available on all platforms including windows NT, which does not support drand48.
miScalar mi_srandom( long seed)
Begin a new random-number sequence for mi_random. This is equivalent to the standard Unix library function srand48.
miScalar mi_erandom( unsigned short seed[3])
Return a random number in the range [0, 1), based on the given seed. This allows shaders to create private random-number generators by initializing a private seed array to some arbitrary but constant values, and passing it to mi_erandom without the chance of other functions or threads disturbing the sequence by ``stealing'' random numbers (assuming they do not have access to the private seed). This is equivalent to the standard Unix library function erand48.
miScalar mi_par_random( miState *state)
Return a parallel-safe random number in the range [0, 1). Parallel-safe random numbers can be used in parallel rendering to produce consistent results that do not change when the number of hosts or threads changes, or when the execution order changes. All shaders that use this function share the same internal seed value. This function should not be used in geometry shaders, displacement shaders, photon shaders, output shaders, or _init functions of shaders because the initial seed of the random number sequence is undetermined in these cases.
void mi_reflection_dir( miVector *dir, miState *state);
Calculate the reflection direction based on the dir, normal, and normal_geom state variables. The returned direction dir can be passed to mi_trace_reflect. It is returned in internal space.
miBoolean mi_refraction_dir( miVector *dir, miState *state, miScalar ior_in, miScalar ior_out);
Calculate the refraction direction in internal space based on the interior and exterior indices of refraction ior_in and ior_out, and on dir, normal, and normal_geom state variables. The returned direction dir can be passed to mi_trace_refract. Returns miFALSE and leaves *dir undefined in case of total internal reflection.
miScalar mi_fresnel( double ior_in, double ior_out, double factor1, double factor2);
miScalar mi_fresnel_reflection( miState *state, miScalar ior_in, miScalar ior_out);
Call mi_fresnel with parameters appropriate for the given indices of refraction ior_in and ior_out, and for the dot_nd state variable.
miScalar mi_phong_specular( miScalar spec_exp, miState *state, miVector *dir);
Calculate the Phong factor based on the direction of illumination dir, the specular exponent spec_exp, and the state variables normal and dir. The direction must be given in internal space.
void mi_fresnel_specular( miScalar *ns, miScalar *ks, miScalar spec_exp, miState *state, miVector *dir, miScalar ior_in, miScalar iot_out);
Calculate the specular factor ns based on the illumination direction dir, the specular exponent spec_exp, the inside and outside indices of refraction ior_in and ior_out, and the state variables normal and dir. ks is the value returned by mi_fresnel, which is called by mi_fresnel_specular. The direction must be given in internal space.
miBoolean mi_cooktorr_specular( miColor *result, miVector *dir_in, miVector *dir_out, miVector *normal, miScalar roughness, miColor *ior);Calculate the specular color result according to the Cook-Torrance reflection model for incident direction dir_in, reflection direction dir_out at a surface with normal normal. The roughness is the average slope of surface microfacets. ior is the relative index of refraction for three wavelengths (ior_out/ior_in for red, green, and blue). See [Foley 90].
miScalar mi_blinn_specular( miVector *dir_in, miVector *dir_out, miVector *normal, miScalar roughness, miScalar ior);
Like mi_cooktorr_specular, but only for one wavelength. Only one index of refraction ior is needed, and the result is a scalar. See [Foley 90].
miScalar mi_ward_glossy( miVector *dir_in, miVector *dir_out, miVector *normal, miScalar shiny);
(see glossy reflection) Calculate the value of the isotropic Ward glossy reflection model for incident direction dir_in, reflection direction dir_out at a surface with normal normal and shinyness shiny. dir_in should point towards the point, while dir_out and normal should point away from the point. Shiny should be low (for example 5) for wide glossy reflection, and high (for example 100) for narrow glossy (nearly specular) reflection.
miScalar mi_ward_anisglossy( miVector *dir_in, miVector *dir_out, miVector *normal, miVector *u, miVector *v, miScalar shiny_u, miScalar shiny_v);
Calculate the value of the anisotropic Ward glossy reflection model for incident direction dir_in, reflection direction dir_out, surface normal normal, and the anisotropic orientation determined by two perpendicular vectors u and v. The shinyness in the u and v direction is shiny_u and shiny_v, respectively. dir_in should point towards the point, while dir_out and normal should point away from the point. u and v should be perpendicular, and also perpendicular to the normal.
miScalar mi_schlick_scatter( miVector *dir_in, miVector *dir_out, miScalar directionality);
(see volume scattering) Calculate the value of the Schlick volume scattering model for incident direction dir_in, scattering direction dir_out, and directionality directionality. dir_in should point towards the point, while dir_out should point away from the point. directionality must be between -1 and 1. For values between -1 and 0 it models backscattering (with -1 being the most directional), for a value of 0 it models diffuse (isotropic) scattering, and for values between 0 and 1 it models forward scattering.
miRay_type mi_choose_scatter_type( miState *state, float transp, miColor *diffuse, miColor *glossy, miColor *specular)
In photon shaders it is important to generate only one photon per photon interaction. To make this happen this function can be used to select one of several new photon types. The function returns: miPHOTON_REFLECT_SPECULAR, miPHOTON_REFLECT_GLOSSY, miPHOTON_REFLECT_DIFFUSE, miPHOTON_TRANSMIT_SPECULAR, miPHOTON_TRANSMIT_GLOSSY, miPHOTON_TRANSMIT_DIFFUSE or miPHOTON_ABSORBED. The return type is based on incoming coefficients and chosen in such a way that the most important component is chosen most often. Notice that for caustics simulations the diffuse and glossy components are ignored. Also note that the sum of the diffuse, glossy and specular coefficients should be less than or equal to one within each of the red, green, and blue color bands, and that mi_choose_scatter_type modifies the input coefficients and scales them correctly based on the probability of generating a photon of that type. To obtain a correct result the shader must use the modified coefficients in the computations performed after mi_choose_scatter_type has been used. The probability for reflection is 1-transp.
int mi_choose_lobe( miState *state, miScalar r);In a two-lobed volume scattering model, choose lobe 1 or 2 based on the probability r of the first lobe.
miScalar mi_spline( miScalar t, const int n, miScalar * const ctl)
This function calculates a one-dimensional cardinal spline at location t. The t parameter must be in the range 0 ...1. The spline is defined by n control points specified in the array ctl. There must be at least two control points. To calculate multi-dimensional splines, this function must be called once for each dimension. For example, spline can be used three times to interpolate colors smoothly.
miScalar mi_noise_1d( const miScalar p)
Return a one-dimensional coherent noise function of p. All six noise functions compute a Perlin noise function from the given one, two, or three dimension parameters such that the noise changes gradually with changing parameters. The returned values are in the range 0 ...1, with a bell-shaped distribution centered at 0.5 and falling off to both sides. This means that 0.5 is returned most often, and values of less than 0.0 and more than 1.0 are never returned. See [Perlin 85].
miScalar mi_noise_2d( const miScalar u, const miScalar v)
Return a two-dimensional noise function of u, v.
miScalar mi_noise_3d( miVector * const p)
Return a three-dimensional noise function of the vector p. This is probably the most useful noise function; a simple procedural texture shader can be written that converts a copy of the state->point vector to object space, passes it to mi_noise_3d, and assigns the returned value to the red, green, and blue components of the result color. The average feature size of the texture will be approximately one unit in space.
miScalar mi_noise_1d_grad( const miScalar p, miScalar * const g)
Return a one-dimensional noise function of p. The gradient of the computed texture at the location p is assigned to *g. Gradients are not normalized.
miScalar mi_noise_2d_grad( const miScalar u, const miScalar v, miScalar * const gu, miScalar * const gv)
Return a two-dimensional noise function of u, v. The gradient is assigned to *gu and *gv.
miScalar mi_noise_3d_grad( miVector * const p, miVector * const g)
Return a three-dimensional noise function of the vector p. The gradient is assigned to the vector g.
miBoolean mi_lookup_color_texture( miColor *color, miState *state, miTag tag, miVector *coord)
(see texture pyramid) tag is assumed to be a texture as taken from a color texture parameter of a shader. This function checks whether the tag refers to a shader (procedural texture) or an image, depending on which type of color texture statement was used in the .mi file. If tag is a shader, coord is stored in state->tex, the referenced texture shader is called, and its return value is returned. If tag is an image, coord is brought into the range (0..1, 0..1) by removing the integer part, the image is looked up at the resulting 2D coordinate, and miTRUE is returned. If the texture is marked with the filter keyword, multi-level (see texture pyramid) pyramid filtering is performed, a procedure related to classical mip-mapping. In both cases, the color resulting from the lookup is stored in *color.
typedef struct { miScalar eccmax; /* default: 12 */ miScalar max_minor; /* default: 4 */ miScalar circle_radius; /* default: 0.8 */ miBoolean bilinear; /* default: miFALSE */ miScalar spare[10]; /* not used */ } miTexfilter; miBoolean mi_lookup_filter_color_texture( miColor *color, miState *state, miTag tag, miTexfilter *paras, miMatrix ST)
This function provides higher quality filtering than the multi-level pyramid filtering of mi_lookup_color_texture described above using a transformation ST which transforms the coordinate system centered in the current raster position in screen space to texture space.
The utility functions mi_texture_filter_project and mi_texture_filter_transform are provided for helping to calculate this transformation matrix.
This function expects that tag does not refer to a texture shader, that is, it works only on texture images. It will return miFALSE when used with incorrect parameters or if a projection failed.
In the filtering algorithm a circle around the current raster position is projected to an ellipse in texture space, and returns the average color of all texture pixels inside the ellipse. If the texture defined by tag is a pyramid texture (see texture pyramid) , multi-level lookup of the texture pixels is performed to speed up the filtering. In the algorithm the level calculation is based on the minor radius of the ellipse.
The filtering can be controlled by choosing different values in paras. If paras is a null, default values for medium filter quality are used.
The eccmax field in miTexfilter specifies the maximum allowed eccentricity of the ellipse. The eccentricity is defined by the ratio of the major and minor radius. Under severe projective distortion the ellipse can have a very large eccentricity and too many texture pixels would be covered by the elliptical area, resulting in long rendering times. In order to limit the filtering time in these cases, the eccentricity threshold can be specified. If the eccentricity is greater than this value, the minor radius of the ellipse is made larger allowing for a potentially higher level in the texture image pyramid since the level calculation is based on the minor radius.
In this calculation a level is selected where the minor radius of the ellipse has a maximum length of max_minor texture pixels. In each successive level in the image pyramid the radius is divided by two, and there are fewer pixels inside the elliptical area.
The useful range for eccmax is approximately 10 to 30, higher values will result in better antialiasing. For max_minor the range should be 3 to 8. Higher values result in better antialiasing.
The size of the projected screen-space circle can be modified with the circle_radius parameter. Larger values will result in more blurring since the elliptical area is also made larger. Smaller values will increase the aliasing. The useful range for this field is 0.4 to 1.0.
When a magnification area is detected, that is, an area in which the pixels are not compressed (the elliptical area is smaller than a texture pixel), bilinear texture pixel interpolation can be switched on by setting the bilinear field to miTRUE, and resulting in a more blurry image.
In texture filtering example an example is given using this function.
miBoolean mi_lookup_scalar_texture( miScalar *scalar, miState *state, miTag tag, miVector *coord)
This function is equivalent to mi_lookup_color_texture, except that tag is assumed to refer to a scalar texture shader or scalar image, as defined in the .mi file with a scalar texture statement, and a scalar is looked up returned in *scalar.
miBoolean mi_lookup_vector_texture( miVector *vector, miState *state, miTag tag, miVector *coord)
This function is also equivalent to mi_lookup_color_texture, except that tag is assumed to refer to a vector texture shader or vector image, as defined in the .mi file with a vector texture statement, and a vector is looked up returned in *vector.
miBoolean mi_texture_filter_project( miVector p[3], miVector t[3], miState *const state, miScalar disc_r, /*recommended: 0.5*/ miUint space)
This function helps in calculating the screen to texture space transformation matrix required by mi_lookup_filter_color_texture. It projects three points including the current raster position onto the current intersection primitive (state->pri) plane and calculates the texture coordinates in the intersections. If state->pri is null, or if the projection fails, miFALSE is returned.
This function assumes that the current intersected object has a texture space associated and uses space as an index into the texture space. The three screen space points are returned in p, the corresponding two dimensional texture space points are put into t. The first point in the array is always the central raster position (0,0), the others are inside a disc with radius of disc_r from this central position. Note that the points are relative to the central position, absolute screen space coordinates are not used. The value of disc_r should be set to 0.5 in most cases, since the full pixel is projected to texture space. However, when there are highly curved objects in the scene, a smaller value can effectively remove projection problems where the projected points are far outside the hit triangle primitive. In texture filtering example an example is given using this function.
miBoolean mi_texture_filter_transform( miMatrix ST, miVector p[3], miVector t[3])
This function helps in calculating the screen to texture space transformation matrix required by mi_lookup_filter_color_texture. The three two dimensional screen space points in p and the corresponding three two dimensional texture space points form a linear equation which is solved for the transformation matrix. In texture filtering example an example is given using this function. This function returns miFALSE if the linear equation mentioned above has no solution.
void mi_tri_vectors( miState *state, int which, int ntex, miVector **a, miVector **b, miVector **c)
All the information in the state pertains to the interpolated intersection point in a triangle. This function can be used to obtain information about the uninterpolated triangle vertices. Together with the barycentric coordinates in the state, parameters retrieved with mi_tri_vectors may be interpolated differently by the shader. The which argument is a character that controls which triple of vectors is to be retrieved:
miBoolean mi_query( const miQ_type query, miState *const state, miTag tag, void * const result, ...)
Return various pieces of information about the current state of mental ray. query is the request code specifying what piece of information to query; state is the shader state, tag is the tag of the DB element to query, if any, and result is a pointer to the variable to store the results in. Some queries do not require a tag; in this case miNULLTAG must be passed as tag. Some queries require extra arguments in addition to the four standard arguments. mi_query returns miFALSE if the queried value is not available or an unknown type code is used.
The following query codes are available:
query code state tag result purpose
miQ_VERSION ~ ~ char * program version string miQ_DATE ~ ~ char * program compile date miQ_NUM_GLOBAL_LIGHTS ~ ~ int number of global lights miQ_GLOBAL_LIGHTS ~ ~ miTag * array with global light tags miQ_NUM_TEXTURES * ~ int * # of textures in state->tex_list miQ_GEO_LABEL ~ ~ miUint translator-defined triangle label miQ_PRI_BBOX_MIN * ~ miVector bounding box of intersected primitive miQ_PRI_BBOX_MAX * ~ miVector bounding box of intersected primitive miQ_INST_FUNCTION ~ * miTag optional procedural transformation miQ_INST_GLOBAL_TO_LOCAL ~ * miMatrix * instance transformation miQ_INST_LOCAL_TO_GLOBAL ~ * miMatrix * inverse instance transformation miQ_INST_IDENTITY ~ * miBoolean miTRUE if identity transformation miQ_INST_ITEM ~ * miTag instanced scene element miQ_INST_PARENT ~ * miTag leaf instance parent (0 otherwise) miQ_INST_HIDE ~ * miBoolean instance is inactive miQ_INST_VISIBLE ~ * miUint visible to primary rays miQ_INST_TRACE ~ * miUint visible to secondary rays miQ_INST_SHADOW ~ * miUint invisible to shadow rays miQ_INST_CAUSTIC ~ * miUint bitmap: 1=enable casting, 2=enable receiving, 3=disable casting, 4=disable receiving miQ_INST_DECL ~ * miTag inherited parameter declaration miQ_INST_PARAM_SIZE ~ * int inherited parameter size miQ_INST_PARAM ~ * void * inherited parameters miQ_INST_MATERIAL ~ * miTag inherited material miQ_TRANS_INTERNAL_TO_WORLD * ~ miMatrix * internal to world space transformation miQ_TRANS_INTERNAL_TO_CAMERA * ~ miMatrix * internal to camera space transformation miQ_TRANS_INTERNAL_TO_OBJECT * ~ miMatrix * internal to object space transformation miQ_TRANS_WORLD_TO_INTERNAL * ~ miMatrix * world to internal space transformation miQ_TRANS_CAMERA_TO_INTERNAL * ~ miMatrix * camera to internal space transformation miQ_TRANS_OBJECT_TO_INTERNAL * ~ miMatrix * object to internal space transformation miQ_TRANS_WORLD_TO_CAMERA * ~ miMatrix * world to camera space transformation miQ_TRANS_WORLD_TO_OBJECT * ~ miMatrix * internal to world space transformation miQ_TRANS_CAMERA_TO_WORLD * ~ miMatrix * camera to world space transformation miQ_TRANS_OBJECT_TO_WORLD * ~ miMatrix * object to world space transformation miQ_GROUP_MERGE_GROUP ~ * miBoolean miTRUE if merged or connected miQ_GROUP_NKIDS ~ * int number of child instances miQ_GROUP_KID ~ * miTag nth child (n is fifth argument) miQ_OBJ_TYPE ~ * int 0=polygonal, 1=surfaces miQ_OBJ_VISIBLE ~ * miBoolean visible to primary rays miQ_OBJ_TRACE ~ * miBoolean visible to secondary rays miQ_OBJ_SHADOW ~ * miBoolean invisible to shadow rays miQ_OBJ_VIEW_DEPENDENT ~ * miBoolean contains view-dependent surfaces miQ_OBJ_CAUSTIC ~ * miUint 0=none, 1=casts, 2=receives, 3=both miQ_OBJ_LABEL ~ * miUint translator-defined object label
(continued)
query code state tag result purpose
miQ_LIGHT_TYPE ~ * int 0=point, 1=directional, 2=spot miQ_LIGHT_AREA ~ * int 0=none, 1=rectangle, 2=disc, 3=sphere miQ_LIGHT_EXPONENT ~ * miScalar distance falloff, n in 1 /rn) miQ_LIGHT_CAUSTIC_PHOTONS ~ * int number of caustic photons miQ_LIGHT_GLOBAL_PHOTONS ~ * miColor number of globillum photons miQ_LIGHT_ENERGY ~ * miTag energy for caustics and globillum miQ_LIGHT_SHADER ~ * miTag tag of light shader miQ_LIGHT_EMITTER ~ * miVector tag of light photon emitter shader miQ_LIGHT_ORIGIN ~ * miVector light position miQ_LIGHT_DIRECTION ~ * miVector light direction miQ_LIGHT_AREA_R_EDGE_U ~ * miVector U size of rectangular area light miQ_LIGHT_AREA_R_EDGE_V ~ * miVector V size of rectangular area light miQ_LIGHT_AREA_D_NORMAL ~ * miVector normal vector of disc area light miQ_LIGHT_AREA_D_RADIUS ~ * miScalar radius of disc area light miQ_LIGHT_AREA_S_RADIUS ~ * miScalar radius of spherical area light miQ_LIGHT_AREA_C_RADIUS ~ * miScalar radius of cylinder area light miQ_LIGHT_AREA_C_AXIS ~ * miVector axis of cylinder area light miQ_LIGHT_AREA_SAMPLES_U ~ * int number of samples in U direction miQ_LIGHT_AREA_SAMPLES_V ~ * int number of samples in V direction miQ_LIGHT_SPREAD ~ * miScalar outer cone angle of spot light miQ_LIGHT_USE_SHADOWMAP ~ * miBoolean light has a shadow map miQ_LIGHT_LABEL ~ * int light label miQ_MTL_OPAQUE ~ * miBoolean material is opaque to shadow rays miQ_MTL_SHADER ~ * miTag material shader miQ_MTL_DISPLACE ~ * miTag displacement shader miQ_MTL_SHADOW ~ * miTag shadow shader miQ_MTL_VOLUME ~ * miTag volume shader miQ_MTL_ENVIRONMENT ~ * miTag environment shader miQ_MTL_CONTOUR ~ * miTag contour shader miQ_MTL_PHOTON ~ * miTag photon shader miQ_MTL_PHOTONVOL ~ * miTag photon volume shader miQ_FUNC_USERPTR ~ * void * user pointer in function instance miQ_FUNC_LOCK ~ * miLock * local function instance lock miQ_FUNC_TYPE ~ * int 0=C/C++, 1=phen., 2=output file miQ_FUNC_DECL ~ * miTag tag of function declaration miQ_FUNC_NEXT ~ * miTag next function in function list miQ_FUNC_INDIRECT ~ * miTag take params from this function miQ_FUNC_PARAM_SIZE ~ * int size of function parameters in bytes miQ_FUNC_RESULT_SIZE ~ * int function result size in bytes miQ_FUNC_PARAM ~ * void * function parameters miQ_FUNC_* * ~ see above same for state->shader miQ_DECL_LOCK ~ * miLock * shared by all function instances miQ_DECL_TYPE ~ * int miTYPE_* result type miQ_DECL_RESULT_SIZE ~ * int result size, 4 unless struct miQ_DECL_NAME ~ * char * function name miQ_DECL_PARAM ~ * char * ascii-encoded parameter declaration miQ_DECL_VERSION ~ * int shader declaration version miQ_DECL_* * ~ see above same for state->shader
(continued)
query code state tag result purpose
miQ_IMAGE_WIDTH ~ * int width of image in pixels miQ_IMAGE_HEIGHT ~ * int height of image in pixels miQ_IMAGE_BITS ~ * int num of bits per component (8,16,32) miQ_IMAGE_COMP ~ * int num of components (1,2,3,4) miQ_IMAGE_FILTER ~ * miBoolean image allows filtering miQ_IMAGE_DESATURATE ~ ~ miBoolean color frame buffer desaturation mode miQ_IMAGE_DITHER ~ ~ miBoolean color frame buffer dithering mode miQ_IMAGE_NOPREMULT ~ ~ miBoolean color frame buffer premultiplication mode miQ_IMAGE_GAMMA ~ ~ double color frame buffer gamma factor
The ``~'' symbol in the state column indicates that the state is not used. The ``~'' symbol in the tag column means that miNULLTAG must be passed. Some queries can specify a state instead of a tag. Their query codes are indicated with a *, which stands for any of the preceding codes whose names begin with the same prefix; in this case mi_query will take the current shader (state->shader) instead of an arbitrary tag. This is slightly faster than passing a tag.
The result type in the table indicates the type of the variable that mi_query accepts a pointer to: to obtain an integer result from mi_query (the table lists an ``int''), a pointer to an integer must be passed as the fourth argument (``int *''). For mi_query, ``function'' is synonymous with ``shader''.
The result of the miQ_INST_VISIBLE, miQ_INST_SHADOW, miQ_INST_TRACE, and miQ_INST_CAUSTIC queries depend on whether a scene DAG or leaf instance tag is passed. A scene DAG instance contains the flags specified by the scene description language when the instance was created; a leaf instance contains the effective instance flags for rendering, that is, with instance inheritance and object flags taken into account. The miQ_INST_VISIBLE etc. modes should be used instead of the miQ_OBJ_VISIBLE etc. modes because they return the same modes that mental ray uses when rendering.
The result vectors of the miQ_LIGHT_ORIGIN and miQ_LIGHT_DIRECTION queries are defined in internal space if the light instance tag is passed, otherwise the vectors are defined in local space.
The query codes miQ_NUM_TEXTURES and miQ_GEO_LABEL may only be used if state->pri has not been modified by the shader or calling shader. Ray-marching volume shaders sometimes clear this state variable. Both are not supported in displacement shaders. miQ_GEO_LABEL also returns miFALSE if the object is not marked tagged (see tagged flag) and no polygon/surface labels exist.
Note that a return type of miMatrix * means that the address of a pointer must be passed, not the address of a matrix. This reduces the number of bytes that mi_query has to copy from 64 (sixteen floats) to only four (or eight, on some CPU architectures).
miBoolean mi_geoshader_add_result( miTag *result, const miTag item)
This function should be called from geometry shaders for adding a scene entity to the result. If result or item are null, miFALSE is returned. If result refers to a null tag, an instance group is created and returned in result. If result is non-null but does not refer to an instance group, an instance group is created, the result entity is put into this group and the group is returned in result. Now that this function has enforced that an instance group entity is always returned, the item entity is put into the returned group.
miBoolean mi_geoshader_tesselate( miState *state, miTag *leaves, miTag source)
This function should be called from geometry shaders only. It builds a list of instances that describes the object, object group, or instance source. If source contains more than one object, there will be multiple instances in the leaves list. The leaves argument is set to the tag of the first instance; the others are chained to one another with the next field in each instance. The last instance in the list has a null next field.
The tesselation function also tesselates the geometry described by each instance, and attaches the triangles resulting from the tesselation to the boxes field of the instance. Triangles are stored in boxes, which are data structures consisting of a header, a vector list, a vertex list, and a triangle list. Boxes have a maximum size; if an object does not fit into one box more are generated and chained using the next_box tag in each box. See the Geometry Shaders chapter for more detail, and for an example using mi_geoshader_tesselate.
miBoolean mi_geoshader_tesselate\_end( miTag leaves)
Release all memory allocated by mi_geoshader_tesselate. This function releases all instances and boxes attached to the instance chain leaves, which was created by mi_geoshader_tesselate. Multiple instance lists can exist at the same time; it is not necessary to release one before creating the next but since memory demands may be substantial if the tesselated geometry is large, it is recommended to not keep instance lists longer than necessary.
miBoolean mi_inclusive_lightlist( int *n_lights, miTag **lights, miState *state)n_lights points to an integer containing the original list size, and lights points to a pointer to the original list. After the function returns, the integer holds the new list length, and the pointer points to a new list. Both the integer and the pointer should be on the stack and should be initialized from the shader parameters using mi_eval to avoid overwriting the actual shader parameters, which should remain intact for the next shader call.
The new list is a copy of the global light list (see mi_query) with only those lights in the passed original list included. If a light in the original list is multiply instanced, it is included more than once.
miBoolean mi_exclusive_lightlist( int *n_lights, miTag **lights, miState *state)n_lights points to an integer containing the original list size, and lights points to a pointer to the original list. After the function returns, the integer holds the new list length, and the pointer points to a new list. Both the integer and the pointer should be on the stack and should be initialized from the shader parameters using mi_eval to avoid overwriting the actual shader parameters, which should remain intact for the next shader call.
The new list is a copy of the global light list (see mi_query) with the lights in the original list removed. If a light in the original list is multiply instanced, it is included/ excluded more than once.
These functions are obsolete; use mi_query for future implementations.
void mi_light_info( miTag tag, miVector *org, miVector *dir, void **paras)
tag is assumed to be a light source instance as found in a light parameter of a shader, or returned by mi_global_lights_info. It is looked up, and its origin (location in internal space) is stored in *org, and its direction (also in internal space) is stored in *dir. If tag refers to a light source instead of the instance, both vectors are defined in local space. Since light sources can only have one or the other but not both, the unused vector is set to a null vector. This can be used to distinguish directional (infinite) light sources; their org vector is set to (0, 0, 0). The paras pointer is set to the shader parameters of the referenced light shader; if properly cast by the caller, it can extract information such as whether a non-directional light source is a point or a spot light, and its color and attenuation parameters. (mental ray considers a spot light to be a point light with directional attenuation.) Any of the three pointers org, dir, and paras can be a null pointer.
int mi_global_lights_info( miTag **tag)
Returns the address of an array containing all global light leaf instances. The tags in this array can be used like light shader parameters, for calls to mi_sample_light and mi_light_info. One important difference between shader light parameters and global lights is that global lights are the result of instancing, so if a light is transformed and/or multiply instanced it will appear transformed and/or more than once in the global light list, while shader parameters will be accessed as stored in the scene with no instancing applied. It is recommended that translators that support multiple light instancing use material shaders that use the global light list instead of a light array in the shader parameter list.
void mi_texture_info( miTag tag, int *xres, int *yres, void **paras)
tag is assumed to be a texture as found in a texture parameter of a shader. If tag refers to a procedural texture shader, * xres and *yres are set to 0 and *paras is set to the shader parameters of the texture shader. If tag is an image texture, *xres and *yres are set to the image resolution in pixels, and *paras is set to 0. Any of the three pointers can be a null pointer.
(see shader initialization)
void *mi_shader_info( miState *state)
Returns a pointer to the user pointer of the current shader in the state, state->shader. This is useful for shader that allocate memory during startup (in the instance init shader) and need a place to store the pointer to the initialized data in a place where shader instances can pick it up. A unique user pointer is returned for each shader instance (each unique function/parameters pair).
int mi_instance_info( miState *state, void ** const paraspp, void ** const spare1, void ** const spare2, void ** const spare3)
Returns the size of and a pointer to the inherited instance parameters. Instance parameters are attached to the instances of the scene DAG, and are combined in a scene DAG traversal step during scene preprocessing, before rendering begins. The structure of the inherited data is determined by the inheritance function, not by the shader, and is generally under control of the translator that generated the scene. Typically, all instances contain either no parameters at all (size 0), or they all use the same data structure. The instance being checked is state-> instance. The spare pointers must be passed as 0; they are reserved for future use.
mental ray's memory allocation functions replace the standard malloc packages found on most systems. They have built-in functions for memory leak tracing and consistency checks, and handle error automatically.
void *mi_mem_allocate( const int size)
Accepts one argument specifying the size of the memory to allocate. A pointer to the allocated memory is returned. If the allocation fails, an error is reported automatically, and mental ray is aborted. This call is guaranteed to return a valid pointer, or not to return at all. The allocated memory is zeroed.
void *mi_mem_reallocate( void * const mem, const int size)
Change the size of an allocated block of memory. There are two arguments: a pointer to the old block of memory, and the requested new size of that block. A pointer to the new block is returned, which may be different from the pointer to the old block. If the old pointer was a null pointer, mi_mem_reallocate behaves like mi_mem_allocate. If the new size is zero, mi_mem_reallocate behaves like mi_mem_release, and returns a null pointer. If there is an allocation error, an error is reported and raylib is aborted. Like mi_mem_alloc, mi_mem_reallocate never returns if the re-allocation fails. If the block grows, the extra bytes are zeroed.
void mi_mem_release( void * const mem)
Frees a block of memory. There is one argument: the address of the block. If a null pointer is passed, nothing is done. There is no return value.
Thread Parallelism and Semaphores
In addition to network parallelism, mental ray also supports shared memory parallelism through threads. Network parallelism is a form of distributed memory parallelism where processes cooperate by exchanging messages. Messages are used to exchange data as well as to synchronize. With shared memory data can easily be exchanged, a process must only access the common memory to do so. A different mechanism has to be used for synchronization. This is usually done by locking. Basically what has to be done is one process has to tell the other that it is waiting to access data, and another process can signal that it has finished working with it, so that any other process may now access it.
By default threads are used on shared memory multiprocessor machines. Threads are sometimes also called lightweight processes. Threads behave like processes running on a common shared memory.
Since memory is shared between two threads, both can write to memory at the same time. It can also happen that one thread writes while another reads the same memory. Both these cases can lead to surprising unwanted results. Therefore -- to guard against these surprises -- when using threads certain precautions have to be observed. Care has to be taken when using heap memory such as global or static data, as any thread may potentially modify it. To prevent corrupting any data (or reading corrupted data), locking must be used when it is not otherwise guaranteed that concurrent accesses will not occur. The stack, however, is always safe because every thread has its own stack that is not shared with any other thread.
In addition to making sure that write accesses to data are performed when no other thread accesses the data, it is important to use only so-called concurrency safe libraries and calls. If a call to a nonreentrant function is done, locking should be used. A function is called reentrant if it can be executed by multiple threads at the same time without adverse effects. (Reentrancy and concurrency safety are related, but the terms stem from different historical contexts, and reentrancy also implies the ability to recurse safely.) Details and examples are explained below.
For example, static data on a shared memory multiprocessor can be modified by more than one processor at a time. Consider this test:
if (!is_init) { is_init = miTRUE; initialize(); }
This does not guarantee that initialize is called only once. The reason is that all threads share the is_init flag, so two threads may simultaneously examine the flag. Both will find that it has not been set, and enter the if body. Next, both will set the flag to miTRUE, and then both will call the initialize function. This situation is called a race condition. The example is contrived because initialization and termination should be done with init and exit functions as described in the next section, but this problem can occur with any heap variable. In general, all threads on a host share all data except local auto variables on the stack.
The behavior described above could also occur if more than one thread is used on a single processor, but by default mental ray does not create more threads than processors are available.
There are two methods for guarding against race conditions. One is to guarantee that only one thread executes certain code at a time. Such code surrounded by lock and unlock operations is called a critical section. Code inside of critical sections may access global or static data or call any function that does so (as long as all is protected by the same lock). The lock used in this example is assumed to have been created and initialized with a call to mi_init_lock before it used here. (See below how locks are initialized.) Here is an example of how a critical section may be used:
miLock lock; mi_lock(lock); if (!is_init) { /* critical section */ is_init = miTRUE; initialize(); } mi_unlock(lock);
The other method is to use separate variables for each thread. This is done by allocating an array with one entry for each thread, and indexing this array with the current thread number. Allocation is done in the shader's initialization function (which has the same name as the shader with _init appended). No locking is required because it is called only once. The termination function (which also has the same name but with _exit appended) must release the array.
mental ray provides two locks for general use: state->global_lock is a lock shared by all threads and all shaders. No two critical sections protected by this lock can execute simultaneously on this host. The second is state->shader->lock, which is local to all instances of the current shader. The lock is tied to the shader, not the particular call with particular shader parameters. Every shader in mental ray, built-in or dynamically linked, has exactly one such lock. mental ray internally uses this lock to guarantee that the initialization and termination functions of a shader do not execute concurrently. Therefore, it need not (and should not) be used in these functions.
The relevant functions provided by the parallelism modules are:
void mi_init_lock( miLock * const lock)
Before a lock can be used by one of the other locking functions, it must be initialized with this function. Note that the lock variable must be static or global. Shaders will normally use this function in their _init function. Shaders should not initialize (or delete) state->global_lock or the local shader lock; they are pre-initialized by mental ray.
void mi_delete_lock( miLock * const lock)
Destroy a lock. This should be done when it is no longer needed. The code should use lock and immediately unlock the lock first to make sure that no other thread is in or waiting for a critical section protected by this lock. Shaders will normally use this function in their _exit function. Do not delete the predefined locks.
void mi_lock( const miLock lock)
Check if any other code holds the lock. If so, block; otherwise set the lock and proceed. This is done in a parallel-safe way so only one critical section locked can execute at a time. Note that locking the same lock twice in a row without anyone unlocking it will block the thread forever, effectively freezing mental ray, because the second lock can never succeed.
void mi_unlock( const miLock lock)
Release a lock. If another thread was blocked attempting to set the lock, it can proceed now. Locks and unlocks must always be paired, and the code between locking and unlocking must be as short and as fast as possible to avoid defeating parallelism. There is no fairness guarantee that ensures that the thread that has been blocked for the longest time is unblocked first.
miVpu mi_par_localvpu(void) int miTHREAD(miVpu vpu) int miHOST(miVpu vpu) miVpu miVPU(int host, int thread)
The term VPU stands for Virtual Processing Unit. All threads on the network have a unique VPU number. mi_par_localvpu returns the VPU number of the VPU this thread is running on. VPUs are a concatenation of the host number and the thread number, both numbered from 0 to the number of hosts or threads, respectively, minus 1. (Future versions of mental ray may use noncontiguous host numbers, but not noncontiguous thread numbers.)
The miTHREAD macro extracts a thread number from a VPU, and the miHOST macro extracts the host number from a VPU. Thread 0 is called the master thread; host 0 is called the master host. Thread 0 on host 0 is normally running the translator that controls the entire operation. The miVPU macro puts a host and thread number together to form a VPU number. The mi_par_localvpu function returns the VPU of the current thread on the local host.
In a shader the fastest way of finding the current thread number is state->thread.
int mi_par_nthreads(void)
Returns the number of threads on the local host. This is normally 1 on a single-processor system. This number can be used to allocate an array of per-thread variables in the shader initialization code. The array can then be indexed by the shader with miTHREAD(mi_par_localvpu()).
int mi_par_aborted(void)
Return a nonzero value if mental ray has been aborted, and the shader should stop what it is doing, clean up, and return. This is only of interest in output shaders because they can run for a long time. This allows the user to press on an abort button, which causes calls to mi_pat_aborted to return nonzero, and have the shader return as soon as possible. For example, the shader might call this function in its scanline loop (not for every pixel to avoid slowing it down), and skip the remaining lines. The shader must still clean up, for example releasing memory that it has allocated.
Shaders may print messages and errors. They are printed in the same format as rendering (RC) messages. Options given to the translator determine which messages are printed and which are suppressed. All message routines have the same parameters as printf(3). All append a newline to the message. Messages are printed in the form
RC host.thread level: message
with the module name, e.g. RC, the host number host if available, the thread number thread with a leading dot if available, the message type level (fatal, error, warning etc), and the message given in the function call. In a networked environment, the messages of servers are usually transferred to the master rendering host and printed in the order they were received. Messages resulting from connection problems and those that were dropped to avoid a network overload may be found in a local file ( /tmp/raylib.log). Newlines in the message are replaced with blanks.
void mi_fatal( const char * const message, ...)
An unrecoverable error has occurred. Unlike all others, this call will not return; it will attempt to recover mental ray and return to the top-level translator. Recovering may involve aborting all operations in progress and clearing the entire database. Fatal messages can be suppressed, but mental ray is always re-initialized.
void mi_error( const char * const message, ...)
An unrecoverable error has occurred. This call returns; the caller must abort the current operation gracefully and return.
void mi_warning( const char * const message, ...)
A recoverable error occurred. The current operation proceeds.
void mi_info( const char * const message, ...)
Prints information about the current operation, such as the number of triangles and timing information. Infos should be used sparingly; do not print information for every intersection point or shader call except during debugging.
void mi_progress( const char * const message, ...)
Prints progress reports, such as rendering percentages.
void mi_debug( const char * const message, ...)
Prints debugging information useful only for shader development.
void mi_vdebug( const char * const message, ...)
Prints more debugging information useful only for shader development. Messages that are likely to be useful only in rare circumstances, or that generate a very large number of lines should be printed with this function.
Note that all these functions incur some runtime overhead even if the verbosity level is set such that no message is printed. It is usually a good idea to enclose shader debugging messages that occur frequently in ifdef DEBUG statements to make sure they are omitted in production versions of the shader.
mental ray provides a way to define initialization and cleanup functions for each user defined function. Many shaders need to perform operations such as initializing color tables or allocating arrays before they are called for the first time. They may also need to do cleanup operations after rendering has finished, for operations like releasing storage to prevent memory leaks.
Before a shader is called for the first time, ray checks if a function of the same name with _init appended exists. If so, it assumes that this is an initialization function and calls it once before the first call of the shader. The state passed to the initialization function is the same as passed to the first call of the actual shader to be initialized. Note that the order of shader calls is unpredictable because the order of pixel samples is unpredictable, so the initialization function should not rely on sample-specific state variables such as state->point.
The initialization function has the option of requesting shader instance initializations by setting the boolean variable its third argument points to to miTRUE. A shader instance is a unique pair of shader and shader parameters. For example, if the shader soft_material is used in two different materials it is said to have two different instances (even if the parameter values happen to be equal).
When rendering has finished, mental ray checks for each user provided shader that was called whether a function of the same name with _exit appended exists. If yes, it assumes that this is a cleanup function and calls it once. For example, if a shader myshader exists, the functions myshader_init and myshader_exit are called for initialization and cleanup if they exist.
The functions are assumed to have the following type:
void myshader_init(miState *state, void *paras, miBoolean *inst_init_req); void myshader_exit(miState *state, void *paras);
Here is an example for init and exit shaders for a shader named myshader. When myshader is about to be used for the first time in a frame, the calling order is:
void myshader_init( /* must end with "_init" */ miState *state, struct myshader *paras, /* valid for inst inits */ miBoolean *inst_req) /* for inst init request */ { if (!paras) { /* main shader init */ *inst_req = miTRUE; /* want inst inits too */ ... } else { /* shader instance init */ /* just an example: */ state->shader->user.p = mi_mem_allocate(...); ... } } void myshader_exit( /* must end with "_exit" */ miState *state, struct myshader *paras) /* valid for inst inits */ { if (!paras) { /* main shader exit */ ... /* no further inst exits * will occur */ } else { /* shader instance exit */ /* just an example: */ mi_mem_release(state->shader->user.p); ... } }
Note that there will generally be many instance init/exits (if enabled), but only one shader init/exit. If an init/exit shader isn't available, it is not called; this is not an error. Initialization and cleanup are done on every host where the function was used, but only once on shared memory parallel machines. They are done for each frame separately. Init and exit shaders will never run concurrently with each other or the actual shader on shared-memory multiprocessor machines; mental ray takes care of the appropriate locking using the shader lock. For this reason, init and exit shaders should never lock the shader lock (obtainable with the miQ_FUNC_LOCK mode of the mi_query function) because that would deadlock mental ray.
Trace functions are functions provided by mental ray that allow a shader to cast a ray into the scene, most of them using standard ray tracing. Not all types of tracing functions can be used in all types of shaders. Conversely, many trace functions cause shaders to be called. This chapter lists these interdependencies.
The following list shows which shaders are called from which trace functions. ~means no, *means yes. Displacement, contour, and photon shaders are never called by any of the functions, and may not call any of them.
(see mi_trace_eye) (see mi_trace_reflection) (see mi_trace_refraction) (see mi_trace_transparent) (see mi_trace_environment) (see mi_trace_light) (see mi_sample_light) (see mi_trace_shadow) (see mi_trace_shadow_seg)
lens environ shadow material light volume
mi_trace_eye * * * ~ ~ * mi_trace_reflection ~ * * ~ ~ * mi_trace_refraction ~ * * ~ ~ * mi_trace_transparent ~ * * ~ ~ * mi_trace_environment ~ ~ * ~ ~ * mi_trace_light ~ ~ ~ * ~ * mi_sample_light ~ ~ ~ * ~ * mi_trace_shadow ~ ~ ~ ~ * ~ mi_trace_shadow_seg ~ ~ ~ ~ * *
(see shader call tree) mental ray's RC module holds internal data corresponding to the ray tree. Therefore shaders may not call arbitrary trace functions, since in RC's data structures entries are only provided for the following children at a node: eye, reflection, refraction, and transparency rays may cause (see primary ray) reflection and refraction rays up to trace depth, and light rays to each light instance. Light rays may cause shadow rays, and shadow rays never cause further rays.
Environment rays do not have entries in this tree. The data in the tree is used for acceleration and can be overridden if a shader wants to cast rays not normally allowed, by setting the state variable state->cache to zero. This should only be done if necessary because it reduces efficiency. The following table shows which trace functions may be called from which shaders:
ray light lens mtl env light shad vol vol
mi_trace_eye * ~ ~ ~ ~ ~ ~ mi_trace_reflection @ * # @ @ * @ mi_trace_refraction @ * # @ @ * @ mi_trace_transparent @ * # @ @ * @ mi_trace_environment # * * # * * * mi_trace_light # * # @ @ * @ mi_sample_light # * # @ @ * @ mi_trace_shadow ~ ~ ~ * ~ ~ * mi_trace_shadow_seg ~ ~ ~ ~ * ~ ~
Example Scene with Custom Shader
This section shows how to create a .mi file that uses a custom shader that is linked at runtime. This example uses a texture shader, but the same procedure is used to create and link any other type of shader.
Step 1: Writing the Shader Source
This texture shader operates in object space and colors the object into eight cubes that meet at the object coordinate origin. A cube mapped with this texture looks like a Rubik's cube for beginners that consists of eight subcubes, each with a different color. The brightness and saturation of the colors is determined by the shader parameters min and max.
(see mi_eval_scalar) (see mi_point_to_object)
#include <stdio.h> #include <mi/shader.h> struct mytexture { miScalar min, max; }; int mytexture_version(void) {return(1);} miBoolean mytexture( miColor *result, miState *state, struct mytexture *paras) { miVector vec; miScalar min, max; mi_point_to_object(state, &vec, &state->point); min = *mi_eval_scalar(¶s->min); max = *mi_eval_scalar(¶s->max); result->r = vec.x < 0 ? min : max; result->g = vec.y < 0 ? min : max; result->b = vec.z < 0 ? min : max; result->a = 1; return(miTRUE); }
This code should be written to a file mytexture.c.
Step 2: Compiling and Linking
There are several ways to integrate this shader into a scene file: placing the source code directly into the .mi file with $code and $end code statements, referencing the source file with a code statement, or compiling the source and referencing the object code with a link statement. The recommended method, and also the fastest method, is to create a DSO (Dynamic Shared Object) file on Unix systems, or a DLL on Windows NT systems. To do this, the shader must be compiled and linked manually:
% cc -O2 -shared -o mytexture.so mytexture.c
This example uses SGI syntax; other compilers require different command lines. See the beginning of this chapter for the commands required for different types of systems. You may also want to specify the -g option for debugging, -O for optimization, or -I directory to tell the compiler where the directory containing the shader.h file can be found (refer to the compiler documentation for details). After this command, there is a mytexture.so DSO in the current directory; move it to a place accessible to all hosts, for example with
% mv mytexture.so /usr/share/local
Assuming a directory /usr/share/local exists and is accessible to you. You may also choose a directory such as /usr/tmp, but since this directory is not normally accessible from other hosts that you want to use as servers (at least not under this name), you will have to copy mytexture.so to /usr/tmp on the other hosts too.
Step 3: Linking and Declaring the Shader
Now the shader needs to be linked and declared in the .mi scene file that contains the object or objects to be mapped with the new texture. Linking means that the DSO (see DSO (Dynamic Shared Object)) (or DLL) becomes callable by mental ray, and the declaration informs mental ray about the shader parameters:
link "/usr/share/local/mytexture.so" declare shader color "mytexture" (scalar "min", scalar "max") version 1 end declare
It is important that the declaration matches the type and order of the C declaration in the source file, struct mytexture. These lines should be added near the beginning of the .mi file, before the first use and before the frame statement if there is one. For complicated DSOs, it is recommended to put all declarations for the library into a separate file mytexture.mi, and use a $include statement:
link "/usr/share/local/mytexture.so" $include "/usr/share/local/mytexture.mi"
Either way, the library must be linked before the declaration is given because mental ray makes sure that a declared shader exists.
Step 4: Using the Shader
Now the shader can be used in a real .mi file. The following example puts the texture on a cube. Note the transform statements; they describe the camera and object transformations to allow the call to mi_point_to_object in the shader to work correctly.
$include <softimage.mi> link "/usr/share/local/mytexture.so" declare shader color "mytexture" (scalar "min", scalar "max") version 1 end declare options "opt" end options camera "cam" output "pic" "demo.pic" focal 50.000000 aperture 49.839305 aspect 1.179941 end camera instance "cam_inst" "cam" end instance light "light1" "soft_point" ("color" 1.0 1.0 1.0) origin 15.614674 14.370044 -10.951728 end light instance "light1_inst" "light1" end instance color texture "tex" "mytexture" ( "min" 0.3, "max" 1.0 ) material "mtl1" opaque "soft_material" ( "mode" 2, "shiny" 50.0, "ambient" 0.5 0.5 0.5, "diffuse" 0.7 0.7 0.7, "specular" 1.0 1.0 1.0, "ambience" 0.3 0.3 0.3, "texture" [ { "map" "tex", "comp" 1, "blend" 1.0, "ambient" 1.0, "diffuse" 1.0 } ], "lights" ["light1_inst"] ) end material object "cube" visible shadow trace group "mesh" -7.288994 -2.709234 -23.455124 -0.695147 -7.196985 -17.423435 -7.288994 5.313703 -17.485828 -0.695147 0.825951 -11.454139 0.229063 1.226829 -28.745323 6.822910 -3.260922 -22.713634 0.229063 9.249765 -22.776028 6.822910 4.762013 -16.744339 v 0 v 1 v 3 v 2 v 1 v 5 v 7 v 3 v 5 v 4 v 6 v 7 v 4 v 0 v 2 v 6 v 4 v 5 v 1 v 0 v 2 v 3 v 7 v 6 p "mtl1" 0 1 2 3 p 4 5 6 7 p 8 9 10 11 p 12 13 14 15 p 16 17 18 19 p 20 21 22 23 end group end object instance "cube_inst" "cube" transform 0.751806 0.000000 0.659385 0.0 0.393606 0.802294 -0.448775 0.0 -0.529020 0.596930 0.603169 0.0 -10.861954 11.174661 12.737814 1.0 end instance instgroup "root" "cam_inst" "cube_inst" end instgroup render "root" "cam_inst" "opt"
Geometry shaders can be used in two places: in instance definitions that reference a shader instead of an object, light, camera, or instance group:
instance "name" geometry function ... end instance
or in phenomena, where they can be attached to the geometry root:
declare phenomenon ... geometry function ... end declare
In either case, the geometry shader is evaluated during scene preprocessing before any other operation such as rendering starts. The geometry shader is expected to create an object, light, camera, or instance group and add the tag of the created entity to the result. The result pointer passed as its first argument always has the type miTag *, and the shader must be declared as
declare shader geometry "shader_name" ("parameter_decl") ... end declare
If result is a null tag, the created entity can be returned directly using this pointer, otherwise the geometry shader must check whether the type of the item to which result refers is miSCENE_GROUP. If it refers to an instance group, the created entities can be put into this group, otherwise the shader must create the group and put the entity to which result refers together with the entities created by the geometry shader into the instance group. The newly created instance group must be returned in result in this case. This ensures that geometry shaders always return either an object or an instance group, and that they can be chained such that the end result is a group. There is a shader interface function available for adding scene entities to result which takes all the above rules into account; refer to mi_geoshader_add_result.
Creation of geometry requires an altogether different set of shader interface functions. The shader is basically doing the same thing that an object or other definition expressed in the .mi language is doing, and must have the same functionality available to it. In fact, the shader interface functions for geometry shaders closely model the .mi language features: there are begin/end functions for most top-level entities and complex sub-entities, and various helper functions to create and attach complex information. Many of these return pointers to the created data structures that let the geometry shader store primitive data directly without the use of a shader interface function.
Geometry shaders must include geoshader.h after shader.h.
This chapter provides an example and lists all shader interface API functions and data structure declarations available to geometry shaders. It does not explain the structure and order of specific API calls needed to create a scene. Refer to the ``Scene Description Language'' section for the sequence of operations necessary to create a scene entity, and to appendix A for the correspondence between API calls and the entities in the scene description language they create.
In any case, writing geometry shaders is far more complex than writing another type of shader. Although a geometry shader has free run of the entire scene database, and is free to alter or create any part of it, it is generally a good idea to avoid doing too much with them. Instead, it is usually preferable to pass objects, materials, and shaders as input parameters instead of creating entire graphs in geometry shaders, although this is possible and sometimes needed in geometry shaders used in phenomena. Normally, geometry shaders should not modify existing parts of the scene because this can confuse future incremental changes and scene traversal during preprocessing, which geometry shaders are part of. Instead, the entity to be modified should be passed to the geometry shader as a parameter of type geometry, and be used as a template to create a new, modified entity.
All geometry shader API calls and data structures may only be used in geometry shaders, not any other type of shader. Conversely, no regular shader interface function related to rendering may be used in a geometry shader because it is called before rendering begins. Geometry shaders are called during the same stage as displacement shaders, but before the displacement shaders of objects created by this geometry shader.
The following example shader creates an axis-aligned unit cube at the origin. Using unit sizes and centering objects at the origin is usually a good idea because the position, orientation, and sizes of objects are better adjusted with instances than by hardcoding them into the object. This assumes that is switched to object space mode, which is recommended for all scenes anyway (this is done in the options statement).
(see mi_api_geovector_xyz_add) (see mi_api_object_begin) (see mi_api_basis_list_clear) (see mi_api_object_group_begin) (see mi_api_vertex_add) (see mi_api_poly_begin_tag) (see mi_api_poly_index_add) (see mi_api_object_group_end) (see mi_api_object_end) (see mi_geoshader_add_result)
#include <shader.h> #include <geoshader.h> int geocube_version(void) {return(1);} #define add_vector(x,y,z) \ v.x = x; v.y = y; v.z = z;\ mi_api_geovector_xyz_add(&v); static int vertex_order[] = { 0, 3, 5, 4, 3, 2, 6, 5, 2, 1, 7, 6, 1, 0, 4, 7 }; miBoolean geocube( miTag *result, miState *state, miTag *mtl) { int i, k, ix=0; miGeoVector v; miTag mtl_tag; mtl_tag = *mi_eval_tag(&mtl); mi_api_object_begin(NULL, miTRUE, miTRUE, miTRUE, miFALSE, 0, 0, 0); mi_api_basis_list_clear(); mi_api_object_group_begin(0, 0.0); add_vector(-0.5., -0.5., -0.5.); add_vector(-0.5., 0.5., -0.5.); add_vector( 0.5., 0.5., -0.5.); add_vector( 0.5., -0.5., -0.5.); add_vector(-0.5., -0.5., 0.5.); add_vector( 0.5., -0.5., 0.5.); add_vector( 0.5., 0.5., 0.5.); add_vector(-0.5., 0.5., 0.5.); for (i=0; i < 4; i++) mi_api_vertex_add(i); for (i=4; i < 8; i++) mi_api_vertex_add(i); for (i=0; i < 16; i++) mi_api_vertex_add(vertex_order[i]); for (i=0; i < 6; i++) { mi_api_poly_begin_tag(1, mtl_tag); for (k=0; k < 4; k++) mi_api_poly_index_add(ix++); mi_api_poly_end(); } mi_api_object_group_end(miTRUE, miFALSE); return(mi_geoshader_add_result(result, mi_api_object_end())); }
The next example creates a trimmed B-Spline free-form surface. There is one trimming curve defined which is used as a hole curve to cut a square hole out of the surface.
(see mi_api_object_begin) (see mi_api_object_group_begin) (see mi_api_vector_xyz_add) (see mi_api_vertex_add) (see mi_api_vertex_ref_add) (see mi_api_curve_begin) (see mi_api_object_group_end) (see mi_api_object_end)
#include <shader.h> #include <geoshader.h> int bspline_surface(void) { return 1; } miBoolean bspline_surface( miTag *result, miState *state, void *paras) { int i, k; miGeoScalar knot; miDlist *params; miGeoRange range; miApprox approx; miVector cp[] = { /* 16 surface control points, 4 curve cps */ {0, 2, 0},{1, 2, 0},{2, 2, 0},{3, 2, 0}, {0, 3, 0},{1, 3, 1},{2, 3, 1},{3, 3, 0}, {0, 4, 0},{1, 4, 1},{2, 4, 1},{3, 4, 0}, {0, 5, 0},{1, 5, 0},{2, 5, 0},{3, 5, 0}, {0.4, 0.4, 0},{0.6, 0.4, 0}, {0.6, 0.6, 0},{0.4, 0.6, 0}}; miGeoScalar curve_knots[] = {0,1,2,3,4}; miUint curve_v[] = {16,17,18,19,16}; /*closed loop*/ /* ************ create object ************ */ mi_api_object_begin(mi_mem_strdup("my_object"), miTRUE,miTRUE,miTRUE,0,0,0,miFALSE); mi_api_basis_list_clear(); mi_api_basis_add(mi_mem_strdup("bspline_3"), miFALSE,miBASIS_BSPLINE,3,0,0); mi_api_basis_add(mi_mem_strdup("bezier_1"), miFALSE,miBASIS_BEZIER,1,0,0); mi_api_object_group_begin(0,0); /* control points and references */ for(i=0; i < 20; i++) mi_api_vector_xyz_add(&cp[i]); for(i=0; i < 20; i++) mi_api_vertex_add(i); /* ************ create trim-curve ************ */ mi_api_curve_begin(mi_mem_strdup("curve_0"), mi_mem_strdup("bezier_1"), miFALSE); /* curve knot sequence */ params = mi_api_dlist_create(miDLIST_GEOSCALAR); for(i=0; i < 5; i++) mi_api_dlist_add(params,&curve_knots[i]); /* curve control point references, non-rational (w=1) */ for(i=0; i < 5; i++) mi_api_vertex_ref_add(curve_v[i],1.0); mi_api_curve_end(params); /* ********** create free-form surface ********** */ mi_api_surface_begin_tag(mi_mem_strdup("surf_0"),0); /* create the uv knot vectors for the (bezier) surface */ for(k=0; k < 2; k++) { params = mi_api_dlist_create( miDLIST_GEOSCALAR); knot = 0.0; for(i=0; i < 4; i++) mi_api_dlist_add(params, &knot); knot = 1.0; for(i=0; i < 4; i++) mi_api_dlist_add(params, &knot); mi_api_surface_params(k == 0 ? miU : miV, mi_mem_strdup("bspline_3"), 0.,1.,params,miFALSE); } /* control point references, nonrational(w=1) */ for(i=0; i < 16; i++) mi_api_vertex_ref_add(i,1.0); /* define one hole curve on the surface */ range.min = 0.0; range.max = 4.0; mi_api_surface_curveseg(miTRUE, /* newloop */ miCURVE_HOLE, mi_mem_strdup("curve_0"), &range); mi_api_surface_end(); /* set approximation method for the surface */ miAPPROX_DEFAULT(approx); approx.method = miAPPROX_CURVATURE; approx.cnst[miCNST_DISTANCE] = 0.01; mi_api_surface_approx(mi_mem_strdup("surf_0"), &approx); /* set approximation method for the curve */ miAPPROX_DEFAULT(approx); approx.method = miAPPROX_PARAMETRIC; approx.cnst[miCNST_UPARAM] = 1; mi_api_curve_approx(mi_mem_strdup("curve_0"), &approx); mi_api_object_group_end(miFALSE,miTRUE); return mi_geoshader_add_result(result, mi_api_object_end()); }
The next example accepts an object as input, and creates a new object based on the input object. The new object is identical to the old one except that only points in space are copied and all normals, texture vectors, motion, and other information is ignored. Also, it deals with groups correctly only if it does not contain multiple local spaces because it ignores the transformation matrices in the instance loop.
(see mi_api_vector_xyz_add) (see mi_api_object_begin) (see mi_api_object_group_begin) (see mi_api_vertex_add) (see mi_api_poly_begin_tag) (see mi_api_poly_index_add) (see mi_api_object_group_end) (see mi_api_object_end) (see mi_geoshader_add_result) (see mi_geoshader_tesselate) (see mi_geoshader_tesselate_end)
#include <assert.h> #include "shader.h" #include "geoshader.h" int facet_version(void) {return(1);} static miBoolean box_to_object( miTag *result, miBox *box) { int i, vs = box->vert_info.sizeof_vertex; miIndex *vert = miBOX_GET_VERTICES(box); miTriangle *tri = miBOX_GET_PRIMITIVES(box); miVector norm; assert(box->type == miBOX_TRIANGLES); mi_api_object_begin(0, miTRUE, miTRUE, miTRUE, miFALSE, 0, 0, miFALSE); mi_api_object_group_begin(0, 0.0); for (i=0; i < box->vect_info.no_points; i++) mi_api_vector_xyz_add(box->vectors + i); for (i=0; i < box->no_vertices; i++, vert+=vs) mi_api_vertex_add(*vert); for (i=0; i < box->no_primitives; i++, tri++) { mi_api_poly_begin_tag(1, 0); mi_api_poly_index_add(tri->a); mi_api_poly_index_add(tri->b); mi_api_poly_index_add(tri->c); mi_api_poly_end(); } mi_api_object_group_end(miTRUE, miFALSE); return(mi_geoshader_add_result(result, mi_api_object_end())); } miBoolean facet( miTag *result, miState *state, miTag *param) { miTag leaves, scan, boxes, next; miBoolean ret; ret = mi_geoshader_tesselate(state, &leaves, *mi_eval_tag(param)); for (scan=leaves; scan; scan=next) { miInstance *inst = mi_db_access(scan); for (boxes=inst->boxes; boxes; boxes=next) { miBox *box = mi_db_access(boxes); ret &= box_to_object(result, box); next = box->next_box; mi_db_unpin(boxes); } next = inst->next; mi_db_unpin(scan); } mi_geoshader_tesselate_end(leaves); return(ret); }
The main facet shader first tesselates the input geometry. This builds a leaf instance list containing a sequence of instances that contain both an item field pointing to the source geometry (which may be difficult to handle because it may include many different types of complex free-form surface and polygonal geometry), and a boxes field that points to triangles. Without mi_geoshader_tesselate, the boxes field may be empty, and the shader would have to do its own traversal to find the instances.
The following nested loops consider all instances, and all boxes in each instance. There are multiple instances if the input object passed to the shader as its only parameter is an instance group or a merge group. An instance may contain multiple boxes if the object has too many vectors, vertices, or triangles to fit into a single box. For each box, a new object is generated by calling box_to_object, which is similar to the previous example except that it gets its information from a box. Every new object is appended to the result using mi_geoshader_add_result.
Finally, the shader calls mi_geoshader_tesselate_end to release the instance list, including all triangle boxes, that was created by mi_geoshader_tesselate. This is important because this list may be very large, and would introduce a large memory leak if not freed.
The API module has been patterned after the .mi2 language, to the point where the .mi2 yacc grammar consists almost entirely of one or very few API calls or variable assignments for every statement and clause. To understand the correct order of API calls, refer to the .mi2 language description. The complete yacc grammar that parses the .mi2 language, including C code, is reprinted in the appendix of this document. For information on the syntax of a yacc grammar description, refer to the Unix manual page for yacc.
Note that all character string arguments passed to any of the API functions below are expected to have been allocated with mi_mem_allocate. All API functions release such strings using mi_mem_release, so the same allocated string may not be passed to API twice. Similarly, miDlists created with mi_api_dlist_create and passed as an argument to an API call are freed by API, and should not be passed to API twice. The reason for this is that API keeps such strings for extended periods of time, so allocation is almost always necessary somewhere, and putting the burden on the API caller avoids double allocations in cases where the caller works with allocated strings anyway (for example in yacc parsers). In the few cases where API could work with non-allocated strings (mi_api_debug, for example) allocation is required anyway to avoid requiring allocation for some strings but not others, which would invite hard-to-find bugs.
Most API functions return a boolean, a pointer, or a tag. If the function succeeds, miTRUE, a non-null pointer, or a non-null tag is returned, respectively. If an error occurs, miFALSE or a null pointer or tag is returned; this means that errors can be caught with C's ``!'' operator. The tags returned by the calls whose names end in _end are merely a convenience; they are not generally useful to standard name-based translators.
The API module identifies all entities by name. Different name spaces are provided for different purposes. In particular, the API module supports both the .mi1 and .mi2 languages, which have a different name space concept. The .mi2 module uses a shared global name space for all entity types, while the .mi1 format uses separate name spaces for each entity type.
Note that shader parameter names do not use a persistent name space and have no assigned symbol table. Shader parameters are stored in declarations and are not accessible in any symbol table. There are also other short-lived sub-entities such as curves or surfaces that are not stored in symbol tables.
In general, symbol tables are used by the API module internally only. There is normally no need for another module to access specific symbol tables. However, API provides lookup functions for symbol tables that return the tag for individual named entities. This can be used for accessing the tesselated renderable representation of a geometric object, or for applying low-level SCENE functions to entities created with API functions.
For example, the API module can be used to set up a small scene consisting of a single free-form surface object, which is then ``rendered'' using a render function that does not call any renderer, but uses the symbol table to find the object's tag, accesses the tesselated geometry, and displays a wireframe of the tesselation in a GUI window. This is a useful application of API because it visualizes the effects of user-defined approximation modes.
The following table lists the symbol tables. All except the global and variable symbol tables are used for .mi1 entities (bracketed by frame and end frame statements) only.
table purpose
S_GLOBAL all .mi2 entities S_FUNCDECL Function declarations (declare statements) S_OPTIONS Options (options statements) S_CAMERA Cameras (camera statements) S_LIGHT Lights (light statements) S_OBJECT Geometric objects (object statements) S_INSTANCE Instances (instance statements) S_INSTGROUP Instance groups (group statements) S_MATERIAL Materials (material statements) S_CTEXTURE Color textures (color texture statements) S_STEXTURE Scalar textures (scalar texture statements) S_VTEXTURE Vector textures (vector texture statements) S_VARIABLE For internal use only
It is possible to use the symbol table lookup functions defined by the LIB module (see the LIB chapter) for direct lookups. However, API defines a number of specialized functions that perform lookups required during parsing:
miTag mi_api_name_lookup( char * name)
Look up the element name in the .mi2 element and function declaration symbol tables (S_GLOBAL and S_FUNCDECL, and return the tag if found. Otherwise, return a null tag. Other symbol tables are not searched because .mi1 elements are temporary and disappear when the current frame ends.
miTag mi_api_decl_lookup( char * name)
Same thing but just for shader declarations. This is a separate function because if an object and a declaration have the same name (this is legal because they are in different name spaces), mi_api_name_lookup will never see the declaration and always return the other object.
miBoolean mi_api_variable_set( char *name, /* variable name */ char *value) /* variable value or 0 to unset */
Set a variable name to a value value. If value is a null pointer, delete the variable. Variables can be redefined. The main purpose is to store meta-data in .mi scene files using the set command for variables such as author, translator, creation date, etc, whose values can be picked up by the client application or even a shader.
const char *mi_api_variable_lookup( char *name) /* variable name */
Return the value of the variable name, as set with a preceding mi_api_variable_set. If the variable is not defined, a null pointer is returned. The returned string should not be modified or released.
const char *mi_api_tag_lookup( miTag tag)
This is a reverse lookup function. Given a tag, it scans all symbol tables for an entity with this tag and returns its name. This function is intended for printing more helpful error messages if only a tag is known, and to allow database dumps containing names. If the tag cannot be found, a null pointer is returned. A non-null pointer should not be modified or released, it points directly into the symbol table.
miTag mi_api_light_lookup( char *name) /* light instance name to look up */
Look up a light by name and return its tag. If no light by this name is found, return a null tag.
miTag mi_api_material_lookup( char *name) /* name of material to look up */
Look up a material by name and return its tag. If no material by this name is found, return a null tag.
miTag mi_api_texture_lookup( char *name, /* name of texture to find */ int type) /* 0=color, 1=scalar, 2=vector tex */
Look up a texture by name and return its tag. If no texture by this name is found, return a null tag. type is the desired texture type: 0 for color textures, 1 for scalar textures, and 2 for vector textures.
miBoolean mi_api_surface_lookup( char *name, /* name of existing face */ miTag *instance, /* instance containing this face */ miGeoIndex *idx) /* index of face */
Look up a surface by name and return its tag. If no surface by this name is found, return miFALSE, otherwise return miTRUE. This function can be used only while defining a geometric object group, between mi_object_group_begin and mi_object_group_end. Its purpose is finding surfaces to be connected for connection statements. The surface instance tag (not currently used) and the surface index (a sequential number beginning with 0 in every object that enumerates the surfaces in the object) are also returned.
void *mi_api_curve_lookup( char *name, /* name of existing curve */ enum miCurve_type type, /* type of curve */ miBoolean newloop, /* is this a new loop */ miGeoRange *range, /* range of the curve */ int *no_scalars)
Look up a curve by name and type and return an identifying pointer. If no curve by this name is found, return a null pointer. The curve type is one of miCURVE_TRIM, miCURVE_HOLE, and miCURVE_SPECIAL, as defined by the SCENE module. This function is used when a surface references a curve as a trim, hole, or special curve with the given range. This function not only looks up the curve, but also creates a new curve reference entry containing the curve identifier, the type, whether this curve begins a new loop or continues an old one (newloop), and the parameter range of the curve to use. The number of parameters of the curve is returned in *no_scalars. This function may only be used during the definition of a surface. Because of the side effects, this is not a true lookup function.
The registry is a sequence of named entries, each of which contains an ordered list of key/value pairs. The entry name and all keys and values are strings. See the user manual for a description of the registry. The registry is a mental ray database that is not related to other registries found on Unix and Windows NT, unless mental ray is built into an application that provides a connection. In this case, the eval and lookup functions can access the system registry through this connection, but all begin/add/end definitions stay local to mental ray and do not write to the system registry. No such connection is built into standalone versions of mental ray.
miBoolean mi_api_registry_begin( char *regname)
Begin the definition of a new registry entry with the given name. The name must begin with and end with . It can later be looked up with mi_api_registry_eval and mi_api_registry_lookup. If the registry entry already existed, it is deleted along with all its key/value pairs and a new entry of the same name is created.
miBoolean mi_api_registry_add( char *key, char *value)
After beginning a registry entry with the previous function, key/value pairs can be associated with the registry entry using this function. The key and value arguments are unrestricted, but the eval and lookup functions assign special meaning to certain keys.
miBoolean mi_api_registry_end(void)
Completes the definition of a registry entry begun with mi_api_registry_begin. Calls to mi_api_registry_add are allowed only between begin and end, and no eval. The eval and lookup functions may be used between begin and end, and they will see partially built registry entries.
const char *mi_api_registry_eval( char *regname)
Scan the string regname for substrings enclosed in curly braces . If the substring begins with a dollar sign, an environment lookup is performed using getenv; otherwise the registry is searched for the given name and, if found, the value associated with its value key is substituted. All characters not encodes in curly braces are passed through unchanged. If a registry lookup is successful, all its known keys are evaluated:
const char *mi_api_registry_lookup( char *regname)
This function is equivalent to mi_api_registry_eval, except that only the value associated with the value key is returned, and no other keys are evaluated.
Dynamic lists (dlists) are used by API to buffer certain types of data where it is not known in advance how many will be needed. They are implemented as linked lists of pages. For example, surface and curve parameter lists are sequences of numbers or number pairs that are collected in a dlist, which is then passed to an appropriate API function.
There are seven types of dlists containing different data types:
dlist type data type purpose
miDLIST_SCALAR miScalar single precision miDLIST_GEOSCALAR miGeoScalar double precision miDLIST_DOUBLE double double precision miDLIST_INTEGER int integer miDLIST_POINTER void * generic pointer miDLIST_VREF struct miVref rational parameter miDLIST_TAG miTag material tag list
The miVref structure contains an integer parameter value and a double-precision floating-point weight. It is used for parameter lists of rational curves and surfaces. Tag lists are currently used only for material arrays in instances.
typedef struct miVref { int ref; miGeoScalar w; } miVref;
There are three API functions that deal with dynamic lists, one to create a list, one to append data to a list, and one to delete the list after it has been passed to another API function that expects a dynamic list:
miDlist *mi_api_dlist_create( int type)
Create a dynamic list of the specified type. The type argument must be one of the standard dlist types such as miDLIST_SCALAR. The returned pointer identifies the list and is passed to the other API functions.
miBoolean mi_api_dlist_add( register miDlist *dlp, void *data)
Append data to an existing dlist. The elem pointer argument must point to a data entry of the type that agrees with the type of the dlist, as listed in the second column of the dlist type table above.
miBoolean mi_api_dlist_delete( miDlist *dlp)
Delete the dlist. The dlist pointer may not be used after this call. dlists passed to an API function (other than mi_api_dlist_add are deleted automatically, so this call is not normally needed.
miTag mi_api_taglist( miDlist *list)
Convert a dlist of type miDLIST_TAG to a scene element of type miSCENE_TAG, and return the tag of the new scene element. If the dlist has the wrong type or is empty, a null tag is returned. This can be used to create a material list for instances.
miBoolean mi_api_error_callback( void (*err_cb)(char *, int), void (*warn_cb)(char *, int))
Install error and warning callbacks. Two functions must be passed that accept a string argument (the message) and an integer (the message code). Message codes are unique identifiers in the range 1..999 that are integrated into the raylib error message codes as the last three digits.
The API module will call the callback functions whenever an error or a warning occurs, with an appropriate message. The function may print the message to the terminal or elsewhere, together with useful context information such as the current file name and line number if available. If no callbacks are installed, they default to mi_nerror and mi_nwarning, respectively, with a second argument of "%s" to prevent evaluation of percent characters. Note that all API functions also return miFALSE or a null pointer or a similar failure indicator if the requested operation could not be performed, in addition to any warning or error messages.
void mi_api_nerror (int code, char *msg, ...)
This API function is called by API whenever an error occurs. It is part of the call interface to allow the API caller to print its own messages in a format similar to that of API itself. The arguments work in the same way as those of mi_nerror. The error code is a unique integer in the range 1..999 that distinguishes messages; if printed by mental ray it will become part of the standard message code as the last three digits. Codes up to 899 are reserved by mental ray; application code should use 900..999.
void mi_api_nwarning (int code, char *msg, ...)
This is the corresponding warning message function called by API.
void mi_api_error (char *msg, ...)
This function is obsolete and should not be used. It calls mi_api_nerror with a code number of 1.
void mi_api_warning (char *msg, ...)
This function is obsolete and should not be used. It calls mi_api_nwarning with a code number of 1.
miBoolean mi_api_incremental( miBoolean incr)
Enable or disable incremental mode. Incremental mode is off by default. If turned on, the next options, camera, texture, light, material, object, instance, or instance group definition (initiated with the appropriate begin call listed below) is not created with fresh defaults, but with the values it was created with earlier. It is an error to request an incremental change to an entity that does not exist, and to define an entity that already exists without specifying incremental mode first. Incremental mode is automatically reset after the following begin call it applies to; it is generally unnecessary to call mi_api_incremental with a miFALSE argument.
Note that although the API module establishes all links in the scene graph with name strings, the strings are immediately converted to tags by all API functions. All internal links in the graph use tags. This means that if an element is created with a certain name, and this name is passed to another function to establish a link, the link remains intact only if all future changes to the named element are done in incremental mode. If the element were instead deleted and recreated with the same name, the same name would be associated with a new tag, and all previous links would become stale. It is therefore important to use incremental mode even when modifying elements that do not actually retain information when changed, such as instance groups.
Any scene needs at least one camera and at least one options block. In a frame block they are implicitly created at frame begin; in the .mi2 language they must be created explicitly with the following calls.
miRc_options *mi_api_options_begin( char *name) /* symbolic name of options */
Create an options entity and return a pointer to it. The options entity is created with default parameters. The caller may modify the defaults by writing directly into the returned structure. Any number of options entities can be created but only one of them can be named in any render operation.
miTag mi_api_options_end (void)
After modifying the default options, this function must be called to complete the options definition. If an error occurs, a null tag is returned; otherwise the tag of the new options element is returned. The type of the returned element is miSCENE_OPTIONS.
miCamera *mi_api_camera_begin( char *name) /* symbolic name of camera */
Create a camera entity and return a pointer to it. The caller can use the pointer to modify the defaults.
miTag mi_api_camera_end (void)
Complete the definition of the camera, after all parameters have been adjusted. If an error occurs, a null tag is returned; otherwise the tag of the new camera element is returned. The type of the returned element is miSCENE_CAMERA.
miTag mi_api_output_file_def( miUlong typemap, /* data type bitmap */ miUlong interpmap, /* interpolation req bitmap */ char *format, /* file type string */ char *fname) /* file name */
Define an output file and return an output function describing the file to write. The returned function has a special type code that tells mi_api_output_shaders to write a file instead of calling an output shader, and has the given format and file name encoded in its parameter area. It is not a normal shading function but can be treated as one by the caller, and can be appended to the camera's output function list.
When the output list is evaluated after rendering, this definition will write a frame buffer to the named file fname, using the file format format. Valid formats are extension strings such as "pic" or "rla"; refer to the user's manual for a complete list. The typemap is a bitmap. Valid bit numbers are defined by the miImg_type enum as defined by the IMG module. Each bit informs the renderer that this shader requires a frame buffer of this type; at rendering time all active bitmaps (those defined in the camera used for rendering) are OR-ed. Only one bit should be set.
The interpmap bitmap uses the same pattern as typemap. It has a bit set if the frame buffer described by the corresponding set bit in typemap should be interpolated.
miTag mi_api_output_function_def( miUlong typemap, /* data type bitmap */ miUlong interpmap, /* interpolation req bitmap */ miTag function) /* output shader */
Define an output shader. Like output files, output shaders require a typemap. Unlike output files, output shaders may have multiple bits set. The function tag identifies the shader to be called. Shader tags are obtained during shader definition, they are returned by mi_api_function_call_end. The returned function tag can be appended to the camera's output function list by the caller.
The interpmap bitmap uses the same pattern as typemap. It has a bit set if the frame buffer described by the corresponding set bit in typemap should be interpolated.
miBoolean mi_api_output_file_override( char *format, char *fname)
Specify a file type and a file name that overrides the corresponding arguments of the next mi_api_output_file_def call. This call is not normally used to define a scene; it is intended for command-line option parsers that accept overrides for the file type and name specified in the scene. The override function must be called before the call to mi_api_output_file_def whose arguments should be overridden.
miBoolean mi_api_output_type_identify( miUlong *typemap, /* returned type bitmap */ miUlong *interpmap, /* returned interpolation bitmap */ char *typename) /* symbolic data type to identify */
This function converts a type string to a type map as required by the output file and output shader definition functions above. A type string is a comma-separated list of types, such as "rgba,n,contour". The special type contour is accepted also; it is the only one that is handled by RC only and not by IMG. All other types are identified in the same way as done by mi_img_type_identify. The type bitmap is returned in *typemap; the bits correspond to 1 shifted left by the miIMG_TYPE_* code.
Types can be prefixed with `+' or `-' to turn interpolation on or off for the frame buffer, respectively. Interpolation means that unsampled pixels get a value interpolated from its neighbors, rather than becoming a copy of one of its neighbors. Interpolation is on by default for all kinds of color frame buffers and off for all others. The interpolation bitmap is returned in *interpmap; the bit layout is the same as for *typemap.
miBoolean mi_api_output_imfdisp_handle( miTag camera_tag) /* camera tag */
This function should be called after all output files have been defined. For every output file in the camera's output list of camera specified by camera_tag, a 128-byte file is created, containing the image resolution and the gamma factor from the camera, as well as information about how to open a socket to mental ray and version information. This information is understood by programs such as imf_disp which can connect to mental ray during rendering and show the rendered image as it is being created. Note that mental ray, when connected to in this way, will always transfer the RGBA picture regardless of the type of the file used to establish the connection.
miBoolean mi_api_output_list( miTag cam_tag) /* camera tag */ miTag opt_tag) /* options tag */
This function calls mi_phen_output_list, which collects all the type maps from the output list in the given camera and places appropriate frame buffer records into the options structure specified by tag. This function is no longer used; RC calls mi_phen_output_list directly after output shaders introduced by phenomena have been added. mi_api_output_list may be removed in a future version of mental ray because it doesn't take phenomena into account, which means it generates incomplete frame buffer lists.
miBoolean mi_api_output_shaders( miTag cam_inst_tag, /* camera instance tag */ miTag opt_tag, /* options tag */ miTag *images) /* of length miRC_MAX_IMAGES */
This function is obsolete since output shaders are called from mi_rc_render. Therefore it prints a warning and always returns miTRUE. The output list remains unchanged in the process.
miLight *mi_api_light_begin( char *name) /* light name */
Begin the definition of a light with the specified name. A pointer to the light is returned; the caller may modify the structure.
miBoolean mi_api_light_end (void)
Complete the definition of a light after all parameters have been set.
miMaterial *mi_api_material_begin( char *name) /* material name */
Begin the definition of a material with the specified name. A pointer to the material is returned; the caller may modify the structure.
miTag mi_api_material_end (void)
Complete the definition of a material after all parameters have been set. If an error occurs, a null tag is returned; otherwise the tag of the new material element is returned. The type of the returned element is miSCENE_MATERIAL.
miTag mi_api_texture_begin( char *name, /* symbolic texture name */ int type, /* 0=color, 1=scalar, 2=vector tex */ int flags) /* bit0=local, bit1=filter */
Begin the definition of a texture with the specified name. In incremental mode, the tag of the existing texture to be changed is returned; otherwise a null tag is returned. The tag is useful mainly to determine whether the previous texture was an image or a shader, using the mi_db_type function. The type of the texture and a flags bitmap must also be specified.
Bit 0 of the flags, if set, specifies that the texture is local. This is meaningful for image textures only, it specifies that each host on the network should read the image from its local file system instead of fetching the texture across the network. Memory-mapped textures are not flagged explicitly; they are recognized automatically at load time. Bit 1 of the flags, if set, specifies that the texture image is looked up using a filter. This flag is ignored for procedural (function) textures.
Bit 1 of the flags, if set, enables pyramid filtering (see texture pyramid) , an algorithm comparable to mip-map textures. mental ray will convert the texture to pyramid format, which takes about 30% more memory but allows filtered lookups. This avoids texture aliasing, which is a problem for point-sampled non-filter textures that are far away so that each point sample hits widely spaced pixels in the texture.
This call is always the first stage of creating a texture and must be followed by one of the next three def calls, which complete the texture definition.
miTag mi_api_texture_file_def( char *file) /* file name to open */
The new texture is defined as a file texture, using the path file. The path must be valid on the local host and, if the texture is local, on all other hosts on the network. If an error occurs, a null tag is returned; otherwise the tag of the new texture element is returned. The type of the returned element is miSCENE_IMAGE.
miTag mi_api_texture_function_def( miTag function) /* function with args */
The new texture is defined as a procedural texture, using the texture shader function. Function tags are created when defining functions, and are returned by mi_api_function_call_end. The local flag is ignored for procedural textures. If an error occurs, a null tag is returned; otherwise the tag of the new texture element is returned. The type of the returned element is miSCENE_FUNCTION.
miBoolean mi_api_texture_array_def_begin( int xres, /* width in pixels */ int yres, /* height in pixels */ int zres) /* # of bytes per component, 1 or 2 */
The final method of defining a texture is verbatim. It is equivalent to a file texture except that the pixel data is not read from a file on disk but is stored by the API caller using the texture copy calls below. This kind of texture cannot be flagged local. The texture resolution is set with the xres and yres arguments. The number of bytes per component is set with the zres argument. The number of components per pixel is determined by the type argument of the preceding call to mi_api_texture_begin: 4 for color textures, 1 for scalar textures, and 2 for vector textures. Vector textures always assume 4 bytes per component (one float) regardless of whether zres is 1 or 2.
miBoolean mi_api_texture_byte_copy( int len, /* number of bytes */ miUchar *bytes) /* bytes from the mi file */
After the texture was defined the contents of the texture must be copied. A total of xres *yres *zres *nc bytes must be copied, with zres being 4 for vector textures and nc being the number of components. The copy function can be called multiple times to collect the required number of bytes, the sum of all len arguments must be the total required number of bytes. The byte order is big-endian (MSB first) if zres >=1. Pixels are stored in component scanline order: first the bottom red scanline is copied, then the bottom green scanline, then blue and finally alpha (scalar textures have only one component, vector textures are stored U first, then V).
miTag mi_api_texture_array_def_end (void)
After all texture bytes have been copied, the texture must be completed with this call. If an error occurs, a null tag is returned; otherwise the tag of the new texture element is returned. The type of the returned element is miSCENE_IMAGE.
Instances and instance groups are used to construct the scene DAG (directed acyclic graph). Instance groups are unordered lists of one or more instances. Instances are nodes of the DAG, they reference an item to be placed into the DAG together with a space transformation, optional inheritance parameters, and various flags. Elements that can be referenced by instances are geometric objects, cameras, lights, and instance groups. If more than one instance references the same item, this is called multiple instancing.
miInstance *mi_api_instance_begin( char *name) /* instance name */
Begin the definition of an instance with the given name. In incremental mode, a pointer to the existing instance is returned; otherwise a pointer to a temporary instance is returned. It is not necessary to store the various flags and IDs in the instance transformation, this is done during preprocessing.
miTag mi_api_instance_end( char *item, /* name of item to instance */ miTag function, /* geometry shader */ miTag params) /* transformation func with params */
Complete the definition of the instance. If the instanced item is not a geometry shader, the item to be instanced is given by item and function must be a null tag. If the instanced item is a geometry shader, the function list is passed in function and item must be a null pointer. It is an error if both item and function are requested to be instantiated. Incremental changes from regular to geometry shader instances and vice versa are possible.
The params tag is either the null tag (no inheritance parameters), the numerical value -1 (leave parameters unchanged during an incremental change), or the tag of a function containing inheritance parameters. Only the parameters are used, the function is ignored. The function should be deleted after being passed to mi_api_instance_end.
If an error occurs, a null tag is returned; otherwise the tag of the new instance element is returned. The type of the returned element is miSCENE_INSTANCE.
miBoolean mi_api_instgroup_begin( char *name) /* group name */
Begin the definition of an instance group with the given name. The list of elements is cleared even if incremental mode is enabled. However, incremental mode still ensures that the linkage of the instance group to its parent instance, if any, remains intact.
miBoolean mi_api_instgroup_additem( char *name) /* instance name */
After the instance group definition was begun, instances can be added to it. The order of the instances being added is irrelevant. Only instances can be added, never elements such as geometric objects directly.
miTag mi_api_instgroup_end (void)
After all instances have been added to the instance group, this call completes the creation of the instance group. If an error occurs, a null tag is returned; otherwise the tag of the new instance group element is returned. The type of the returned element is miSCENE_GROUP.
Geometric objects can be divided into
polygonal and free-form surface
objects. Each object contains one or more object groups that group geometry. Object groups are maintained for backwards compatibility with the .mi1 language; newer designs should use multiple objects rather than a single object with multiple object groups. Object groups are unrelated to instance groups as described above.
miBoolean mi_api_object_begin( char *name, /* object name for progress message */ miBoolean visible, /* object is visible to 1st-gen rays */ miBoolean shadow, /* object casts shadows */ miBoolean trace, /* object is visible to 2nd-gen rays */ miBoolean caustic, /* object casts caustics */ miUint label, /* Softimage's "tag channel" value */ miMatrix transform, /* object transformation (mi1 only) */ miBoolean mtl_is_label) /* poly/surf materials are labels */
Begin the definition of an object with the given name. If the name parameter is zero, the object is not registered in the symbol tables, so it cannot be referenced by name. In this case the tag returned by mi_api_object_end must be used for referencing the object. All geometrical information is deleted regardless of incremental mode. This is the first call of the object definition sequence. After the object begin, first bases are defined if the object contains free-form surfaces. Next, the object group or groups must be defined. The mtl_is_label flag specifies that the object is marked tagged, which means polygons and surfaces specify label integers instead of materials. (This argument was added in version 2.0.17.)
miBoolean mi_api_object_group_begin( char *name, /* group name, not used */ double merge) /* merge epsilon */
The next step after the begin call is beginning an object group. If the object contains free-form geometry, and the surfaces in the group should be merged automatically (using adjacency detection), a nonzero merge epsilon must be specified. Merge epsilons in objects are a .mi1 feature that has been superseded by .mi2 merge groups; for new designs the merge argument should be zero. The name is not used, it is retained for .mi1 compatibility only. A null pointer can (and should, in new designs) be passed for the name.
miBoolean mi_api_object_group_connection( char *surfname1, /* name of first surface */ char *curvename1, /* name of first curve */ miGeoRange *range1, /* range of first curve */ char *surfname2, /* name of second surface */ char *curvename2, /* name of second curve */ miGeoRange *range2) /* range of second curve */
After all surfaces in the free-form object have been defined, just before mi_api_object_group_end is called, this function can be used to establish connections between surfaces. Connections can only be defined along the trimming curves on the surfaces, which must exist. Untrimmed surfaces cannot be connected, but a trivial trim curve that follows the parameter boundaries such that no part is actually trimmed off is simple to create. A parameter range must be specified for both curves.
miBoolean mi_api_object_group_end( miBoolean are_polys, /* are there polygons? */ miBoolean are_surfs) /* are there surfaces? */
After all geometry in the object group has been defined, this function completes the object group definition. Another group can be begun after ending the current one.
miTag mi_api_object_end (void)
Complete the finished object. This is the last call of the object definition sequence. If an error occurs, a null tag is returned; otherwise the tag of the new object or group element is returned. API decides whether to create and return an object or an instance group element based on the object complexity: if it has multiple object groups, or a merge epsilon, or connections, an instance group is created that hase the merge_group flag set, and references one or more instances, each of which references an object element containing a single object group. If there is only one object group, no merge epsilon, and no connections, the object is created and returned directly without creating intermediate instances or connections. This decision is transparent to the caller; the tesselator accepts instance groups in this case as well as objects. The type of the returned element is either miSCENE_OBJECT or miSCENE_GROUP.
Objects containing free-form surfaces must contain at least one basis. A basis defines the curve type or one of the two surface parameter axis types (e.g., NURBS or Bézier), a rational flag, and the degree. All surfaces in all object groups of an object share the same basis list in the object, and reference bases by name.
miBoolean mi_api_basis_list_clear (void)
Delete all bases in the current object basis list.
miBoolean mi_api_basis_add( char *name, /* symbolic name of basis */ miBoolean rational, /* rational surface if true */ enum miBasis_type type, /* basis type, miBASIS_* */ miUshort degree, /* degree of basis */ miUshort stepsize, /* step size if miBASIS_MATRIX */ miDlist *matrix) /* basis matrix if miBASIS_MATRIX */
Append a basis to the basis list of the current object. The basis name, a rational flag, the basis type, and the degree must be defined. The matrix stepsize and the basis matrix must be defined only for the basis matrix type. The matrix is passed as a dlist (dynamic list, see above) because its size depends on the degree of the basis; (degree+1)2 floating-point numbers of type miGeoScalar are expected.
miGeoIndex mi_api_basis_lookup( char *name, /* name of basis to look up */ miBoolean *rational, /* returned rational flag */ miUshort *degree) /* optional: returned degree */
This function allows basis lookups by basis name. The sequential number of the basis in the object's basis list is returned, as well as the rational flag and the degree if the second and third arguments are non-null pointers. This function is not essential for scene definition.
An object group can contain either polygonal or free-form surface geometry. Polygonal geometry requires the definition of three blocks of data: vectors, vertices, and polygons. Optionally, an approximation may be given which is used by the tesselator for displacement-mapped polygons.
miBoolean mi_api_geovector_xyz_add( miGeoVector *newvec) /* new vector to append */ miBoolean mi_api_vector_xyz_add( miVector *newvec) /* new vector to append */
After beginning the object group that should contain polygonal geometry, one call to this function for every XYZ vector to be used must be done. Vectors will be referenced by sequential number, beginning with 0 in every object group, to be used as points in space, normals, motion vectors, and various other purposes. There are restrictions of vector sharing; for example, a vector cannot be used as a point in space and a normal at the same time. Also, vectors should be listed in a certain order (points in space first, then normals, and so on) for maximum efficiency. Any other order will be re-sorted automatically.
Refer to the description of the .mi2 language in the mental ray manual for details.
There are two versions of this call. The first accepts single-precision vectors and the second accepts double-precision vectors. Both store the vectors in double precision. If the vector is available in double precision, always use mi_api_geovector_xyz_add to avoid precision loss. Both versions can be mixed, they are identical except for the argument type.
miBoolean mi_api_vector_lookup( miGeoVector *pos, /* Output: vertex position */ int idx) /* index of vector */
This function looks up a vector in the current object by index, and returns its value. This function is not essential for scene definition.
miBoolean mi_api_vertex_add( int p) /* point in space */ miBoolean mi_api_vertex_normal_add( int n) /* normal vector */ miBoolean mi_api_vertex_deriv_add( int u, /* dPdu derivative */ int v) /* dPdv derivative */ miBoolean mi_api_vertex_deriv2_add( int u, /* d2Pdu2 derivative */ int v, /* d2Pdv2 derivative */ int uv) /* d2Pduv derivative */ miBoolean mi_api_vertex_tex_add( int t, /* texture parameter vector */ int x, /* X basis vector, -1 if none */ int y) /* Y basis vector, -1 if none */ miBoolean mi_api_vertex_motion_add( int m) /* motion vector */ miBoolean mi_api_vertex_user_add( int u) /* user vector */
After all vectors have been defined, vertices must be defined. Vertices must contain a point in space, defined with mi_api_vertex_add, optionally followed by a normal, first and second surface derivatives, one or more texture vectors with optional bump basis vectors, a motion vector, and one or more user vectors. After the initial point in space, the order of the other calls is fixed. Vectors are referenced by sequential number in the object group, with 0 being the first vector. A vector index -1 means ``none,'' which is useful for defining texture vectors without bump basis vectors. Either no or two bump basis vector indices must be given. All add functions until the next call to mi_api_vertex_add or the end of the vertex list append to the current vertex. The next mi_api_vertex_add begins the next vertex.
miBoolean mi_api_vertex_lookup( miGeoVector *pos, /* Output: vertex position */ int idx) /* index of vertex */
This functions looks up the point in space of the vertex specified by the index idx. Vertices, like vectors, are sequentially beginning with 0 in every object group. This function is not essential for scene definition.
miBoolean mi_api_poly_begin( int type, /* 0=concave, 1=convex */ char *material) /* material name */
miBoolean mi_api_poly_begin_tag( int type, /* 0=concave, 1=convex */ miTag material) /* material tag */
After all vectors and vertices have been defined, polygons are defined. Each polygon requires a begin call followed by at least three index add calls followed by an end call. Optionally, holes may be defined. At this time, type must be either 0 (concave or unknown) or 1 (convex). Specifying type 1 improves performance because the tesselator does not need to test the convexity of the polygon. The first polygon in an object group must always specify a material by either a name or a tag, therefore the two begin functions are provided. For all following polygons, a null pointer or a null tag may be passed to specify ``same material as before in the same object group.'' This improves API performance. In the case of tagged objects, the material pointer or tag must be null; the label is passed as an extra index (using mi_api_poly_index_add) before the regular vertex indices.
miBoolean mi_api_poly_index_add( int idx) /* new index to append */
After the polygon begin call, at least three vertices must be specified by index. The first vertex in the current object group has index 0. The order is significant; polygon vertices must be defined in counter-clockwise order. Polygons (and their holes) should be planar. Small deviations are handled gracefully but very large deviations may lead to unpredictable behavior. This function is also used to store the polygon label directly after mi_api_poly_begin if the object is tagged.
miBoolean mi_api_poly_hole_add (void)
After the boundary loop of the polygon has been specified using index add calls, this call may be used to indicate the end of the current loop and the beginning of a hole loop. Hole loops, like boundary loops, consist of a sequence of at least three index add calls. No two loops may intersect, and a hole loop must be fully contained in the boundary loop in the plane of the polygon.
miBoolean mi_api_poly_end (void)
After all boundary and optional hole loops have been added, the polygon must be completed with this call. After the end call, a new polygon may be begun.
miBoolean mi_api_poly_approx( miApprox *approx) /* approximation technique */
After the last polygon an approximation statement may be specified. It is used by the tesselator for displacement-mapped polygons only. Displacement mapping is a feature of the material assigned to polygons.
Object groups containing free-form surfaces also consist of three sections: the vector list, the vertex list (more accurately called the control point list), and the curve and surface lists. The vector list is defined using one call to mi_api_vector_xyz_add for every vector. All vectors have three components X, Y, Z; weights for rational curves and surfaces are specified as part of the vertex references later.
The vertex list is defined with calls to mi_api_vertex_add, exactly like in the polygonal case, except that all normals, textures, and other optional vertex information except motion vectors is ignored and should not be specified. Surfaces always compute their own normals, and there are special constructs for textures and bump maps.
The curve and surface list uses the vertices defined in the first section as control points. Curves can be used as trimming curves, hole curves, and special curves. Vertices can also be used as special points on surfaces. Special points and special curves are always included in the tesselated surface.
miBoolean mi_api_surface_begin( char *name, /* name of the surface */ char *mtlname) /* name of the material */
Begin the definition of a surface with the given name. The name is valid only inside the object group and is used for connections and approximations. The given material is assigned to the surface. The name may not be a null pointer.
miBoolean mi_api_surface_begin_tag( char *name, /* name of the surface */ miTag mtltag) /* name of the material */
This function is similar to mi_api_surface_begin, but instead of a name a material tag must be specified. This tag may not be a null tag.
miBoolean mi_api_surface_params( int dimen, /* miU or muV */ char *basis_name, /* name of basis for this dimen. */ miGeoScalar range_min, /* minimum range */ miGeoScalar range_max, /* maximum range */ miDlist *params, /* parameters for this dimen */ miBoolean rational) /* miTRUE if rational flag present */
A surface has two parameter directions, U and V. After the surface begin call, both must be defined with this call, once with dimen = miU and once with dimen = miV. Both times, the basis name as defined at the beginning of the object definition must be given, as well as the parameter range, the surface parameter list, and the rational flag. The rational flag can either be set here or in the basis; the latter is recommended. The rational flag can be specified here for backwards compatibility with the .mi1 language.
The parameter list is a dlist (dynamic list, see above) containing the parameter vector. The length of this vector depends on the
basis; refer to the .mi2 language specification in the mental ray manual for the exact numbers.
The type of the dlist is miDLIST_GEOSCALAR, containing floating-point numbers of type miGeoScalar.
miBoolean mi_api_vertex_ref_add( int ref, /* vertex reference */ double w) /* homogenous coordinate */
After both surface parameter vectors have been specified, the control points must be listed. The number of control points depends on both bases and the length of their parameter vectors; again refer to the mental ray manual for details. For every control point one call to mi_api_vertex_ref_add is required. If both bases are non-rational, it is sufficient to specify a vertex index ref. As with polygons, 0 selects the first vertex in the current object group. For rational bases, a weight w must be given for every control point reference.
miBoolean mi_api_surface_curveseg( miBoolean newloop, /* is this a new loop */ enum miCurve_type type, /* type of curve */ char *name, /* name of existing curve */ miGeoRange *range) /* range of the curve */
After the surface parameters have been defined, this call can be used to attach curves to the surface. The curve must have been defined in the same object group (see below) and is referenced by name. The type of the curve is one of miCURVE_TRIM, miCURVE_HOLE, and miCURVE_SPECIAL. The parameter range specifies the piece of the curve to be used. Multiple curves or pieces of curves can be concatenated to form the final loop; curves specified by consecutive mi_api_surface_curveseg are concatenated to a single loop by setting newloop to miTRUE for the first call and to miFALSE for all following curves to be appended.
miBoolean mi_api_surface_specpnt( int uv_index, /* 2D point reference */ int v_index) /* optional 3D point */
Attach a special point to a surface. A vertex index uv_index must be given that contains the coordinate in the surface's UV space. The tesselated surface will contain a triangle vertex at that UV coordinate. If v_index is not -1, the special point is assigned the XYZ coordinate contained in the indexed vertex.
miBoolean mi_api_surface_texture_begin( miBoolean is_volume, miBoolean is_bump, char *ubasis_name, miDlist *uparams, miBoolean u_rational, char *vbasis_name, miDlist *vparams, miBoolean v_rational)
This call attaches a texture surface to the most recently defined surface. A texture surface is a simplified type of surface that causes texture vectors to appear in the vertices of the triangles that result from tesselation. Whenever the tesselator creates a triangle vertex at a certain UV coordinate of the main surface, it looks up this UV coordinate in the texture surface and computes the location of the texture surface at that point, and stores that as texture vector. If is_volume is miFALSE, wrap compensation is applied before storing the texture vector; this ensures that texture lookups near the surface seam do not ``rewind.'' Generally it should be miFALSE for 2D textures and miTRUE for 3D (volume) textures. If is_bump is miTRUE, a pair of basis vectors is created in the tesselated surface instead of a texture vector. The bases and parameters in both the U and V directions are specified in the same call; they must use the same parameter ranges as the base surface.
Note: whenever the texture on a surface looks strangely shifted, or if texture coordinates are too large by a constant of 1, the reason is usually that is_volume had not been set to miTRUE on a 3D texture.
miBoolean mi_api_surface_derivative( int degree) /* 1=first, 2=second derivatives */
Specify that during tesselation, first or second derivatives should be computed and stored with the vertices of the tesselated surface that was most recently begun with mi_api_surface_begin. If both first and second derivatives should be stored, this function must be called twice.
miBoolean mi_api_surface_end (void)
After the surface, both of its parameter directions, all its texture surfaces and its derivatives have been specified, this call must be used to complete the surface definition. After this call, the next surface can be started by repeating the sequence beginning with mi_api_surface_begin.
miBoolean mi_api_surface_approx( char *name, /* name of surface */ miApprox *approx) /* approximation technique */
After the definition of a surface is complete, an approximation can be attached to it. The default is parametric approximation. This call can be used to change the default to regular parametric, curvature-dependent or spatial approximations. This call must be used before the object group ends because at that point the surface names go out of scope.
miBoolean mi_api_surface_approx_displace( char *name, /* name of surface */ miApprox *approx) /* approximation technique */
In addition to mi_api_surface_approx, which control the approximation of the base surface, this function stores an approximation for the displaced surface. The default is parametric. This is often useful to provide a high-resolution parametric approximation for the base surface to catch small displacement map features, and a curvature dependent tesselation for the displaced surface to properly approximate the curvature introduced by the displacement.
miBoolean mi_api_surface_approx_trim( char *name, /* name of surface */ miApprox *approx) /* approximation technique */
This function assigns an approximation to the trimming, hole, and special curves attached to the named surface, just like the previous function assigns an approximation to the surface itself.
Curves can be used as trimming curves, hole curves, and special curves as described in the free-form surface section above.
This section lists the API functions to create curves. This can be done inside any object group, after the vectors and vertices are defined, in any place where a surface definition would be legal. Like surface names, curve names are in object group scope and can only be referenced in the object group they are defined in.
miBoolean mi_api_curve_begin( char *name, /* name of the curve */ char *basis, /* name of the basis */ miBoolean rational)
Create a new curve with the given name, using the specified basis. Like surfaces, a rational flag can be set for backwards compatibility reasons. The recommended method is setting the rational flag in the basis. Curves and surfaces can share the same bases.
miBoolean mi_api_curve_specpnt( int t_index, /* 1D point reference */ int v_index)
Attach a special point to a curve at the parameter point v_index of the curve. v_index references the vertex to use; only the X value is used.
miBoolean mi_api_curve_end( miDlist *dlp) /* parameter list of the current curve */
Complete the definition of the curve, and pass the parameter vector. The type of the dlist is either miScalar for the non-rational case or miVref for the rational case.
miBoolean mi_api_curve_approx( char *name, /* name of curve */ miApprox *approx) /* approximation technique */
This function assigns an approximation to the curve. Alternatively, an approximation can be assigned to all curves used in a surface at once using mi_api_surface_approx_trim, which overrides the approximation set with mi_api_curve_approx.
Functions are user-written C routines with parameters that are used as shaders during preprocessing, rendering, and postprocessing. Before a function can be defined (by giving its name and listing its parameter values), it must be declared so mental ray knows the complete parameter list the C function accepts, and the data type for each parameter. For a complete list of available shaders, refer to the mental ray manual.
miParameter *mi_api_parameter_decl( miParam_type type, /* one of miTYPE_* above */ char *name, /* parameter name */ int strlength) /* if string, string length */
Before the function itself is declared, a complete description of the parameters and their types must be built. This function builds a miParameter structure for a single parameter to be appended to the list. The parameter , the parameter name, and the maximum length of the string if the type is miTYPE_STRING, must be specified. The string length includes the trailing null byte.
miParameter *mi_api_parameter_append( miParameter *list, /* list of parameters */ miParameter *parm) /* new parameter */
Parameter structures created with the previous function must be concatenated to a list with this function. It appends a new parameter parm to an existing list list. The parameter pointer created with the definition of the first parameter, which is also the anchor of the parameter list that will be passed to mi_api_function_declare, can be used as list argument, but it is more efficient to pass the parameter pointer returned by the previously appended parameter.
miBoolean mi_api_parameter_child( miParameter *parm, /* struct or array to attach to */ miParameter *child) /* struct or array body to attach */
Function parameter lists are not necessarily linear lists. Parameters of type miTYPE_STRUCT and miTYPE_ARRAY require a sublist of parameters. Arrays always require a single parameter as subtree, which becomes the type of the array, while structures require a list as subtree that contains one parameter for every member of the structure. The sub-parameter or sub-parameter list child is attached to the array or structure parameter parm with this function.
miTag mi_api_function_declare( miParameter *outparms, /* result parameter list */ char *name, /* function name */ miParameter *inparms) /* opaque parameter list */
After the parameter list (or tree, if arrays and structures are present) has been built, the function itself can be declared. The return parameter, the function name (which must agree with the name of the C function to be executed), and the anchor of the parameter list must be passed. This call completes the declaration of a function.
The result parameter list is similar to the input parameter list, but may only be either a simple type or a structure, not an array or a structure containing structures or arrays. The result parameter does not have a name (i.e. the name argument in the call to mi_api_parameter_decl that returned outparms was a null pointer), but if the result parameter is a structure, the structure members must be named as usual.
Earlier versions of the API passed a miParam_type instead of a miParameter. This was changed to allow structured return types. For backward compatibility, mi_api_function_declare still accepts miParam_type codes, but since the function prototype changed the compiler will generate an error unless a cast is applied.
Functions, also called function calls, are references to a particular C function bundled with a set of parameter values. A function definition begins with naming the desired C function, which must have been declared (see above), followed by assignments of numerical or other values to individual parameters. The order of assignments is irrelevant; it is done by parameter name. Parameters that remain unassigned have a numerical value 0 or the empty string.
miBoolean mi_api_function_call( char *name) /* name of called function */
Every function definition begins with this call, which references the C function by name. An error occurs if the name is not declared. This function is special in that it does not insist on name to be declared in the current scope (see Scopes below). Instead, it searches for the function beginning in the innermost scope and works back towards the outermost scope. This is useful because shader declarations usually appear in the form of a $include statement at the beginning of the scene file, it would be inconvenient to force a redeclaration in every scope (such as phenomenon definitions) just to have the correctly scoped names handy.
miBoolean mi_api_parameter_name( char *name) /* name of new parameter */
After mi_api_function_call, the parameter values are assigned. The first step for an assignment is this call, which specifies the name o the parameter to be assigned to. The name must be one of those specified when the function was declared. name is always a simple name; to access the subtrees of arrays and structs special functions (push, pop, array element) are provided, see below. Parameter names may not contain dots (``.'').
miBoolean mi_api_parameter_value( miParam_type type, /* parameter type, one of miTYPE_* */ void *value, /* pointer to value */ int *offs, /* returned offset in parm block */ int *size) /* returned size in parm block */
Assign a value to the parameter named with the previous call. The type of the value being assigned and a pointer to the value must be passed. The type must agree with the declared type of the parameter, following these rules:
miBoolean mi_api_parameter_shader( char *name) /* shader name */
Instead of providing a value to a parameter, register another function by name that is called at runtime to calculate the value. This is used to build dynamic shader trees.
miBoolean mi_api_parameter_interface( char *name) /* interface parameter name */
Instead of providing a value to a parameter, register a link to an interface parameter that provides the value at runtime.
miBoolean mi_api_parameter_push( miBoolean is_array) /* pushing array or struct? */
After the call to mi_api_parameter_name for a structure or array, before values can be stored, the context must be pushed one level down into the structure's or array's parameter subtree. The is_array argument is miFALSE for structures and miTRUE for arrays. After the push, structure members and array elements can be assigned with regular mi_api_parameter_value calls. Every new array member must be preceded with a call to the following function.
miBoolean mi_api_new_array_element (void)
After the push operation, before a new array value can be assigned, this function must be used to make room for another array element. After the first call to this function, mi_api_parameter_value assigns to array element [0], after the next [1] is assigned, and so on. Array elements can only be assigned sequentially. If no values or not enough values are assigned to a parameter, its remaining scalars remain zero.
miBoolean mi_api_parameter_pop (void)
After a structure or array has been assigned its component values, the parameter subtree of the structure or array must be exited with a pop operation. For example, to assign the scalars 1 and 2 to members a and b of structure s, the call sequence is name(s), push, name(a), value(1), name(b), value(2), pop. The order of assignment to a and b is not significant. To assign 3 and 4 to an array a of length 2, the call sequence is name(a), push, new_element, value(3), new_element, value(4), pop. Push/pop pairs can be nested.
miTag mi_api_function_call_end( miTag oldtag) /* if incremental, re-use old tag */
After all parameter values have been assigned, the function definition is completed with this call. The tag of the new function is returned and can now be used where a function is expected during scene definition. Since functions (as opposed to declarations) are not generally named, the standard incremental method cannot be applied; therefore a tag argument can be supplied. If tag is not the null tag, the existing function identified by tag is replaced with the new one such that all references in the scene that referred to the old function now refer to the new one. In this case tag is also returned.
miTag mi_api_function_append( miTag list, /* function list to append to, or 0 */ miTag func) /* function to append to list */
Most regular shaders (not output shaders and inheritance shaders) can be given as a list of shaders. In this case mental ray will call all shaders in the list in sequence, with each successive shader operating on the result of the previous. The main application are lens shaders, which are often lists of lens shaders. To append a function func to another function or function list list this call is used. The first function in the list is the anchor of the list; it can be passed as list when appending to the list but it is more efficient to pass the last function added to the list.
miBoolean mi_api_function_delete( miTag *list) /* function list to delete */
When a scene element referencing functions is changed incrementally, it is usually desired to delete the old function list before a new one is built with calls to mi_api_function_append. This call deletes all functions of the function list, and stores a null tag in the list anchor. The list argument is a pointer to the function list tag to permit this call to clear the function list tag itself.
miBoolean mi_api_shader_add( char *name, /* shader name */ miTag func_tag) /* shader function */
Normally functions are unnamed. This call can be used to assign a name to a function. The main application are procedural textures, which are nothing but a texture shading function accessible by name (the name can be provided as parameter value to texture parameters of other functions). Named shaders also appear in the .mi2 language as shader statements. Note that lights, materials, and other scene elements are more than just shading functions, a named shader can not be used where a material or light is expected.
miTag mi_api_function_assign( char *name) /* name of shader being assigned */
This call can be used to look up a named shader (was given a name by the preceding call) to obtain its tag. The returned tag can be used like a tag returned by mi_api_function_call_end. This is called an ``assignment'' because the .mi2 language allows assigning an existing named shader wherever a shading definition is called; such assignments are introduced with an equals sign.
miBoolean mi_api_shader_call( miTag func_tag, /* function (list) to call */ char *c_inst_name, /* name of camera instance */ char *option_name) /* name of options element */
This function can be used to call a shading function immediately. This call returns after the called function returns. If func_tag is a function list, all functions in the list are called in sequence. The functions are provided with a dummy state containing mostly nulls; if a camera instance or options element are passed by name, appropriate camera and options substructures are placed in the state. Either c_inst_name or option_name may be null pointers.
Phenomena are declared after all the subshaders to be used inside the phenomenon have been declared. Inside the phenomenon, subshaders will be defined, complete with shader name and parameters. Parameters may have constant values, like in a regular shader definition, or they may have subshaders assigned to them, or they may have phenomenon interface parameters assigned to them. It is important to understand that the phenomenon declaration contains shader definitions.
miBoolean mi_api_phen_begin( char *name) /* phenomenon name */
Begin the declaration of a new phenomenon name. After the beginning of the phenomenon, its parameters should be declared in the same way shader parameters are declared, using calls to mi_api_parameter_decl, mi_api_parameter_append, and mi_api_parameter_child. This must be done for both the return parameters and the input parameters. When all parameters have been declared, they are attached to the phenomenon using the next function:
miFunction_decl *mi_api_phen_parameter_end( miParameter *outparms, /* one of miTYPE_* above */ miParameter *inparms) /* opaque parameter list struct */
After the phenomenon was begun and a parameter declaration structure has been built for both the return parameters and the input parameters, this function attaches both to the phenomenon and returns a pointer to the phenomenon. A pointer to the function declaration is returned; the caller should fill in the shader tree tags and all other fields in the phen substructure. Shaders are appended using the standard mi_api_function_append call.
miTag mi_api_phen_end(void)
After the declaration returned by the previous function has been set up, this function terminates the phenomenon declaration. If an error occurs, a null tag is returned; otherwise the tag of the new phenomenon element is returned. The type of the returned element is miSCENE_FUNCDECL.
In most cases, compiled C shading functions will be linked to the program as shared libraries. The library can also link existing object files and C source files. This is handled by the LINK module and is outside the scope of the API module. However, API allows verbatim specification of C sources as part of the scene definition with the following three calls. Use of these functions is discouraged except when parsing .mi2 scene files because the overhead is significantly higher than linking shared libraries.
miBoolean mi_api_code_verbatim_begin (void)
Begin the definition of verbatim code. This call opens a temporary file in /usr/tmp with a unique file name that receives the C sources.
miBoolean mi_api_code_byte_copy( int length, /* number of data bytes to copy */ miUchar *bytes) /* data array, contains C source */
Append ascii source text to the C source file opened by the preceding call. The bytes string is expected to contain length characters, not counting a trailing null byte (which is not required). The bytes pointer, like any other string passed to an API call, is released using mi_mem_release.
miBoolean mi_api_code_verbatim_end (void)
After the sources have been stored with one or more byte copy calls, this call completes the verbatim source definition. The temporary file is closed, compiled, linked, loaded, and removed.
Tesselation is the operation that turns the defined scene into a renderable representation containing triangles, ready for rendering. The actual render process that creates images or other representations of the triangles is outside the scope of API. The caller of API is free to let every type of renderer use the data created here: ray tracers, scene viewers, wireframe viewers, etc.
miBoolean mi_api_render( char *group_name, /* root group */ char *c_inst_name, /* camera */ char *option_name, /* options */ char *inh_name) /* inheritance function or 0 */
After the scene has been set up, it can be prepared for rendering. This can be done implicitly using frame calls (see above), but the recommended method uses direct render calls. The name of the root instgroup, the name of the camera instance to use (which must also be added to the root group explicitly), the options element name, and an optional inheritance function name must be given. The inheritance function is not a shader, has no custom parameters and is not declared. It is sufficient if it exists under the given name in one of the libraries linked with the LINK module. mi_api_render does not start rendering itself but only does some checks, prepares API for rendering and returns to the caller.
miBoolean mi_api_render_params( miTag *root_group, miTag *camera_inst, miTag *camera, miTag *options, miInh_func *inheritance_func)
Give access to the render parameters. This function can be called after a successful mi_api_render or mi_api_frame_end.
The function using these parameters should call mi_api_output_list if output shaders should be supported and mi_scene_preprocess for tesselation and other preprocessing. Next, the tesselated data can be picked up for display or rendered using mi_rc_render if the RC module is available. Next, mi_scene_postprocess must be called for cleanup. Finally, if output shaders should be supported, mi_api_output_shaders must be called. Two sample render functions are listed at the end of this chapter.
Scopes disambiguate name spaces. The current scope is defined by a string that is prepended to all toplevel entity names (objects, materials, lights, functions, declarations, etc) passed to API. Non-toplevel entities such as surfaces, curves, and shader parameters whose lifetime are limited to the enclosing toplevel entity are not scoped. The scope string is initially empty. Scopes can be nested. Scopes in the scope string are separated by double colons; the scope string ``a::b::'' is the result of first beginning scope ``a'' and then scope ``b''. If applied to a name ``x'', the resulting name is ``a::b::x''. Since symbol tables contain scoped names, this can be used to distinguish different ``x''.
For example, mi_api_phen_begin automatically begins a new scope with the phenomenon name. This causes the phenomenon name and a double colon to be prepended to all shaders defined inside the phenomenon, which allows different phenomena to have subshaders with the same name. If phenomena ``phen1'' and ``phen2'' both have a subshader `` shader'', no conflict arises because their internal names are `` phen1::shader'' and ``phen2::shader''. The scoping functions are available to other modules, for example to allow the shader editor to read a declaration file without causing conflicts with the loaded scene. The name of the declared phenomenon itself, like the name of any other declaration, is global and does not get a scope prepended.
Every context has its own scope string (see LIB module about contexts).
miBoolean mi_api_scope_begin( char *name) /* new scope name */
Enter a scope. The given name and two colons (in this order) are appended to the scope string.
miBoolean mi_api_scope_end(void)
Exit a scope. The last name and double colon is stripped off the scope string.
char *mi_api_scope_apply( char *name) /* name to put in scope (allocated) */
Prepend the scope string to name, if any. If name contains double colons, check the scope part of name against the scope, and return a null pointer if the check fails. For example, it is an error for name to contain double colons if there is no scope; assuming that the scope is ``a::b::'', the names `` x'', ``b::x'', and ``a::b::x'' are all ok and return the same string ``a::b::x'' but ``a::x'' is an error. As usual, name must have been allocated using MEM functions. The returned string is allocated with MEM and may be identical to name.
The following functions accept char* arguments that are passed through mi_api_scope_apply automatically by API:
mi_api_delete, mi_api_delete_tree, mi_api_options_begin, mi_api_camera_begin, mi_api_render, mi_api_light_begin, mi_api_light_lookup, mi_api_material_begin, mi_api_material_lookup, mi_api_texture_begin, mi_api_texture_lookup, mi_api_instance_begin, mi_api_instance_end, mi_api_instgroup_begin, mi_api_instgroup_additem, mi_api_object_begin, mi_api_poly_begin, mi_api_surface_begin (material name only), mi_api_function_assign, mi_api_function_call, mi_api_parameter_shader, mi_api_shader_add, mi_api_shader_call, and mi_api_gui_begin.
Subsection Miscellaneous
miBoolean mi_api_delete( char *what) /* name of element to delete */
Delete a single element from the scene database. This function may be used only after all references to the element have been deleted. For example, before deleting an object all instances referencing it must be deleted, or incrementally changed to reference something else, and so on.
miBoolean mi_api_delete_tree( char *what) /* name of element to delete */
Delete a tree anchored at the named element in the scene database. This function takes care of deleting all members of the tree in the proper order such that references to an element are removed before the element itself is deleted, but only within the named tree - multiple instancing from outside is not recognized, and outside references to what itself are also not removed.
miBoolean mi_api_debug( char *what, /* string describing what to dump */ char *arg) /* if scene dump, which item */
This function is useful for debugging code that calls the API module. It is not essential for scene definition. The what argument specifies what to check, and the arg argument specifies the name of a scene element to be checked, depending on what is being checked. The following what strings are supported:
what arg operation
sym -- print all symbol tables sym global -- print the global (.mi2) symbol table sym declare -- print the declaration symbol table sym options -- print the .mi1 options symbol table sym camera -- print the .mi1 camera symbol table sym light -- print the .mi1 light symbol table sym object -- print the .mi1 object symbol table sym instance -- print the .mi1 instance symbol table sym group -- print the .mi1 instance group symbol table sym material -- print the .mi1 material symbol table sym ctexture -- print the .mi1 color texture symbol table sym stexture -- print the .mi1 scalar texture symbol table sym vtexture -- print the .mi1 vector texture symbol table mem summary -- print per-module MEM memory usage summary mem dump -- print all allocated MEM memory blocks mem check -- run a consistency check on MEM memory db statistics -- print DB database access statistics db dump -- print a list of all DB database entries task statistics -- print TASK task queue statistics img verbose -- future image file accesses print messages scene check element consistency check of a scene element scene dump element recursively print info on scene element scene alldump element recursively print detailed element info
Here are two sample render functions. The first uses the optional RC module to render the scene:
miBoolean render_function( miTag root_group, /* root of scene to render */ miTag c_inst_tag, /* camera instance */ miTag camera_tag, /* camera (== inst->item) */ miTag option_tag, /* RC options */ miInh_func inh_func) /* inheritance function */ { miCamera *cam; /* camera in memory */ miScene_preprocess control; miTag images[miRC_MAX_IMAGES]; int type; cam = mi_db_access(camera_tag); mi_api_output_imfdisp_handle(cam->x_resolution, cam->y_resolution, 1.0); miSCENE_PREPROCESS_CONTROL_INIT(control); control.root_group = root_group; control.camera_inst = c_inst_tag; control.options = option_tag; control.object_space = control.render_space = 'o'; control.cache_boxes = miTRUE; control.time = cam->frame_time; control.inheritance_func = inh_func; if (!mi_scene_preprocess(&control)) { mi_error("scene preprocessing failed"); mi_db_unpin(camera_tag); mi_db_unpin(option_tag); return(miFALSE); } mi_debug("preprocessed %d boxes, %d leaf instances, %d lights", control.no_boxes, control.no_leaf_insts, control.no_lights); /* * render scene */ (void)memset(images, miNULLTAG, miRC_MAX_IMAGES * sizeof(miTag)); mi_api_output_list(option_tag); mi_rc_render(c_inst_tag, control.lights, control.no_lights, control.leaf_insts, control.no_boxes, option_tag, images); mi_api_output_shaders(c_inst_tag, option_tag, images); mi_scene_postprocess(&control); for (type=0; type < miRC_MAX_IMAGES; type++) if (images[type] != miNULLTAG) mi_scene_delete(images[type]); mi_db_unpin(camera_tag); return(miTRUE); }
The second example is simpler. It tesselates the scene but does not render, and does not call output shaders. It does make the simplifying assumption that the accessed object is a simple object, not a merge group.
miBoolean tesselation_function( miTag root_group, /* root of scene to render */ miTag c_inst_tag, /* camera instance */ miTag camera_tag, /* camera (== inst->item) */ miTag option_tag, /* RC options */ miInh_func inh_func) /* inheritance function */ { miCamera *cam; /* camera in memory */ miTag obj_tag; /* object tag */ miObject obj; /* object */ miTag box_tag, next; /* box tag */ miBox box; /* box */ miScene_preprocess control; cam = mi_db_access(camera_tag); miSCENE_PREPROCESS_CONTROL_INIT(control); control.root_group = root_group; control.camera_inst = c_inst_tag; control.options = option_tag; control.object_space = control.render_space = 'o'; control.cache_boxes = miTRUE; control.time = cam->frame_time; control.inheritance_func = inh_func; if (!mi_scene_preprocess(&control)) { mi_error("scene preprocessing failed"); mi_db_unpin(camera_tag); return(miFALSE); } mi_debug("preprocessed %d boxes, %d leaf instances, %d lights", control.no_boxes, control.no_leaf_insts, control.no_lights); /* * Operate on the boxes of one tesselated source object, "objname". * The do_something_with_box function could display a wireframe. * Use of S_GLOBAL implies non-frame mode, otherwise use S_OBJECT */ obj_tag = (miTag)mi_sym_lookup_attribute(S_GLOBAL, "objname", 0); obj = mi_db_access(obj_tag); for (box_tag=obj->boxes; box_tag; box_tag=next) { box = mi_db_access(box_tag); next = box->next_box; do_something_with_box(box); mi_db_unpin(box_tag); } mi_db_unpin(obj_tag); mi_scene_postprocess(&control); mi_db_unpin(camera_tag); return(miTRUE); }
Limitations and Future Changes
The API module is a new development and still contains a small number of idiosyncrasies that will be fixed in later releases. The following points will be addressed:
Geometry Shader Data Structures
This chapter describes the data structures that define a scene in memory. While the scene is constructed using API calls, there is not an API call for all operations that merely write a value into an existing data structure. Instead, the mi_api_*_begin calls return a pointer to the data structure just created and expect the caller to fill in simple parameters. This chapter describes these data structures.
This chapter discusses every element type in detail. There is one subsection for each element type. Each describes
typedef struct { void *address; /* D: address in the cache if nonzero*/ void *overlay; /* S: incr mode: editable overlay */ miUint size; /* D: size of item in bytes */ miUint oversize; /* S: if overlay, size of overlay */ miUint time; /* D: least-recently-used timestamp */ int refs; /* S: reference count, for un/link */ miUchar pinned; /* D: pin counter, 0 means unpinned */ miUchar type; /* D: data type, one of miSCENE_* */ miUchar module; /* D: module that created tag */ miCBoolean valid; /* D: tag is in use, miInfo is valid */ miCBoolean dirty; /* S: has bn edited since preprocess */ miCBoolean newmatrix; /* S: during preproc, dirty transform*/ miCBoolean traversed; /* not used */ miCBoolean spare; /* not used */ } miInfo;
overlay is used to save the overlay buffer when the element is edited in parallel incremental mode. DB continues to maintain the address of the element as it is being rendered; the overlay pointer holds the address of the new data. If the element has not been edited, the pointer is null.
oversize is the overlay size which may become different from the base item size. If it is 0 and the address is nonzero, the item has been deleted but the deletion has been deferred until incremental mode ends.
refs is the reference count for the element. It is modified by the link and unlink functions. If the reference count reaches 0, the element and its children are deleted recursively by decrementing their reference counts. Only objects, groups, lights, transformations, cameras, and instances have reference counts. The reference count is not incremented when a tag is stored in a shader parameter block.
dirty is set by Scene when an element has been edited since the last preprocessing, and informs the next preprocessing that an action to remake the renderable representation may be required.
newmatrix is used during preprocessing. It is set in instances whose transformation functions were found to be dirty in the first pass of preprocessing, informing the second pass of the change in orientation.
traversed is not currently used.
spare is not currently used.
Element type: miSCENE_INSTANCE Data type: miInstance Sizes: int param_size Defaults: all nulls, two identity matrices, parameter size as given by size argument
enum miGenMotionType { miGM_INHERIT = 0, miGM_TRANSFORM, miGM_OFF }; typedef struct miTransform { miTag function; /* function, may be null tag */ float time; /* the last evaluation time */ miMatrix global_to_local; /* transformation in world */ miMatrix local_to_global; /* transformation to world */ miCBoolean identity; /* SCENE: matrices are ident.*/ miCBoolean spare[3]; /* SCENE: not used */ int id; /* SCENE: unique transform ID*/ } miTransform; typedef struct miInstance { miTransform tf; /* space transformation */ miMatrix motion_transform; /* motion transformation */ miTag item; /* instanced item */ miTag material; /* inherited material */ miTag parent; /* SCENE: leaf inst. parent */ miTag boxes; /* SCENE: renderable repres. */ miTag next; /* SCENE: leaf instance list */ miTag prev; /* SCENE: leaf instance list */ int mtl_array_size; /* if mtl array, array size */ int n_boxes; /* SCENE: # of boxes attached */ int n_relations; /* SCENE: # of valid relations*/ int max_relations; /* SCENE: size of relat. array*/ miTag geogroup; /* SCENE: geomshader group */ miCBoolean off; /* ignore this instance */ miUint1 gen_motion; /* motion information */ miUint1 visible; /* visible ? */ miUint1 shadow; /* casts shadow? */ miUint1 trace; /* visible as reflection? */ miUint1 caustic; /* caustic bitmap */ miCBoolean spare_1[2]; /* not used */ int spare_2; /* not used */ miTag param_decl; /* parameter declaration */ int param_size; /* parameter size in bytes */ char param[8]; /* arbitrary-sized parameters*/ } miInstance;
A translator must provide: either tf.function and time, or global_to_local and local_to_global; also item (using mi_scene_link), param_decl, param_size, and param.
All fields except the matrices, the material, and the parameters are reserved for Scene and may not be modified by other modules. The tags can be modified indirectly with the link and unlink functions, which take care of reference counts. The instance element has two variable parts; one for inherited shader parameters and one for the relation list. The latter is maintained exclusively by Scene.
tf.function optionally points to a transformation function that computes a matrix from the current time. If present, this function is called during preprocessing with three parameters: a pointer to the result global--to--local matrix, the instance tag, and the time as given in the preprocessing control structure.
tf.time is the time the evaluation function, if present, was last called with. If it matches the current time, the matrices need not be calculated by calling the function again.
tf.global_to_local transforms the parent space to the local space of the instanced subtree. If a transformation functions exists, it writes its result here; if not, the translator must store an appropriate matrix here.
tf.local_to_global is the inverse matrix, set by Scene after the transformation function (if it exists) has returned the tf.global_to_local matrix.
tf.identity is miTRUE if the transformation matrices in this instance are identity transforms (this saves the renderer unnecessary ray transformations).
tf.id is a unique transformation ID. Scene creates it during preprocessing, trying to assign identical IDs to identical transformations. This is especially important for leaf instances: the renderer does not have to retransform the current ray if the new box has the same transformation (same ID) as the previous.
motion_transform specifies the transformation from parent space to local space for motion blur transforms. If this is a null matrix, the instance transformation tf is used.
item is the tag of the element being instanced. Only groups, cameras, lights, objects, functions and boxes can be referenced. If a box is referenced, it is taken as a ``trivial object'' that does not need tessellation; boxes that result from tessellation are not attached here. If a function-list is instanced, actually the geometry shader created scene elements are intended to be instanced.
material is either a material (if mtl_array_size is 0) or a material list (if mtl_array_size is nonzero) used for material inheritance (as opposed to parameter inheritance). During scene preprocessing, the non-null material tag closest to a geometrical object (lowest in the DAG hierarchy) becomes the default material for any polygon or surface in the object that does not have its own non-null material tag. If the tag references a list (type miSCENE_TAG) or materials, it is expected to contain mtl_array_size tags, which will be indexed with the polygon or surface label during rendering.
mtl_array_size Specifies the number of tags in the material tag list if nonzero. If the material tag specifies a material directly, it must be zero. This number is used during rendering as a flag that tells mental ray that the material tag references a list, and also how many items there are in the list to prevent array bounds overflows.
boxes is used to point to boxes that resulted from tessellation. They are used in two cases only: object or box instances attach to the cached untransformed boxes (see above for a definition) if enabled; and leaf instances attach to the final renderable representation.
n_boxes is the number of boxes attached to boxes. Keeping this separate avoids having to access boxes to count them.
n_relations is the number of valid relation tags in the relation list that follows the inherited shader parameters array.
max_relations is reserved for internal use by Scene. It is the allocated size of the relation list (number of tags), which is normally larger than n_relations to avoid frequent reallocations.
geogroup is used to point to the group element which contains all the geometry-shader created scene elements for this instance.
off, if true, stops the recursive descent during preprocessing as if the instance and its instanced item didn't exist. This is useful for temporary suspending subtrees without deleting them.
gen_motion specifies the motion blur generation for the instance. Setting this field to miGM_OFF switches off motion blur for this instance. Motion blur is always generated when this field is set to miGM_TRANSFORM. The parent instance determines whether motion blur is active or not when it is set to miGM_INHERIT.
visible, shadow, trace and caustic flags are inherited in the scene DAG. During scene preprocessing, the non-null flag in the DAG instance closest to a geometrical object (lowest in the DAG hierarchy) is put into the leaf instance. Before rendering starts the leaf instance flags are merged with the object flags and stored in internal raylib rendering data structures. In the merging operation these flags are treated as "fuzzy booleans", that is, a zero value means that the flag is ignored (the object flag is taken), 1 disables the flag (even if the object has enabled it) and 2 enables it (even if the object has not set the flag). The caustic bitmap has the same bit interpretation as the corresponding field in the miObject, but has two more bits defined here. Bit 2, if set, disables generation of caustics cast by this object. Bit 3, if set, disables receiving caustics. In the merging bit 0 and bit 1 of the leaf instance and object caustic bitmaps are logically combined with an OR operation followed by AND of the instance caustic bitmap bits 2 and 3 (shifted right two times).
param_decl points to a parameter declaration that describes the parameters. If parameters exist, this tag must be set by the translator to allow data exchange with other hosts. If it is missing, no data will be swapped regardless of the remote host's byte order. In leaf instances, Scene stores the most recent parent instance's parameter declaration tag here.
param_size is the size of the parameters in bytes. It is taken from the first and only argument of the create and resize calls. In the leaf instance, the inheritance function determines the size.
param is an arbitrary-sized array of parameters stored by the translator. During preprocessing, the DAG is traversed, and the parameters are passed to an inheritance function that is called for every instance found during traversal. The end result is stored in the leaf instance by Scene.
Element type: miSCENE_GROUP Data type: miGroup Sizes: int max_kids, int max_connections Defaults: all nulls, max_* as given by size arguments
typedef struct miGroup { miGeoScalar merge; /* merge tolerance */ miTag boxes; /* SCENE: cached shared tessellation */ int n_boxes; /* SCENE: # of boxes attached */ miBoolean merge_group; /* perform merging on members? */ int max_kids; /* number of kids allocated */ int no_kids; /* number of kids actually used */ int max_connections;/* number of connections allocated */ int no_connections; /* number of connections used */ int pad; /* not used, align conns on 64 bits */ miTag kids[1]; /* kid list (instances) */ } miGroup; typedef struct { miTag instance[2]; /* the two face type object instances*/ miGeoIndex face[2]; /* indices of the two miFace's */ miGeoIndex curve[2]; /* indices of the two miCurves*/ miGeoRange range[2]; } miConnection;
A translator must provide: nothing.
A translator may provide: merge_group, merge, the connection array.
Groups have two variable-sized sections, one for the list of instances and one for the list of connections. The latter exists only if the group is a merge group. The macro miGROUP_GET_CONNECTIONS(n) returns a miConnection pointer to the n-th connection. Note that instances must be added to groups with mi_scene_link, but the connection tags are written directly to the miConnection.
boxes is used to point to boxes that resulted from tessellation. They are used in two cases only: object or box instances attach to the cached untransformed boxes (see above for a definition) if enabled; and leaf instances attach to the final renderable representation.
n_boxes is the number of boxes attached to boxes. Keeping this separate avoids having to access boxes to count them.
merge_group is miTRUE if this group is a merge group. Merge groups are treated like objects; all their subobjects are tessellated as one object. In fact, the Scene module treats merge groups like objects and lets GAP pick apart the group and its subtrees.
merge is the merge epsilon. It is used only in merge groups.
max_kids is the current size of the kids array.
no_kids is the number of instances stored in the kids array.
max_connections is the current size of the connection array.
no_connections is the number of connections in the connection array.
kids is the beginning of the variable section of miGroup, consisting of an array of instance tags followed by an optional array of connections.
Element type: miSCENE_CAMERA Data type: miCamera Sizes: --- Defaults: as described below
typedef struct miCamera { miBoolean orthographic; /* orthographic rendering? */ float focal; /* focal length */ float aperture; /* aperture */ float aspect; /* aspect ratio of the image */ miRange clip; /* min/max clipping */ int x_resolution; /* x resolution */ int y_resolution; /* y resolution */ struct {int xl, yl, xh, yh} window; /* corners of subwindow */ miTag volume; /* opt. volume shader */ miTag environment; /* opt. environment shader */ miTag lens; /* opt. lens shader */ int frame; /* current frame number */ float frame_time; /* ... as time in seconds */ int frame_field; /* 0=frame, 1/2=field number */ } miCamera;
A translator must provide: nothing.
A translator may provide: all fields.
orthographic (default miFALSE) switches the camera from the standard pinhole camera to an orthographic camera.
focal (default 1.0) is the focal length of the camera (the distance between the camera and the viewing plane).
aperture (default 1.0) is the width of the viewing plane in 3D space.
aspect (default 1.0) is the height of a pixel 1 unit wide.
clip (default 0.0001, 1000000.0) describes the minimum (hither) and maximum (yon) limits of the scene. Geometry outside these limits will not be rendered.
x_resolution (default 768) is the width of the rendered image in pixels.
y_resolution (default 576) is the height of the rendered image in pixels.
window (default 0, 0, 65535, 65535) describes the lower left and upper right corners of the rendered subwindow. Pixels outside this window will be black. If the upper right pixel exceeds the resolution, it is clipped to the resolution.
volume (default miNULLTAG) is the optional atmosphere volume shader.
environment (default miNULLTAG) is the optional view environment shader that is called for all (including primary) rays leaving the scene (the active environment shader for secondary rays may be overridden by materials).
lens (default miNULLTAG) is the optional first lens shader.
frame (default 0) is the frame number of the current render. It is not used by mental ray but is accessible to shaders.
frame_time (default 0.0) is the same as the frame number, but expressed in seconds. It is not used by mental ray.
frame_field (default 0) is the number after the field substatement in a frame statement. Since frame numbers are integers, the field number must be used to distinguish the first and second field of the frame, if field rendering is in effect. By convention, 0 means that the entire frame is rendered; 1 is the first (odd) and 2 is the second (even) field. It is not used by mental ray.
Element type: miSCENE_LIGHT Data type: miLight Sizes: --- Defaults: as described below
enum miLight_type { miLIGHT_ORIGIN, miLIGHT_DIRECTION, miLIGHT_SPOT }; enum miLight_area { miLIGHT_NONE = 0, miLIGHT_RECTANGLE, miLIGHT_DISC, miLIGHT_SPHERE, miLIGHT_CYLINDER }; typedef struct miLight { enum miLight_type type; /* light type */ enum miLight_area area; /* area? */ miScalar exponent; /* global illum. 1/r^(2*exp)*/ unsigned int caustic_photons;/* caustics photons to make */ unsigned int global_photons; /* global photons to make */ miColor energy; /* global illum. intensity */ miTag shader; /* light shader */ miTag emitter; /* photon emitter shader */ miVector origin; /* opt. origin */ miVector direction; /* opt. normalized direction */ float spread; /* size of spot? (cos angle) */ union { struct miLight_rectangle rectangle; struct miLight_disc disc; struct miLight_sphere sphere; struct miLight_cylinder cylinder; } primitive; /* area primitive */ short samples_u; /* area u samples */ short samples_v; /* area v samples */ short low_samples_u; /* low area u samples */ short low_samples_v; /* low area v samples */ short low_level; /* switch to low at this lvl */ short spare1; /* not used */ miBoolean use_shadow_maps;/* for this light */ miTag shadowmap_file; /* the shadow map file */ int shadowmap_resolution; /* resolution */ float shadowmap_softness; /* sample region size */ int shadowmap_samples; /* #samples */ miBoolean visible; /* visible? area lights only */ int label; /* light label */ int spare; /* not used */ } miLight; struct miLight_rectangle { miVector edge_u; miVector edge_v; }; struct miLight_disc { miVector normal; miScalar radius; }; struct miLight_sphere { miScalar radius; }; struct miLight_cylinder { miVector axis; miScalar radius; };
A translator must provide: type, shader, origin and/or direction and spread depending on type, all primitive fields for area light sources.
type (default miLIGHT_ORIGIN) distinguishes between point lights (origin only), directional lights (direction only), and spot lights (origin, direction, and spread angle).
area (default miLIGHT_NONE describes the type of area light geometry, and is one of miLIGHT_NONE, miLIGHT_RECTANGLE, miLIGHT_DISC, miLIGHT_SPHERE, and miLIGHT_CYLINDER.
exponent (default 2) controls the falloff of the light at a given distance. An exponent of 2 is physically correct, but other exponents can be chosen to make the light reach farther or less far than it should. An exponent of 1 means that the light energy does not fall off with distance. Exponents other than 2 disturb the energy balance in the scene.
caustic_photons are the number of photons to create in the caustic photon map.
global_photons are the number of photons to create in the globillum photon map.
shader (default miNULLTAG) is the tag of a database element of type miSCENE_FUNCTION containing the light shader, which computes illumination by this light at render time.
emitter (default miNULLTAG) is the tag of a database element of type miSCENE_FUNCTION containing the light photon emitter shader, which emits photons during the global illumination or caustics preprocessing phase.
origin (default 0, 0, 0) is the origin of the light in object space. It is used only if type is miLIGHT_ORIGIN or miLIGHT_SPOT.
direction (default 0, 0, 0) is the direction of a directional light. It is used only if type is miLIGHT_DIRECTION or miLIGHT_SPOT.
spread is used only by spot lights and it specifies the size of the outer spotlight cone.
primitive contains the size of the area light source if area is not miLIGHT_NONE. Depending on area, the width and height of the rectangle are given in rectangle.edge_u and rectangle.edge_v, or the orientation and radius of the disc are given in disc.normal and disc.radius, or the radius of the sphere is given in sphere.radius, or the axis and radius of the cylinder are given in cylinder.axis and cylinder.radius. All defaults are 0.
samples_u (default 3) is the number of samples taken in the U direction of the area light source if area is not miLIGHT_NONE.
samples_v (default 3) is the number of samples taken in the V direction of the area light source if area is not miLIGHT_NONE.
low_samples_u (default 2) is the number of samples taken in the U direction of the area light source if area is not miLIGHT_NONE, when the trace depth specified by low_level is reached or exceeded.
low_samples_v (default 2) is the number of samples taken in the V direction of the area light source if area is not miLIGHT_NONE, when the trace depth specified by low_level is reached or exceeded.
low_level (default 0) is the sum of the reflection and refraction trace depth at which area light sampling switches from samples to low_samples. 0 means that no switching takes place and samples are always used.
the V direction of the area light source if area is not miLIGHT_NONE, when the trace depth specified by low_level is reached or exceeded.
use_shadow_maps specifies whether shadowmap are used for this light source.
shadowmap_file is a tag to a string containing the filename for the shadowmap. The tag can also be miNULLTAG and no file loading/saving will be done.
shadowmap_resolution is the resolution of the shadow map.
shadowmap_softness is the size of the region in the shadow map in which the samples are placed. If this parameter is 0 only one sample will be used = sharp shadows.
shadowmap_samples is the number of samples taken from the shadow map.
visible is miTRUE if the light should be seen in the rendering. This only applies to area light sources.
Element type: miSCENE_FUNCTION Data type: miFunction Sizes: int param_size Defaults: all nulls
enum miFunction_type { miFUNCTION_C, /* regular C/C++ shader */ miFUNCTION_PHEN, /* phenomenon interface */ miFUNCTION_C_REQ, /* C/C++ shader with reqmnts */ miFUNCTION_OUTFILE /* write an image to a file */ }; typedef struct miFunction { miPointer user; /* for shader (init data etc)*/ miPointer cached_address; /* for RC only */ miPointer cached_result; /* array[thread#] of results */ miPointer cached_ghost_ptr; /* if ghost_offs, tag ptrs */ miPointer gcache_timestamp; /* if ghost_offs, eval time */ miPointer lightlist; /* array of lights for shader*/ miLock lock; /* local shader lock */ enum miFunction_type type; /* C function or phenomenon */ miUint out_typemap; /* if output, IMG_TYPE bitmap*/ miUint out_interpmap; /* if output, interpolate bm */ miTag function_decl; /* function declaration */ miTag next_function; /* next function call in list*/ miTag parameter_indirect; /* get params from this func */ miTag interfacephen; /* parent phen, if any */ miTag spare_tag; /* not used */ int parameter_size; /* size of parameter block */ int result_size; /* size of result struct */ int ghost_offs; /* offset to tag ghost, or 0 */ miCBoolean has_init_shader; /* init shader exists */ miCBoolean func_init; /* was function initialized? */ miCBoolean inst_init_req; /* init every instance too? */ miCBoolean inst_init; /* was instance initialized? */ miCBoolean no_expl_params; /* candidate for indirect par*/ miCBoolean spare[5]; /* not used */ miUchar ret_type; /* PHEN:return type of shader*/ miUchar label; /* sequential # for bitmasks */ char parameters[8]; /* parameter block, then */ /* miTag ghost for phenomena */ } miFunction;
A translator must provide: function_decl (using mi_scene_link), parameter_size (as mi_scene_create argument), result_size, parameters.
A translator may provide: next_function.
Functions are shading functions are pairs of a function identification (the shader name) and a parameter block. They are used for materials, textures, lights, transformations, and many other purposes.
Most are called during rendering; some are called during preprocessing or tessellation.
user is not used by mental ray. It is provided so init shaders can access the function and hang local instance data (color tables etc) off this pointer. Previous versions of mental ray required hacks such as adding dummy integers to the shader parameters as a place to store instance data.
cached_address is used to reduce the number of lookups of shader names. After the first lookup, the result of the lookup is cached, and all future lookups first check the address and skip the lookup if it is nonzero. All addresses are stored as a miPointer to ensure that they occupy 64 bits in the data structure, regardless of whether the host uses 32-bit or 64-bit pointers.
cached_result is used by mi_call_shader and similar functions to cache the result of the last call to this shader. The cache is an array of result_size bytes for each thread. This data structure is maintained by mi_call_shader and should not be modified by translators. This and the next two fields exist to support phenomena.
cached_ghost_ptr is also created and used exclusively by mi_call_shader. It points to an array that has twice the size of the standard tag ghost array (see parameters below), and contains one pointer for every tag in that array. It is twice the size because a miPointer must be 64 bits wide. The pointer array is used to avoid looking up tags all the time.
gcache_timestamp is also created and used exclusively by mi_call_shader. It points to an array that contains one timestamp for every render thread. Whenever a new phenomenon is executed, a global time (per thread) is incremented. If this global time does not agree with the timestamp, cached_result is not valid and the shader must be called. If they do agree, the shader has been called before inside the current phenomenon, and the cached result can be used directly.
lightlist is a pointer to an array of light tags. It is used for shaders that have their light list modified according to the global light list.
lock is a lock shared by all instances of a shader. Every shader such as soft_material has exactly one local lock that is shared by all concurrent invocations of this shader.
out_typemap is a bitmap that contains the image types needed for the function if it is either an output shader or an output file.
out_interpmap is a bitmap that contains the interpolation flags for each bit in the out_typemap bitmap.
function_decl is the declaration of the function, containing both the shader name and a declaration of the parameters it requires. Declaration database elements have type miSCENE_FUNCTION_DECL; see below.
next_function references the next function in a chain. Some shader types allow chaining; for example there may be multiple lens shaders anchored in the camera that are called in sequence.
has_init_shader and the other three flags help to determine whether the function and the function instance have been initialized. Except for func_init these flags are copies from the declaration (see below) and are here to find out quickly whether it is necessary to access the declaration and do a full init. They are used internally only to cache declaration flags.
parameter_indirect, if nonzero, is the tag of another function that contains the parameters to be used for this function. parameter_size and parameters are unaffected. This is used for parameterless shadow and photon shaders, which are called with the corresponding material shader's arguments to avoid having to copy the parameters.
interfacephen is the tag of the phenomenon that contains the shader. Presently only used internally to cache the phenomenon tag for lens shaders required by a phenomenon. Should not be used by translators.
parameter_size is the size of the shader parameter area in bytes. Shader parameters are stored at the beginning of the parameters array.
result_size is the number of bytes in the result data structure. By default this is 16 (the size of miColor, which is the default return type). It must be set to match the result_size field of the declaration.
ghost_offs is nonzero for phenomenon miFunctions and provides an offset into the parameters array where the tags begin. This value is either zero or the smallest multiple of 4 equal to or greater than parameter_size because tags are integers that must be properly aligned.
ret_type is internally used by the PHEN module for caching the function declaration return type.
label is a number in the range 0 ... 255 that helps distinguishing shaders. A new number is assigned automatically whenever a new function is created, but no attempt is made to keep the numbers unique. This helps the incremental rendering heuristics to decide whether a pixel should be re-rendered.
parameters contains one or two variable-sized data areas: the function parameters, arranged as described in the mental ray User Manual, and the optional tag ghost array. A pointer to the parameter block is passed to the shader whenever it is called. The parameters are expected to be laid out as described by the function declaration.
The tag ghost array is used for phenomenon miFunctions. It exists only if ghost_offs is nonzero. It has the same layout as the parameter array, such that there is one tag for every parameter at the same offset in the respective array. If the tag for a parameter is nonzero, the tag points to another miFunction which must be called (unless its cache is valid, in which case the previous result is re-used; see above) to provide the parameter instead of using the parameter directly. In this case the parameter in the parameter array is the offset into the result returned by the call.
Element type: miSCENE_FUNCTION_DECL Data type: miFunction_decl Sizes: int decl_size Defaults: all nulls, type miFUNCTION_C
typedef enum { miTYPE_BOOLEAN = 0, /* simple types: used for */ miTYPE_INTEGER, /* returns and parameters */ miTYPE_SCALAR, miTYPE_STRING, miTYPE_COLOR, miTYPE_VECTOR, miTYPE_TRANSFORM, miTYPE_SHADER, /* complex types: used for */ miTYPE_SCALAR_TEX, /* parameters only */ miTYPE_COLOR_TEX, miTYPE_VECTOR_TEX, miTYPE_LIGHT, miTYPE_STRUCT, miTYPE_ARRAY, miTYPE_TEX, miTYPE_MATERIAL, /* phenomenon types */ miTYPE_GEOMETRY, miNTYPES } miParam_type;
typedef struct miPhen_decl { int n_subtags; /* # of subshader/mtl tags */ miTag root; /* root attachment point */ miTag lens; /* optional lens shaders */ miTag output; /* optional output shaders */ miTag volume; /* optional volume shaders */ miTag environment; /* optional environm. shaders*/ miTag geometry; /* optional geometry shaders */ miTag contour_store; /* opt'l contour store func */ miTag contour_contrast; /* opt'l contour contrast f. */ int lens_seqnr; /* opt'l sequence number */ int output_seqnr; /* opt'l sequence number */ int volume_seqnr; /* opt'l sequence number */ int environment_seqnr; /* opt'l sequence number */ miCBoolean scanline; /* need scanline? */ miCBoolean trace; /* need ray tracing? */ miCBoolean deriv1; /* need first derivatives? */ miCBoolean deriv2; /* need second derivatives? */ miUchar mintextures; /* need n texture spaces */ miUchar minbumps; /* need n bump spaces */ miUchar minusers; /* need n user vectors */ miUchar spare1; /* not used */ char shadow; /* 0, 1, 'l' sort, 's' segm */ char face; /* 'f'ront, 'b'ack, 'a'll */ char render_space; /* 'c'amera, 'o'bject, 0 any */ miCBoolean smart_volume; /* volume shd calls material */ } miPhen_decl;
typedef struct miFunction_decl { miLock lock; /* local shader lock */ miPointer cached_address; /* for RC only */ enum miFunction_type type; /* C function or phenomenon */ miParam_type ret_type; /* return type of shader */ int declaration_size; /* size of declaration */ int result_size; /* size of result struct */ int version; /* shader version from .mi */ miUint apply; /* what can it be used for? */ miPhen_decl phen; /* if type==miFUNCTION_PHEN */ miCBoolean has_init_shader; /* init shader exists */ miCBoolean has_no_init_shader; /* checked before, no init sh*/ miCBoolean inst_init_req; /* init every instance too? */ miCBoolean func_init; /* was function initialized? */ char name[miNAMESIZE]; /* ascii name */ char declaration[1]; /* declaration string */ } miFunction_decl; #define miDECL_SUBTAG(d,i) (...)
A translator must provide: name, result_size, declaration.
A translator may provide: type, ret_type.
Provided by mi_scene_create: parameter_size, declaration_size, version, apply.
lock is a lock shared by all instances of a shader.
Every shader such as soft_material has exactly one local lock that is shared by all concurrent invocations of this shader.
cached_address is used to reduce the number of lookups of shader names. After the first lookup, the result of the lookup is cached, and all future lookups first check the address and skip the lookup if it is nonzero. The address is a miPointer because the structure must have the same size regardless of whether pointers are 32 or 64 bits.
type must currently be miFUNCTION_C.
ret_type is the return type of the function. For backwards compatibility, undefined return types default to miTYPE_COLOR. The type is important for subshaders in shader trees. Only ``simple types'' are allowed here.
has_init_shader and the other three flags help the renderer to determine whether the function and the function instance have been initialized. has_no_init_shader is intended for remembering whether the renderer has already checked whether there is an init shader; if not, no further tests are needed.
name is an ASCII string identifying the shader. This name will be looked up in LINK's symbol table at runtime.
parameter_size helps the translator decide how many bytes to allocate when a new miFunction entry is allocated, see above. The parameter size does not include space needed for parameter arrays.
declaration_size is the size of the declaration array in bytes, including the trailing null byte.
result_size is the number of bytes in the result data structure. By default this is 16 (the size of miColor, which is the default return type). It must be set to match the result_size field of the declaration.
version is the declaration version. It can be queried by the shader using mi_query and allows the shader to ensure that the declaration and the shader agree. Also, if a shader library contains a function named shadername_version, it is caled and its returned integer value must match the version. It is highly recommended to use this feature.
apply is a bitmap that specifies what the shader can be used for. Each bit stands for a specific type of shader:
miAPPLY_LENS lens shader in a camera miAPPLY_MATERIAL material shader in a material miAPPLY_LIGHT light shader miAPPLY_SHADOW shadow shader in a material miAPPLY_ENVIRONMENT environment shader in a material or camera miAPPLY_VOLUME volume shader in a material or camera miAPPLY_TEXTURE texture shader miAPPLY_PHOTON photon shader in a material miAPPLY_GEOMETRY geometry shader miAPPLY_DISPLACE displacement shader in a material miAPPLY_PHOTON_EMITTER photon emitter shader in a light miAPPLY_OUTPUT output shader in a camera
If the apply bitmap is zero (the default), it is not known what the shader can be used for, and all uses are legal. mental ray does not currently enforce non-applicability, this is only a hint.
phen is a substructure containing fields used if the type is miFUNCTION_PHEN. The miPhen_decl substructure is still under development. Note that phenomena keep a list of tags of shaders, materials, lights, and other sub-objects defined in the scope of the phenomenon in a tag list that follows the declaration string. Tags in this list can be accessed with the miDECL_SUBTAG macro.
declaration describes the parameter layout required by the shader. It is a sequence of ascii characters, each describing a type or structure; the sequence is an abbreviated form of the declaration syntax in the .mi file. The declaration is a list of return and parameter declarations. Each list item begins with an optional 'a' for array, followed by the type (one of biscvtSCVlS$ for boolean, integer, scalar, color, vector, transform, scalar texture, color texture, vector texture, light, shader, and string) followed by a double-quoted name. Substructures are defined with {" name" followed by the structure name followed by the structure definition followed by }. The declaration has two parts separated by an equals sign; the first part declares the return type and the second part declares the parameters. The first part may have only one field that may not be an array and whose double-quoted name part is omitted, but it may be a structure containing named fields. One null byte terminates the entire declaration.
For example, a shader returning a color r and accepting three parameters, a scalar s, an array of structures t containing two integers i1 and i2, followed by a light array l would lead to the following declaration string:
c=s"s"a{"t"i"i1"i"i2"}al"l"
If the return type were a structure containing a color c and a boolean b, the declaration changes to:
{c"c"b"b"}=s"s"a{"t"i"i1"i"i2"}al"l"
Element type: miSCENE_BOX Data type: miBox Sizes: miVector_list *, miVertex_content *, int nvert, npri, enum miBox_type type, miBoolean moving Defaults: all nulls except stored arguments
enum miBox_type { miBOX_TRIANGLES, miBOX_ALGEBRAICS }; typedef struct miBox { miUint label; /* translator: object label */ miTag next_box; /* translator: box list */ enum miBox_type type; /* type of primitives */ miBoolean visible; /* visible? */ miBoolean shadow; /* casts shadow? */ miBoolean trace; /* visible as reflection? */ miBoolean mtl_is_label; /* triangle mtls are labels */ miUint1 caustic; /* bit0=cast, bit1=receive */ miCBoolean spare[3]; /* not used */ miVertex_content vert_info; /* size & content of vertices */ miVector_list vect_info; /* sections of vector array */ miIndex no_vertices; /* number of vertices */ miIndex no_primitives; /* number of primitives */ miVector vectors[1]; } miBox;
A translator must provide: vectors, vertices, primitives.
A translator may provide: label, next_box (using
mi_scene_link), visible, shadow, trace, caustic, mtl_is_label.
Provided by mi_scene_create: type, vert_info, vect_info, no_vertices, and no_primitives
Boxes consists of a miBox header, followed by three variable-sized parts: the vector list, the vertex list, and the primitive list. Currently only triangles can be used as a primitive type. All primitives must have the same data type. All primitives in a box were always generated from the same object, no two objects share a box.
label is the label value copied from the object. It is used by shaders only, and can be written to label frame buffers.
next_box is used to chain boxes when more than one box results from an object. The maximum size of boxes is limited by the capacity of miIndex.
type is either miBOX_TRIANGLES and miBOX__ALGEBRAICS. Algebraics are not currently supported. Together with vert_info.motion_offset (which indicates moving motion-blurred geometry if nonzero), the type determines the data type of the primitives, currently only (miTriangle.
visible (default miTRUE) makes the triangles visible to primary rays.
shadow (default miTRUE) enables shadows cast by the triangles.
trace (default miTRUE) makes the triangles visible to second-generation rays.
caustic is a bitmap with two valid bits. Bit 0, if set, enables generation of caustics cast by this object. Bit 1, if set, enables receiving caustics.
mtl_is_label is a flag that informs the renderer that the material field in the triangles does not hold a material but an integer label. The renderer must ignore the material in this case and use the inherited material. This flag is set for tagged objects in the mi2 language.
no_vertices is the number of vertices, each of which consists of vert_info.sizeof_vertex miIndex values.
no_primitives is the number of primitives (triangles or algebraics).
There are two data structures that are used in boxes and several other places to describe the format of vertices and boxes:
typedef struct miVertex_content { miUchar sizeof_vertex; /* size of a vertex (in the input in miGeoIndex's; in boxes in miIndex's) */ miUchar normal_offset; /* when 0, not present */ miUchar motion_offset; /* when 0, not present */ miUchar derivs_offset; /* surf derivs, when 0, not present */ miUchar derivs2_offset; /* 2nd derivs, when 0, not present */ miUchar texture_offset; /* when 0, not present */ miUchar no_textures; /* number of textures */ miUchar bump_offset; /* when 0, not present */ miUchar no_bumps; /* number of bumps */ miUchar user_offset; /* when 0, not present */ miUchar no_users; /* number of user vectors */ } miVertex_content;
A translator must provide: all fields.
This structure is used in miBox to describe which information is stored with each vertex. It is also used to inform the tessellator what information to create vertices with. Vertices always consist of at least one vector reference for the point in space. The normal, the motion vector, the surface derivative pair, the list of textures, the list of bump basis vectors, and the list of user vectors are all optional.
As described in the subsection for boxes below, a vertex is a list of indices into the actual vector table. Each such list begins with the index for the point in space, said to be at offset 0 in the list. The miVertex_content structure describes at which offset in the list other vector indices can be found. For example, if there is an index for a normal in the list directly after the index for the point in space, normal_offset would be 1. Offset value 0 is reserved for nonexisting items; no item can have offset 0 because the point in space index is stored there.
If some vertices have a certain type type of index and others do not (like some vertices have normals and others do not), the offset in the miVertex_content structure is nonzero but the index is set to miNULL_INDEX resp.miNULL_GEOINDEX in those vertices that do not need it. Using 0 for nonexistent offset members is ok because the first index in a vertex (which has offset 0) is the point in space, which must always exist. Do not confuse null offsets with null indices, which must use miNULL_INDEX resp.miNULL_GEOINDEX instead of 0 because the first vector in a polygon vector list can be something other than a point in space. This is inconvenient for the polygon-to-box tessellation code because box vector lists are divided into sections, and the vector list does begin with points in space so non-point-in-space indices cannot be 0, but this rule applies only to boxes. Polygon lists mirror the order in the .mi file, which traditionally does allow index 0 for vectors that are not points in space.
The second recurring structure is the vector list header. Depending on whether the list contains miGeoVectors (in the input) or miVectors (in the boxes) miGeoIndex or miIndex variables are used to reference them.
typedef struct miGeoVector_list { miGeoIndex no_vectors; /* total number of input vectors */ miGeoIndex no_points; /* number of points in space */ miGeoIndex no_normals; /* number of normals */ miGeoIndex no_derivs; /* number of 1st/2nd surface derivs */ miGeoIndex no_motions; /* number of motion vectors */ miGeoIndex no_textures; /* number of texture coordinates */ miGeoIndex no_bumps; /* number of bump basis vectors */ miGeoIndex no_users; /* number of user-defined vectors */ } miGeoVector_list; typedef struct miVector_list { miIndex no_vectors; /* total number of box vectors */ miIndex no_points; /* number of points */ miIndex no_normals; /* number of normals */ miIndex no_derivs; /* number of surf. derivs. */ miIndex no_motions; /* number of motion vectors */ miIndex no_textures; /* number of texture vectors */ miIndex no_bumps; /* number of bump vectors */ miIndex no_users; /* number of user vectors */ } miVector_list;
A translator must provide: all fields.
All the vertex indices described by the miVertex_content structure are indices into the actual vector list. The vector list itself is partitioned in the seven ``sections'': one for points in space, one for normals, one for surface derivatives, and so on. This partitioning allows the preprocessing stage to quickly transform a box without traversing the vertex index list; points are transformed in a different way than normals or motion vectors, and some vectors such as user vectors are not transformed at all.
The complete box structure can be summarized as:
The header is declared as miBox itself. The vector part is a miVector array. The vertex part is a miIndex array. The primitive part is an array of triangles, which are declared as follows. Fields marked ``R:'' are reserved for the renderer and should not be used by any other module.
enum miTriangle_projection { TRI_XY, TRI_XZ, TRI_YZ }; /* gap_flags */ #define miISOLATED_TRI 0x1 #define miIS_STRIP 0x2 #define miSTRIP_BEGIN 0x4 #define miSTRIP_RIGHT 0x8 typedef struct { miUchar proj; /* R: miTriangle_projection flag */ miUchar gap_flags; /* flags for strip output */ miIndex a; /* vertex index */ miIndex b; /* vertex index */ miIndex c; /* vertex index */ miTag material; /* opt. material */ miVector normal; /* R: normal */ miScalar d; /* R: distance */ } miTriangle;
A translator must provide: a, b, c, material.
projection is a flag used by the renderer to cache the best projection plane of the triangle. Possible values are TRI_XY, TRI_XZ, and TRI_YZ. It is declared as a miUchar because declaring it as an enum miTriangle_projection (a 4-byte int) would cause a structure misalignment.
gap_flags store information about triangle strips indicating whether the triangle is not adjacent to its predecessor or successor in the box (miISOLATED_TRI), whether it belongs to a strip or fan (miIS_STRIP), whether it is the first triangle of a fan or strip (miSTRIP_BEGIN), and the type of strip (miSTRIP_RIGHT).
gap_flags are computed when the option strips is set.
a, b, c are indices into the vertex list. The first vertex is numbered 0. The indices are multiplied by vert_info.sizeof_vertex before the actual array lookup; up to 65536 vertices can be indexed.
material is the tag of a material for this primitive. The referenced database element must have type miSCENE_MATERIAL.
normal is the geometric triangle normal. Normals are calculated and accessed by the renderer only.
Element type: miSCENE_OBJECT Data type: miObject Sizes: --- Defaults: as described below
enum miObject_type { miOBJECT_POLYGONS, miOBJECT_FACES, miOBJECT_BOXES, miOBJECT_SPACECURVES, miOBJECT_ALGEBRAICS /* for future use */ }; typedef struct miObject { miTag boxes; /* SCENE: cached shared tess */ int n_boxes; /* SCENE: # of boxes attached */ miTag geogroup; /* SCENE: geomshader group */ enum miObject_type type; /* which in union to use */ miBoolean visible; /* object visible? */ miBoolean shadow; /* cast a shadow? */ miBoolean trace; /* reflection and refraction? */ miUint1 caustic; /* bit0=cast, bit1=receive */ miCBoolean spare_1[3]; /* not used */ miBoolean view_dependent; /* miOBJECT_FACES only */ miBoolean mtl_is_label; /* triangle mtls are labels */ miBoolean spare_2; /* not used, for padding */ miUint label; /* optional label */ miTag functions; /* SCENE: material req list */ unsigned int n_functions; /* SCENE: # tags in functions*/ union { miPolygon_list polygon_list; miFace_list face_list; miTag box_list; miTag spacecurve_list; } geo; /* geometry */ } miObject; typedef struct miPolygon_list { miGeoIndex no_polygons; miGeoIndex no_indices; miGeoIndex no_vertices; miGeoVector_list vect_info; /* contents of vector array */ miVertex_content vert_info; /* vertex size & content */ miTag polygons; /* array of miPolygon */ miTag indices; /* array of miGeoIndex */ miTag vertices; /* see vert_info */ miTag vectors; /* array of miGeoVector */ miApprox approx; /* poly approx technique */ } miPolygon_list; typedef struct miFace_list { miGeoIndex no_faces; miGeoIndex no_surfaces; miGeoIndex no_curves; miGeoIndex no_specpnts; miGeoIndex no_surf_scalars; miGeoIndex no_curve_scalars; miTag faces; /* array of miFace */ miTag surfaces; /* array of miSurface */ miTag curves; /* array of miCurve */ miTag specpnts; /* array of miCurve_point */ miTag surf_scalars; /* array of miGeoScalar */ miTag curve_scalars; /* array of miGeoScalar */ miTag basis_list; /* miBasis_list */ } miFace_list; typedef struct miSpacecurve_list { miGeoIndex no_spacecurves; miGeoIndex no_curves; miGeoIndex no_specpnts; miGeoIndex no_curve_scalars; miTag spacecurves; /* array of miSpacecurve */ miTag curves; /* array of miCurve */ miTag specpnts; /* array of miCurve_point */ miTag curve_scalars; /* array of miGeoScalar */ miTag basis_list; /* miBasis_list */ miBoolean pad; /* not used */ } miSpacecurve_list;
A translator must provide: type; all miPolygon_list fields for polygonal objects; no_faces, no_surfaces, no_surf_scalars, faces, surfaces, surf_scalars, basis_list.
boxes is used to point to boxes that resulted from tessellation. They are used in two cases only: object or box instances attach to the cached untransformed boxes (see above for a definition) if enabled; and leaf instances attach to the final renderable representation.
n_boxes is the number of boxes attached to boxes. Keeping this separate avoids having to access boxes to count them.
geogroup is used to point to the group element which contains all the geometry-shader created scene elements for this object.
type (default miOBJECT_POLYGONS) specifies the type of the geometry attached to this object. It may be one of miOBJECT_POLYGONS, miOBJECT_FACES, miOBJECT_BOXES, or miOBJECT_ALGEBRAICS. This field determines which member of the union is used. Algebraics are not currently supported.
visible (default miTRUE) makes the object visible to primary rays.
shadow (default miTRUE) enables shadows cast by this object.
trace (default miTRUE) makes the object visible to second-generation rays.
view_dependent (default miFALSE) enables view-dependent tessellation if the object has type miOBJECT_FACES.
caustic is a bitmap with two valid bits. Bit 0, if set, enables generation of caustics cast by this object. Bit 1, if set, enables receiving caustics.
mtl_is_label is a flag that informs the renderer that the material field in the polygons or surfaces does not hold a material but an integer label. The renderer must ignore the material in this case and use the inherited material. This flag is set for tagged objects in the mi2 language.
label is a 32-bit integer that may be used to identify the object in shaders. mental ray does not use it in any way.
functions if this is not a null tag, it points to a list of material tags. This list represents the required materials for the object.
n_functions the number of tags in the functions taglist.
geo is a union containing type-specific data. The suffix ``_list'' indicates that several types store the actual geometry in one or more lists of specific types; these lists are anchored here. The box_list case is a tag of a database element of type miSCENE_BOX. The box data type has been described above. Polygonal geometry is stored in four lists, all anchored in the geo.polygon_list structure.
The following diagram shows the connections between the four lists:
Arrows indicate indices into the pointed-to data structure. All four grey boxes are separate Scene database element types, refer to the sections for miSCENE_POLYGON, miSCENE_GEOINDEX, and miSCENE_GEOVECTOR, respectively. The data structures are similar to the box data structure, except that the lists are stored in separate database elements, and that an index list is inserted that allows storing polygons with different numbers of vertices in a miPolygon data structure that has a constant size. The polygon points to the first vertex index in the index list and gives the number of vertices, as opposed to triangles in boxes that contain the three vertex indices directly.
geo.polygon_list.no_polygons is the number of polygons in the polygons list.
geo.polygon_list.no_indices is the number of indices in the indices list.
geo.polygon_list.no_vertices is the number of vertices in the vertices list. Each vertex consists of vert_info.sizeof_vertex indices.
geo.polygon_list.vect_info describes the sections of the vector list. For details, see the section for boxes above.
geo.polygon_list.vert_info describes the layout of the indices in the vertex list. For details, see the section for boxes above.
geo.polygon_list.polygons is the tag of the polygon list.
geo.polygon_list.indices is the tag of the index list.
geo.polygon_list.vertices is the tag of the vertex list.
geo.polygon_list.vectors is the tag of the vector list.
geo.polygon_list.approx is the approximation technique for displacement-mapped polygons. (It does not apply to polygons whose material does not specify displacement maps.)
Surface geometry is more complex. It is stored in up to seven different database elements, all of which are anchored in geo.face_list. The term face describes one complete visible free-form surface, which is built from one geometry surface and multiple optional texture surfaces, bump surfaces, and/or motion surfaces that provide certain types of mappings on the surface.
For details on texture surfaces, refer to the mental ray User Manual.
Unlike polygons, surfaces store their parameter lists and vectors in large miGeoScalar lists. This simplifies storing one, two, three, and four dimensional data in the same list. Trimming, hole, and special curves can optionally be attached to faces; they have their own scalar lists. Both surfaces and curves may reference optional special points. Every surface references two bases, and every curve references one base. Again, all lines in the following diagram indicate indices.
geo.face_list.no_faces is the number of faces in the geo.face_list.faces list.
geo.face_list.no_surfaces is the number of surfaces in the geo.face_list.surfaces list.
geo.face_list.no_curves is the number of curves in the geo.face_list.curves list.
geo.face_list.no_specpnts is the number of special points in the geo.face_list.specpnts list.
geo.face_list.no_surf_scalars is the number of surface scalars in the geo.face_list.surf_scalars list.
geo.face_list.no_curve_scalars is the number of curve scalars in the geo.face_list.curve_scalars list.
geo.face_list.faces is the tag of the list of faces.
geo.face_list.surfaces is the tag of the list of surfaces.
geo.face_list.curves is the tag of the list of curve.
geo.face_list.specpnts is the tag of the list of special points.
geo.face_list.surf_scalars is the tag of the list of surface scalars, which are used for parameter vectors, control points, weights etc. that define all surfaces in this object.
geo.face_list.curve_scalars is the tag of the list of curve scalars, which are used for parameter vectors, control points, weights etc. that define all curves in this object.
geo.face_list.basis_list is the tag of the list of all bases.
An object of type miOBJECT_SPACECURVE contains a list of spacecurves. A single spacecurve consists of curve segments which are approximated as a single three dimensional curve. The approximation result is stored in a miLinebox.
geo.spacecurve_list.no_spacecurves is the number of space curves in the geo.spacecurve_list.spacecurves list.
geo.spacecurve_list.no_curves is the number of curves in the geo.spacecurve_list.curves list.
geo.spacecurve_list.no_specpnts is the number of special points in the geo.spacecurve_list.specpnts list.
geo.spacecurve_list.no_curve_scalars is the number of curve scalars in the geo.spacecurve_list.curve_scalars list.
geo.spacecurve_list.spacecurves is the tag of the list of spacecurves.
geo.spacecurve_list.curves is the tag of the list of curves.
geo.spacecurve_list.specpnts is the tag of the list of curve special points.
geo.spacecurve_list.curve_scalars is the tag of the list of curve scalars.
geo.spacecurve_list.basis_list is the tag of the list of all bases used by the curves.
Element type: --- Data type: miApprox Sizes: --- Defaults: as set by miAPPROX_DEFAULT()
enum miApprox_method { miAPPROX_PARAMETRIC, miAPPROX_REGULAR, miAPPROX_SPATIAL, miAPPROX_CURVATURE, miAPPROX_ADJACENCY, /* only for curves */ miAPPROX_ALGEBRAIC, /* only for surfaces */ miAPPROX_DEFTRIM, /* only for miFace def_trim_approx */ miAPPROX_NMETHODS }; enum miApprox_style { miAPPROX_STYLE_NONE, miAPPROX_STYLE_GRID, miAPPROX_STYLE_TREE, miAPPROX_STYLE_MESH, miAPPROX_NSTYLES }; #define miCNST_UPARAM 0 /* regular/parametric only */ #define miCNST_VPARAM 1 #define miCNST_LENGTH 0 /* curvature/spatial only */ #define miCNST_DISTANCE 1 #define miCNST_ANGLE 2 typedef struct miApprox { miGeoScalar cnst[3]; /* indexed with miUPARAM...miANGLE */ miBoolean view_dep; /* view dependent ? */ enum miApprox_method method; enum miApprox_style style; miUshort subdiv[2]; /* recursive subd.*/ } miApprox; #define miSWAP_SCENE_APPROX "dddiiiss" #define miAPPROX_MAX_SUBDIV 7 #define miAPPROX_DEFAULT(A) \ (A).style = miAPPROX_STYLE_TREE;\ (A).method = miAPPROX_PARAMETRIC;\ (A).view_dep = miFALSE; \ (A).cnst[0] = 1.0; \ (A).cnst[1] = 1.0; \ (A).cnst[2] = 0.0; \ (A).subdiv[miMIN] = 0; \ (A).subdiv[miMAX] = 5;
A translator may provide: all fields.
The approximation structure is a substructure of miPolygon_list, miCurve, and miSurface. It does not have its own database entry and no associated SCENE functions of its own.
method is the approximation method, and must be one of miAPPROX_PARAMETRIC, miAPPROX_REGULAR, miAPPROX_SPATIAL, miAPPROX_CURVATURE, miAPPROX_LDA, miAPPROX_ADJACENCY (available for curves only), and miAPPROX_ALGEBRAIC (available for surfaces only). The default is miAPPROX_PARAMETRIC. ``LDA'' stands for Length, Distance, Angle; this mode combines the spatial and curvature-dependent modes.
style is the approximation style, and must be one of miAPPROX_NONE, miAPPROX_GRID, miAPPROX_TREE, and miAPPROX_STYLE_MESH. Tree mode is the default. The API and GAP module will automatically change the style field to grid mode if the method is parametric or regular.
view_dep, if miTRUE, turns on view-dependent tessellation. Close faces are tessellated more finely than distant faces. This mode is not available for the parametric and regular method.
cnst[0], cnst[1], and cnst[2] specify the approximation precision. They depend on the approximation method. The defaults are 1.0 (which is not ideal for curvature-dependent tessellation). The cnst array should be indexed using the miCNST_* constants.
technique cnst[0] cnst[1] cnst[2]
parametric number of U subdivs /degree number of V subdivs /degree --- regular number of U subdivs number of V subdivs --- spatial edge length limit --- --- curvature --- distance tolerance angle tolerance lda edge length limit distance tolerance angle tolerance
subdiv[0] and subdiv[1] specifiy the recursion limits. The defaults are 0 and 5, respectively. The maximum value is 7. A subdivision level n means that the curve, triangle, or surface is halved in each parameter direction n times, yielding on the order of 2n segments.
Element type: miSCENE_POLYGON Data type: miPolygon[ ] Sizes: int no_polys Defaults: all nulls
typedef struct miPolygon { miGeoIndex no_loops; /* 1 + (number of holes) */ miGeoIndex no_vertices; /* total number of vertices + headers*/ miGeoIndex vertex_idx; /* into indices list, for sharing */ miTag material; /* surface properties */ miCBoolean convex; /* is polygon convex ? */ miCBoolean spare[3]; /* not used */ } miPolygon;
A translator must provide: all fields except spare.
no_loops is the number of loops of the polygon. Every polygon has exactly one outer boundary loop, plus any number of optional hole loops. Every loop is stored as a pseudo-index called a ``header'' that doesn't actually refer to the vector list but gives the number of indices to follow, followed by that many indices.
no_vertices is the total number of headers (the number of holes plus 1) plus the total number of vertices for this polygon.
vertex_idx is an index to the first header of the polygon in the index list. no_vertices indices beginning at vertex_idex define the polygon.
material is the tag of the material of the polygon. It must refer to a database element of type miSCENE_MATERIAL.
convex is a flag telling the tessellator that the polygon has no holes and is guaranteed to be convex. This saves time because GAP can use a very simple tessellation algorithm.
Element type: miSCENE_GEOINDEX Data type: miGeoIndex[ ] Sizes: int no_indices Defaults: all nulls
Polygon indices are simple miGeoIndex arrays. As described above, each polygon indexes a consecutive block of indices that describes one or more loops, each of which begins with a header pseudo-index giving the number of loop indices to follow. Each index indexes into the vertex list, after being multiplied by vert_info.sizeof_vertex.
Element type: miSCENE_GEOVERTEX Data type: miGeoIndex[ ] Sizes: int no_indices Defaults: all nulls
The vertex list is an miGeoIndex array. It works exactly like the vertex section of boxes; see above. The vert_info structure in miPolygon determines how many indices make up one vertex, and how it is laid out. Note that the no_indices argument of the create and resize functions is the number of indices, not the number of vertices.
Element type: miSCENE_GEOVECTOR Data type: miGeoVector[ ] Sizes: int no_vectors Defaults: all nulls
The vector list is a miGeoVector array. It is partitioned into sections as described by the vect_info structure in miPolygon.
Element type: miSCENE_FACE Data type: miFace[ ] Sizes: int no_faces Defaults: all nulls except where otherwise noted
typedef struct miFace { miApprox def_trim_approx; /* approx techn. for default trims */ miGeoRange range[miUV_DIM]; /* min/max for parameter vals*/ miGeoIndex no_curves; /* total number of curves */ miGeoIndex no_surfaces; /* total # of surfs: 1 geo. + xtras */ miGeoIndex surface_idx; /* surface list index */ miGeoIndex curve_idx; /* index into the 'curves' list*/ miTag material; /* optional: material of the face */ miVertex_content gap_vert_info; /* For calculating box sizes */ } miFace;
A translator must provide: range, no_surfaces, surface_idx, material, gap_vert_info.
A translator may provide: def_trim_approx, no_curves, curve_idx.
def_trim_approx is the approximation for default trimming curves. Default trimming curves are automatically created if the face has no explicit trimming curves attached. The default trimming curve follows the edges of the face. The miAPPROX_DEFTRIM approximation technique, which is allowed only here, should be used as default to create a default trim curve that does not introduce new vertices.
range[miU] and range[miV] specify the minimum and maximum values of the U and V parameter vectors.
no_curves specifies the total number of curves used by this face.
no_surfaces is the total number of surfaces for this face. This is at least 1 for the geometric surface, plus the numbers of texture, bump, and/or motion surfaces.
surface_idx is the index of the first surface in the surface list anchored in the object (see above) that is used for this face. This is the first of no_surfaces consecutive surfaces to use.
curve_idx is the index of the first curve in the curve list anchored in the object that is used for this face. This is the first of no_curves consecutive curves to use.
material specifies the tag of a material to use for the face.
gap_vert_info describes the layout and size of the vertices to create during tessellation. The boxes created during tessellation will get a copy of this vertex info structure.
Element type: miSCENE_SURFACE Data type: miSurface[ ] Sizes: int no_surfaces Defaults: all nulls, except where otherwise noted
typedef struct miSurface { miApprox approx; /* approx techn. for surface */ miApprox disp_approx; /* approx for disp. surface */ miGeoIndex no_parms[miUV_DIM]; /* no of parameter values */ miGeoIndex no_ctls; /* no of control points */ miGeoIndex no_specpnts; /* no of special points */ miGeoIndex scalar_idx; /* surf_scalar list index */ miGeoIndex specpnt_idx; /* special points list index */ miGeoIndex basis_idx[miUV_DIM]; /* index into basis list */ enum miSurface_type type; miUshort degree[miUV_DIM]; /* from bases, for GAP */ miUshort ctl_dim; /* control pnt dimension */ miUshort spare[3]; } miSurface;
A translator must provide: type, approx, disp_approx, ctl_dim, degree, no_parms, no_ctls, scalar_idx, basis_idx.
A translator may provide: no_specpnts, specpnt_idx.
type must be one of the following: miSURFACE_GEOMETRIC, miSURFACE_GEOMOTION, miSURFACE_TEXTURE, miSURFACE_BUMP, miSURFACE_TEXBUMP_2D, or miSURFACE_TEXBUMP_3D. The 2D variants perform seam rewind correction, the 3D variants do not.
approx is the approximation technique as described above, in the miFace description.
disp_approx is the approximation technique for the displaced surface, if a displacement shader is available.
ctl_dim is the number of scalars per control point:
miSURFACE_GEOMETRIC, non-rational: 3 miSURFACE_GEOMETRIC, rational: 4 miSURFACE_GEOMOTION, non-rational: 6 miSURFACE_GEOMOTION, rational: 7 miSURFACE_TEXTURE_2D 2 miSURFACE_TEXTURE_3D 3 miSURFACE_BUMP: 3 miSURFACE_TEXBUMP_2D, non-rational: 5 miSURFACE_TEXBUMP_2D, rational: 6 miSURFACE_TEXBUMP_3D, non-rational: 5 miSURFACE_TEXBUMP_3D, rational: 6
degree[0] and degree[1] contains the degree of the surface in the U and V directions, respectively. This is used by GAP to cache the degree from the surface bases.
no_parms[0] and no_parms[1] are the lengths of the U and V parameter vectors, respectively.
no_ctls is the total number of control points for this surface. The number of scalars required in the scalar list is no_ctls *ctl_dim.
no_specpoints is the number of special points to be included in the tessellation.
scalar_idx is the index of the first scalar in the surface scalar list (also anchored in the object, see above). The scalar list for one surface consists of the U parameter vector, followed by the V parameter vector, followed by the geometric control points (all X first, then all Y, then all Z, then all W, then all motion X, then all motion Y, then all motion Z, if present), followed by the texture and bump control points in the same XYZW order if present.
specpnt_idx is the index of the first special point in the special points list (anchored in the object) to use.
basis_idx[0] and basis_idx[1] are indices for the bases in the basis list (anchored in the object) to use, separate for the U and V parameter directions.
Element type: miSCENE_CURVE Data type: miCurve Sizes: int no_curves Defaults: as described below
enum miCurve_type { miCURVE_TRIM, miCURVE_HOLE, miCURVE_SPECIAL, miCURVE_SPACE }; typedef struct miCurve { enum miCurve_type type; miApprox approx; /* approx techn. for surface */ miBoolean new_loop; /* F=concatenate to prev, T=begin new */ miUshort ctl_dim; /* control point dimension 2 or 3 */ miUshort degree; /* from basis, for ease in GAP */ miGeoRange range; /* min/max for curve params. */ miGeoIndex no_parms; /* number of curve parameters */ miGeoIndex no_ctls; /* number of control points */ miGeoIndex no_specpnts; /* number of special points */ miGeoIndex scalar_idx; /* index into curve_scalar list */ miGeoIndex specpnt_idx; /* index into special points list */ miGeoIndex basis_idx; /* index into basis list */ } miCurve;
A translator must provide: type, approx, new_loop, ctl_dim, degree, range, no_parms, no_ctls, scalar_idx, basis_idx.
A translator may provide: no_specpnts, specpnt_idx.
type specifies what the curve is used for. It must be one of miCURVE_TRIM, miCURVE_HOLE, miCURVE_SPECIAL or miCURVE_SPACE. The default is miCURVE_TRIM.
approx is the approximation technique as described above, in the miFace description.
new_loop is miTRUE for every new loop. Loops can be pieced together from multiple consecutive curves. The default is miTRUE.
ctl_dim is the control point dimension; 2 for UV curves or 3 for space curves. The default is 2.
degree is the degree of the curve. The default is 3.
range is the range over which the curve is evaluated. The value depends on the parameter list. The defaults are 0.0 and 1.0.
no_parms is the length of the parameter vectors.
no_ctls is the number of control points for this curve. The number of scalars required in the curve scalar list is no_ctls *ctl_dim.
no_specpoints is the number of special points to be included in the approximation.
scalar_idx is the index of the first scalar in the curve scalar list (also anchored in the object, see above). The scalar list for one curve consists of the parameter vector, followed by the control points (all X first, then all Y, then all Z, if present).
specpnt_idx is the index of the first special point in the special points list (anchored in the object) to use.
basis_idx is the index of the basis in the basis list (anchored in the object) to use.
Element type: miSCENE_SCALAR Data type: miGeoScalar[ ] Sizes: int no_scalars Defaults: all nulls
Both surfaces and curves store all their geometric data (parameter lists and control points) in unstructured scalar lists. The exact layout and the size of each control point depends on the surface or curve. Surfaces and curves store their scalars in separate scalar lists, but all surfaces in an object share one scalar list, and all curves in the object share the other (which is omitted if there are no curves). Each surface and curve has an index that specifies where in the respective scalar list the scalars for the surface or curve begin. All scalars for a single surface or curve are consecutive. The internal layout of each such curve depends on the surface or curve; see above for details. In general, the parameter vectors come first, followed by the control points in X, Y, Z, W order. All the X are given before the Y, etc.
Element type: miSCENE_CURVPNT Data type: miCurve_point[ ] Sizes: int no_points Defaults: all nulls
typedef struct miCurve_point { miGeoVector v; /* xyz point on surface of curve at t */ miGeoVector2d uv; /* uv value of curve at t */ miGeoScalar t; /* curve parameter value */ miUshort flags; /* GAP: */ miCBoolean is_t; /* is t value present */ miCBoolean is_uv; /* is uv value present */ miCBoolean is_v; /* is xyz value present */ miCBoolean spare; /* not used */ } miCurve_point;
A translator must provide: all fields except spare.
(Despite the name, curve points may also be referenced as special points by surfaces.)
is_t, is_uv, and is_v, if miTRUE, specify that t, uv, or v are valid, respectively.
t, if enabled by is_t, gives the parameter value of the special point.
uv, if enabled by is_uv, gives the UV value of the curve at the special point.
v, if enabled by is_v, gives the location in space of the special point.
Element type: miSCENE_BASIS_LIST Data type: miBasis_list[ ] Sizes: int no_bases, int no_scalars Defaults: all nulls
enum miBasis_type { miBASIS_NONE = 0, /* for internal use */ miBASIS_BEZIER, miBASIS_BSPLINE, miBASIS_CARDINAL, miBASIS_MATRIX, miBASIS_TAYLOR }; typedef struct { miGeoIndex no_bases; miGeoIndex no_scalars; miBasis bases[1]; } miBasis_list; typedef struct miBasis { enum miBasis_type type; miGeoIndex degree; /* undefined for Cardinal */ miGeoIndex stepsize; /* only for miBASIS_MATRIX */ miGeoIndex value_idx; /* index into the basis scalar list */ } miBasis;
A translator must provide: type, degree; if the type is basis matrix, also stepsize and value_idx.
no_bases specifies the number of bases in the basis list.
no_scalars specifies the number of scalars that follow the basis list. Scalars are used for basis matrix bases, which require degree2 scalars for a matrix defining the basis. The first scalar in the list has index 0.
bases is the basis array. It is actually allocated larger.
type is one of miBASIS_BEZIER, miBASIS_BSPLINE, miBASIS_CARDINAL, miBASIS_MATRIX, and miBASIS_TAYLOR.
degree is the degree of the basis.
stepsize is used for basis matrix bases only.
For an explanation, see the description of bases in the mental ray User Manual.
Element type: miSCENE_ALGEBRAIC Data type: miAlgebraic[ ] Sizes: --- Defaults: ---
This type is not currently supported.
Element type: miSCENE_SPACECURVE Data type: miSpacecurve[ ] Sizes: int no_spacecurves Defaults: all nulls except where otherwise noted
typedef struct miSpacecurve { miApprox def_approx; /* default approximation */ miGeoIndex no_curves; /* total number of curves */ miGeoIndex curve_idx; /* index into the 'curves' list*/ miBoolean pad; /* not used */ miVertex_content gap_vert_info; /* For calculating box sizes */ } miSpacecurve;
A translator must provide: no_curves, curve_idx, gap_vert_info.
A translator may provide: def_approx.
def_approx is the default approximation for curve segments which do not have their own approximation defined.
no_curves specifies the total number of curves used by this spacecurve.
curve_idx is the index of the first curve in the curve list anchored in the object that is used for this spacecurve. This is the first of no_curves consecutive curves to use.
gap_vert_info describes the layout and size of the vertices to create during tessellation. The boxes created during tessellation will get a copy of this vertex info structure.
Element type: miSCENE_MATERIAL Data type: miMaterial Sizes: --- Defaults: all nulls
typedef struct miMaterial { miBoolean opaque; /* no transparency? */ miTag shader; /* material shader */ miTag displace; /* opt. displacement shader */ miTag shadow; /* opt. shadow shader */ miTag volume; /* opt. volume shader */ miTag environment; /* opt. environment shader */ miTag contour; /* opt. contour shader */ miTag photon; /* opt. photon RT shader */ miTag photonvol; /* opt. photon volume shader */ } miMaterial;
A translator must provide: shader.
A translator may provide: all others.
opaque informs mental ray that the material is always opaque and casts an opaque shadow. This is a hint to mental ray and may not be used by all renderers.
shader is the tag of a material shading function. It must refer to a database element of type miSCENE_FUNCTION. This tag may not be miNULLTAG.
displace is the tag of an optional displacement shader function.
shadow is the tag of an optional shadow shader function.
volume is the tag of an optional volume shader function.
environment is the tag of an optional environment shader function.
contour is the tag of an optional contour shader function.
photon is the tag of an optional photon shader function.
photonvol is the tag of an optional photon volume shader function.
Element type: miSCENE_OPTIONS Data type: miOptions Sizes: --- Defaults: as described below
The rendering options were designed to accommodate as wide a range of renderers as possible, from simple wireframe displayers to complex ray tracers. The interpretation of the structure members is left to the renderer. Generally, no renderer will require all provided fields; each renderer may use a different subset. This data structures was named miRc_options in earlier versions of raylib; the old name still works.
typedef struct miOptions { miBoolean trace; miBoolean scanline; miBoolean motion; miBoolean shadow_sort; miBoolean preview_mode; int reflection_depth; int refraction_depth; int trace_depth; int min_samples; int max_samples; miColor contrast; miColor time_contrast; miTag contour_contrast; miTag contour_store; miBoolean caustic; miBoolean globillum; int caustic_accuracy; float caustic_radius; int global_accuracy; float global_radius; float caustic_filter_const; float filter_size_x; float filter_size_y; float jitter; float shutter; int subdivision; int subdivision_2d; int subdivision_memory; int space_max_size; int space_max_depth; float grid_size; int no_images; miImg_type image_types[miRC_MAX_IMAGES]; miBoolean write_image[miRC_MAX_IMAGES]; miBoolean interp_image[miRC_MAX_IMAGES]; miTag shader_tree_image; miTag pixel_sample_image; miCBoolean use_shadow_maps; miCBoolean rendering_shadow_maps; char recompute_shadow_maps; char shadow; char caustic_filter; miCBoolean rendering_caustics; char filter; char acceleration; char face; char field; char smethod; miCBoolean pixel_preview; miCBoolean task_preview; miCBoolean visible_lights; miCBoolean shadow_map_motion; int task_size; miBoolean strips; miTag photonmap_file; miBoolean photonmap_rebuild; int photon_reflection_depth; int photon_refraction_depth; int photon_trace_depth; int space_max_mem; miPointer image[miRC_MAX_IMAGES]; miCBoolean no_lens; miCBoolean no_volume; miCBoolean no_geometry; miCBoolean no_displace; miUint1 no_output; miCBoolean no_merge; miCBoolean spare[2]; int photonvol_accuracy; float photonvol_radius; miCBoolean spare3[16]; int cut_windows; miScalar cut_expand; miApprox approx; miApprox approx_displace; } miOptions;
A translator must provide: nothing.
A translator may provide: all fields.
The parameters are described very briefly; for a detailed description refer to the mental ray User Manual.
trace (default miTRUE) enables ray tracing.
scanline (default miTRUE) enables the first-generation scanline algorithm if applicable to improve speed.
motion (default miFALSE) enables motion blurring.
preview_mode (default miFALSE) not currently used.
shadow (default 1) controls shadow casting. It is one of 0 (no shadows), 1 (normal shadows), 's' (shadow segments with separate volume shaders), and 'l' (shadow intersections sorted from the light source towards the illumination point). Note: Version 2.0.8 older versions do not yet support shadow sorting and shadow segments, and use a simple shadow boolean and a nonfunctional shadow_sort flag. Shadow segments belong to a new set of space probing features introduced in 2.0.4.
filter (default 'b') is one of 'b' (box filter), 't' (triangle filter), and 'g' (gaussian filter).
acceleration (default 'b') is one of 'b' (space subdivision, BSP), 'c' (ray classification), and 'g' (grid).
face (default 'a') enables backface culling, one of 'f' (front-facing only), 'b' (back-facing only), and 'a' (both).
field (default 0) enables field rendering, one of 0 (no field rendering), 'e' (even fields only), and 'o' (odd fields only).
reflection_depth (default 1) limits the ray tracing depth for reflection rays.
refraction_depth (default 1) limits the ray tracing depth for refraction or transparency rays.
trace_depth (default 1) limits the ray tracing depth for the sum of all rays.
min_samples (default -2) specifies the minimum number of samples in recursive oversampling mode. At least 2 min_samples samples will be taken.
max_samples (default 0) specifies the maximum number of samples in recursive oversampling mode. At most 2 max_samples samples will be taken.
contrast (default 0.1 0.1 0.1 0.1) specifies the contrast limit above which more spatial samples are taken.
time_contrast (default 0.3 0.3 0.3 0.3) specifies the contrast limit for motion blurring above which more time samples are taken. Because of blurring, this can usually be much higher than the spatial contrast.
contour_contrast (default no shader) specifies a function that computes contrasts in contour rendering mode.
contour_store (default no shader) specifies a function that collects and stores sample information for use by the contour contrast function, in contour rendering mode.
caustic specifies whether caustics should be rendered.
globillum specifies whether global illumination should be rendered. This mode is available only in version 2.1 or later.
caustic_accuracy specifies the number of photons to use when estimating radiance for caustics.
caustic_radius specifies the maximum distance in which photons used in the radiance estimate for caustics are located. If the radius is 0.0 then an estimate based on the scene extent will be used.
global_accuracy specifies the number of photons to use when estimating radiance for global illumination.
global_radius specifies the maximum distance in which photons used in the radiance estimate for global illumination are located. If the radius is 0.0 then an estimate based on the scene extent will be used.
caustic_filter_const is a constant used when filtering caustics radiance estimates.
caustic_filter is the type of filter used: 'b' box filter, 'c' cone filter and 'g' gauss filter.
rendering_caustics is set when caustics are being rendered.
filter_size_x (default 1.0) specifies the width of the filter specified in the filter field.
filter_size_y (default 1.0) specifies the height of the filter specified in the filter field.
jitter (default 0.0) is the jitter interval. Jittering displaces samples to avoid sampling artefacts.
shutter (default 1.0) is the shutter time of the camera if motion blurring is enabled.
subdivision (default 0) is a correction factor for the number of ray space subdivisions in ray classification acceleration mode.
subdivision_2d (default 0) is a correction factor for the number of shadow ray space subdivisions in ray classification acceleration mode.
subdivision_memory (default 6) specifies how many megabytes the ray classification algorithm should use for acceleration data structures.
space_max_mem (default 0) specifies the maximum memory in megabytes used in the BSP preprocessing. A value of zero indicates that there is no limit on the memory consumption, this is the default. This flag is useful only on multiprocessor machines since the memory consumption increases with the number of rendering threads. When the specified amount of allocated memory is reached, mental ray will prevent threads from being scheduled for preprocessing, thus reducing the memory requirements.
space_max_size (default 4) is the maximum leaf size in BSP mode.
grid_size (default 10.0) specifies the grid size for the grid acceleration method.
space_max_depth (default 24) is the maximum tree depth in BSP mode.
image_types (default miIMG_TYPE_RGBA) is an array specifying the image types to be generated during rendering.
See the documentation of the IMG module for a list of allowed types.
write_image (default miTRUE, miTRUE, ...) specifies for each image whether it is written to a file after rendering completes.
interp_image (default miTRUE, miFALSE, ...) specifies for each image whether it is to be interpolated. This means that if fewer than one sample per pixel is taken, the holes are filled in.
shader_tree_image (internal to rc) is an image which remembers which shaders have been used to render each individual pixel. Each pixel contains 32 bits corresponding to 32 different shaders. If shader 31 is used to render a pixel then bit 31 is set in this image for this pixel. If the image uses more than 32 shaders then then the shader number modulo 32 are used to identify the right bit.
pixel_sample_image (internal to rc) is an image in which each pixel set indicates a pixel which should be re-rendered.
no_images (default 1) is the number of valid entries in the three image arrays above.
use_shadow_maps if miTRUE specifies that shadow maps should be used for the light sources which have shadow maps. This is the global on/off control. The default is off.
rendering_shadow_maps is miTRUE when the shadow maps are being rendered. This option is internal to RC and only used to optimize the rendering of shadow maps.
recompute_shadow_maps can be 'y' meaning that all existing shadow maps are recomputed even if they could have been loaded from a file, 'n' means that no shadow maps are computed - only the ones that could be re-used or loaded from a file are used. Otherwise shadow maps are recomputed "intelligently" meaning that the shadow map code tries to identify those light sources for which a new shadow map needs to be recomputed. This recomputation currently only applies to light sources for which the position or direction of the emitted light (ie. the transformation matrix) changes.
pixel_preview is set when pixel previewing should be used. The default is off.
task_preview is set when task previewing should be used. The default is off.
visible_lights is set if there are any area lights with the 'visible' flag. This option is internal to RC and used to optimize rendering.
shadow_map_motion enables motion blurring of shadowmap shadows. The default is on.
task_size (default 0) is the size of the image tasks to be rendered. If it is zero, a heuristic based on the image resolution will be used in RC in order to calculate an appropriate value. task_size can also be specified explicitly.
strips specifies whether the approximated geometry is represented as triangle strips and fans. The default is off.
photonmap_file is a string containing the filename of a file from which a photon map should be loaded (if possible) or otherwise saved to. If this tag is a miNULLTAG then no photon map is loaded or saved.
photonmap_rebuild will, if true, enforce a recomputation of the photon map even if it could have been loaded from a file.
photon_reflection_depth controls the trace depth of reflected photons. The default is 5.
photon_refraction_depth controls the trace depth of refracted photons. The default is 5.
photon_trace_depth controls the combined trace depth of reflected and refracted photons. The default is 5.
image is an array of pointers to frame buffers. It is valid only in output shaders. Use (miImg_image *)image[n].p to access.
no_lens, if miTRUE, disables all lens shaders. The default is miFALSE.
no_volume, if miTRUE, disables all volume shaders. The default is miFALSE.
no_geometry, if miTRUE, disables all geometry shaders. The default is miFALSE.
no_displace, if miTRUE, disables all displacement shaders. The default is miFALSE.
no_output is a bitmap. Bit zero, it set, disables all output shaders. Bit one, if set, disables writing of output image files. The default is zero for both bits.
no_merge, if miTRUE, disables all edge merging and adjacency detection. The default is miFALSE.
photonvol_accuracy specifies the number of photons to use when estimating radiance inside participating media.
photonvol_radius specifies the maximum distance in which photons used in the radiance estimate for participating media are located. If the radius is 0.0 then an estimate based on the scene extent will be used.
cut_windows enables rendering every image as n*n cut windows if set greater than zero. This is available in mental ray 2.0 and 2.1 only.
cut_expand increases the cut window frustrum by a factor all around the frustrum if greater than zero, to catch invisible outside geometry that becomes visible because it is displaced into the frustrum. The default is 0.1. It has an effect only if cut windows are enabled.
approx overrides all base surface approximations in objects, both free-form surfaces and polygonal, if the approximation style is not miAPPROX_STYLE_NONE. This style is the default, and all other fields are 0. Use the miAPPROX_DEFAULT macro to activate this approximation. See the miApprox data structure description above.
approx_displace is the same thing for displacement approximations.
Element type: miSCENE_IMAGE Data type: miImg_image Sizes: --- Defaults: all nulls
typedef struct miImg_image { miBoolean filter; /* caller wants filtered lookups */ int dirsize; /* valid # of filter levels */ int dir[miIMG_DIRSIZE]; /* offs from this to other imgs */ int width, height; /* width and height in pixels */ int bits; /* requested bits per comp, 8/16/32 */ int comp; /* requested components/pixel, 1..4 */ miBoolean local; /* local texture, use image/mmap/path*/ int null[3]; /* unused */ int c[4]; /* local==miFALSE: */ /* indexed by miIMG_*; 4*height */ /* component indices follow, */ /* then component scanlines */ miPointer image; /* local==miTRUE: */ miImg_file ifp; /* describes open file */ miBoolean mmapped; /* mmapped, not mi_mem_allocated*/ char path[4]; /* local file name to open */ } miImg_image;
A translator must provide: nothing.
A translator may provide: nothing.
Provided by mi_scene_create: all fields.
Images consist of three sections: the header (type miImg_image), a list of scanlines indices, and the actual uncompressed pixel data. There are height *4 scanline indices, one set of comp (up to 4) indices for each scanline. They contain indices relative to the first pixel data byte. The pixel data is arranged in one-component scanlines of width component values each. All images are scanned bottom-up.
For more details, see the documentation of the IMG module.
filter, if nonzero, enables pyramid texture mode.
dirsize and dir are used for internal purposes.
width is the number of pixels per scanline.
height is the number of scanlines.
bits is the number of bits per component pixel.
comp is the number of components per pixel.
local, if false, specifies a local image with scanline index list c followed by pixel data. If true, the image is local, which means there is no simage data but a reference to an image file, which is loaded or mapped at runtime, once on each host.
See the IMG documentation for details.
c is the first scanline index block. Only comp of the 4 available indices are used. It is unused if local is true.
image is used to cache the pointer to the local image. This and the remaining fields are used onbly if local is true.
host is the local host ID. The image pointer is valid only if the host ID matches the local host ID. This guards against network transfers.
ifp is used internally to save information about the open local texture file if memory-mapped.
mmapped is true if the local texture is memory-mapped and needs to be unmapped when the texture is invalidated.
path is the path of the file where each host can find the texture when the image is accessed for the first time.
Element type: miSCENE_STRING Data type: char[ ] Sizes: int no_chars Defaults: ---
Strings hold standard null-terminated character strings. They are used for the string shader parameter type and for shadowmap file names in lights, and for other purposes. The number of characters passed when creating or resizing the string must include the trailing null byte.
Element type: miSCENE_TAG Data type: miTag[ ] Sizes: --- Defaults: ---
Tag lists are not currently used.