Shadow Shaders

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 the resulting color is black, that is, if the object is fully opaque, the shadow shader should return miFALSE.

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 or segmented shadows are turned on. Shadow shaders that rely on being called in the correct order, the one closest to the light source first, should define shadow sort in their shader declaration to ensure that shadow sorting is enabled if the shader is used.

Shadow shaders should check for shadow segment mode. 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:

If a new material shader is written, it is often necessary to also write a matching shadow shader. The shadow shader performs a subset of the calculations done in the material shader: it may evaluate textures and transparencies, but it will not sample lights and it will not cast secondary rays (other than shadow rays in shadow segment mode). The shader writer can either write a separate shadow shader, or let the material shader double as shadow shader by building the scene such that the material shader appears twice in the material definition. Most shaders take the latter approach. It relies on the material shader to omit all calculations that are necessary only in the material shader when called as a shadow shader. The shader can find out whether it is called as a material shader or as a shadow shader by checking if state→type is miRAY_SHADOW: if yes, this is a shadow shader.

The following shadow shader is a separate shader that attenuates the light that passes through the object based on two shader 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, it still gets a copy of the material shader's shader 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 avoids having to duplicate a large set of parameters.

    miBoolean myshadow(
        miColor         *result,
        miState         *state,
        struct myshadow *paras)
    {
        miScalar        opacity;
        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->b = 0;
        }
        opacity = 1 - *mi_eval_scalar(&paras->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.

Copyright © 1986-2009 by mental images GmbH