Volume Shaders

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.

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

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→cameravolume, 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:

    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(&paras->maxdist);
        fogcolor =  mi_eval_scalar(&paras->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. 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. Note that the shader parameters are evaluated with mi_eval after the if statement to ensure that no unnecessary work is done by obtaining parameter values that are not going to be used.

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

Volume shader that sample light sources should not only clear state→pri but also use this test at the beginning:

    if (state->type == miRAY_LIGHT) return(miTRUE);

Since sampling a light causes one or more light rays to be cast, and since these light rays most likely travel through the same volume, they would also call this same volume shader recursively. If the volume shader doesn't realize this and proceeds to cast even more recursive rays, it will get into an infinite recursion until a stack overflow aborts the program. Note that the test should come before all other code, especially mi_eval calls, to avoid unnecessary work and increase performance.

Copyright © 1986-2009 by mental images GmbH