Output Shaders

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.

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. Non-procedural textures can be looked up using functions like mi_lookup_color_texture and mi_query. Lights can also be 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:

    struct out_depthfade {
        miScalar   tnear;   /* no fade closer than this */
        miScalar   tfar;    /* farther objects disappear */
    };

    DLLEXPORT miBoolean out_depthfade(
        void                 *result,
        miState              *state,
        struct out_depthfade *paras)
    {
        int         x, y;
        miColor     color;
        miScalar    depth, fade;
        miScalar    tnear, tfar;
        miImg_image *fb_color, *fb_depth;

        tnear    = *mi_eval_scalar(&paras->tnear);
        tfar     = *mi_eval_scalar(&paras->tfar);
        fb_color = mi_output_image_open(state, miRC_IMAGE_RGBA);
        fb_depth = mi_output_image_open(state, miRC_IMAGE_Z);

        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 >= tfar || depth == 0.0)
                                color.r=color.g=color.b=color.a = 0;
                        else if (depth > tnear) {
                                fade = (tfar - depth) / (tfar - tnear);
                                color.r *= fade;
                                color.g *= fade;
                                color.b *= fade;
                                color.a *= fade;
                        }
                        mi_img_put_color(fb_color, &color, x, y);
                }
        }
        mi_output_image_close(state, miRC_IMAGE_Z);
        mi_output_image_close(state, miRC_IMAGE_RGBA);

        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. The parameter names tnear and tfar have been chosen instead of near and far to avoid conflicts with older compilers that pre-define those constants for internal use.

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, and use the link statement instead of code. See the introductory shaders section 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"
    $ifdef "RAY36"
framebuffer "main" datatype "rgba" filetype "tif" filename "out.tif" framebuffer "depth" datatype "z" output "out_depthfade" ("near" 10.0, "far" 100.0)
$else output "rgba,z" "out_depthfade" ("near" 10.0, "far" 100.0) output "tif" "out.tif" $endif ... end camera

3.6 Note, the marked section shows how to use the new .mi syntax to create a named frame buffer with or without image file output and output shader calls. Ordering of the statements is not important, in contrast to previous syntax.

The framebuffer statements request both a color (rgba) and a depth (z) standard frame buffer to be created and rendered by mental ray. After rendering has finished, the output shader will be called which can access the existing buffers and change their pixel values. Finally, the frame buffer(s) which are marked for file output will be written to image files. Note, that the execution order of multiple output shaders is determined by the order of output statements on the camera.

Note, that a depth frame buffer is not filtered (or not interpolated) by default, but the min depth within the pixel is stored. To enable depth values to be filtered a filtering on statement need to be added to the frame buffer definition (see Frame Buffers). Without the depth frame buffer definition, all depth values returned by mi_img_get_depth would be 0.0.

Deprecated For old-style syntax, ordering of statements is more important. The output shader statement appears before the output file statement. This will ensure that the output shader can change the frame buffer content before it is written to the image file out.tif. 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.

Deprecated 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".

This example shader does not anti-alias very well because there is only one depth value per pixel. The shader makes pixels at a depth value of 0.0 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.

Copyright © 1986-2009 by mental images GmbH