Environment Shaders

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:

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→cameraenvironment). Subsequent material shaders get the environment defined in the material if present, or the camera environment otherwise.

In mental ray 2.x, 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. In mental ray 3.x, the environment shader is inherited unless the material defines its own.

Here is an example environment shader that uses a texture that covers an infinite sphere around the scene:

struct mib_texture_lookup_spherical {
    miVector        dir;
    miScalar        rotate;
    miTag           tex;
};

DLLEXPORT miBoolean mib_lookup_spherical(
    miColor         *result,
    miState         *state,
    struct mib_texture_lookup_spherical *paras)
{
    miTag           tex = *mi_eval_tag(&paras->tex);
    miVector        dir = *mi_eval_vector(&paras->dir);
    double          theta, norm;

    result->r = result->g = result->b = result->a = 0;
    if (!tex)
            return(miFALSE);
    if (dir.x == 0 && dir.y == 0 && dir.z == 0)
            mi_vector_to_world(state, &dir, &state->dir);

    norm = mi_vector_norm(&dir);
    if (!norm)
            return(miFALSE);

    /*
     * without rotation, 0 is in the direction of the x axis,
     * increasing in a clockwise direction
     */

    /* avoid calling atan2(0, 0), which return NaN on some platforms */
    if (!dir.x && !dir.z)
            theta = 0.0;
    else
            theta = -atan2(dir.x, dir.z) / (2.0*M_PI);

    theta += *mi_eval_scalar(&paras->rotate) / M_PI;
    theta -= floor(theta);
    dir.x = theta;
    dir.y = asin(dir.y/norm) / M_PI + 0.5;
    dir.z = 0.0;
    return(mi_lookup_color_texture(result, state, tex, &dir));
}

This shader uses a parameter in its shader parameter structure, a tag tex, to specify a texture shader. The texture is evaluated by calling mi_lookup_color_texture, which stores storing the texture coordinate in state→tex and calls the texture shader. For a description of texture shaders and how to call them, see the texture shader section on page texshaderex.

Here is an interesting variation of environment shaders that pastes the environment textures as a background plate, such that it exactly covers the rendered image plane. There are also parameters for zooming and panning to permit a bigger environment, if environment rays are cast outside the normal range.

struct mib_lookup_background {
    miVector        zoom;
    miVector        pan;
    miBoolean       torus_u;
    miBoolean       torus_v;
    miTag           tex;
};

DLLEXPORT miBoolean mib_lookup_background(
    miColor         *result,
    miState         *state,
    struct mib_lookup_background *paras)
{
    miVector        *zoom;
    miVector        *pan;
    miVector        coord;
    miTag           tex = *mi_eval_tag(&paras->tex);

    if (!tex) {
            result->r = result->g = result->b = result->a = 0;
            return(miFALSE);
    }
    zoom = mi_eval_vector(&paras->zoom);
    pan  = mi_eval_vector(&paras->pan);
    coord.x = state->raster_x / state->camera->x_resolution * .9999;
    coord.y = state->raster_y / state->camera->y_resolution * .9999;
    coord.z = 0;
    coord.x = pan->x + (zoom->x ? zoom->x * coord.x : coord.x);
    coord.y = pan->y + (zoom->y ? zoom->y * coord.y : coord.y);
    if (*mi_eval_boolean(&paras->torus_u))
            coord.x -= floor(coord.x);
    if (*mi_eval_boolean(&paras->torus_v))
            coord.x -= floor(coord.y);
    if (coord.x < 0 || coord.y < 0 || coord.x >= 1 || coord.y >= 1) {
            result->r = result->g = result->b = result->a = 0;
            return(miTRUE);
    } else
            return(mi_lookup_color_texture(result, state, tex, &coord));
}

Note that this shader returns miTRUE even if the ray missed the environment texture. Effectively, the shader considers all possible rays, and returns black if the texture is missed. If an environment does not cover all possible points around the scene, it should return black or some other valid color.

Copyright © 1986, 2013 NVIDIA Corporation