Keywords: main

Next: 3.21 Functions for Shaders Up: 3. Using and Writing Previous: 3.19 Geometry Shaders
Subsections

- 3.20.1 Contour Computation
- 3.20.2 Contour Store Function
- 3.20.3 Contour Contrast Function
- 3.20.4 Contour Shaders
- 3.20.5 Contour Output Shaders

# 3.20 Contours

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 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 shader, the contour contrast shader, and the contour shaders are called. Note that contour shaders do not follow the normal shader argument conventions. They do support initialization and exit shaders with standard init/exit shader prototypes and semantics.

## 3.20.1 Contour Computation

A regular color image is created by recursively sampling the scene, that is, 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 shader returns `miTRUE`. The contour contrast shader 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 (that is, the contour contrast shader 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 some other information saved by the contour store shader. 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 straight contour line segments, each represented by its two endpoints.

These contour line segments are received by a contour output shader in a postprocessing step. The contour output shader reads the contour segment endpoints and generates, for example, an image or a PostScript file.

## 3.20.2 Contour Store Function

The contour store shader 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 shader for a scene. The contour store function's job is to collect all information that the contour contrast function needs to decide where to put contours and that the contour shaders need to draw a contour.

To give an example of a contour store shader, 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;

Here is a contour store shader that fills in these fields of `MyInfo`:

         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 shader is called. The number of stored `MyInfo` data structures grows up to the number of samples taken in an image task (typically a 32 `x` 32 pixel block).

## 3.20.3 Contour Contrast Function

The contour contrast shader 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 large. The decision is based on the contour information for two points (the information saved by the contour store shader), the reflection or refraction level of the two points, the state, and the parameters of the contour contrast function. The output is a Boolean value indicating whether there should be a contour between the two points. If the returned value is `miTRUE`, one of two things can happen: If the distance between the two points corresponds to `max samples`, a contour will be placed between the two points (getting contour width, color, etc. by calling the material's contour shader). If not, mental ray will take some additional samples to get a more precise position of the contour. There is only one contour contrast shader for the scene.

As an example of a user-defined contour contrast shader, 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 (mi_vector_dot(&info1->normal, &info2->normal) <
                                cos(paras->ndelta * M_PI/180.0))
                return(miTRUE);

            /*
             * No contour otherwise
             */
            return(miFALSE);
         }

## 3.20.4 Contour Shaders

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 shader), the state, and shader parameters. Each material can have a separate contour shader. If no contour shader is specified for a material, that material does not get a contour.

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 image X 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`.

## 3.20.5 Contour Output Shaders

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. 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;

            mi_info("Contour endpoints:");

            /* Get and write the contour endpoints */
            while (mi_get_contour_line(&p1, &p2))
                 mi_info("%f %f --- %f %f",
                              p1.point.x, p1.point.y,
                              p2.point.x, p2.point.y);
            return(miTRUE);
         }

Next: 3.21 Functions for Shaders Up: 3. Using and Writing Previous: 3.19 Geometry Shaders