Photon Emitter Shaders

Photon emitter shaders are used in the photon tracing phase to control the emission of photons from the light sources. There exists a number of built-in photon emitters that handle the following types of light sources:

Unless there are good reasons, these light sources should be used since they are well optimized. This is particularly true for the point light and sphere light which use a projection map to limit the emission of photons to the directions in which caustics generating objects are found. The appropriate built-in photon emitters are automatically applied if no photon emitter is specified in the light source definition.

If more complex light sources are needed, it can be necessary to write a specialized shader. For example, this could be necessary for a spot light that should support intensity fading near the edges of the cone. Note that the light emitter shader is not called if the light has zero energy; such lights are ignored by mental ray during photon tracing.

An example of a simple point light that emits photons uniformly in all directions around the current point is given in the following shader [16]. The shader receives information about the light source from state->light_instance.

    void point_emitter_init(
        miState         *state,
        void            *p,
        miBoolean       *inst_init_req)
    {
        miTag           light_tag;
        miMatrix        *T;

        if (!p)
                *inst_init_req = miTRUE;
        else {
                miVector org, *torg, **user;
                torg = mi_mem_allocate(sizeof(miVector));

                mi_query(miQ_INST_ITEM, state,
                         state->light_instance, &light_tag);
                mi_query(miQ_INST_LOCAL_TO_GLOBAL, state,
                         state->light_instance, &T);
                mi_query(miQ_LIGHT_ORIGIN, state,
                         light_tag, &org);

                mi_point_transform(torg, &org, *T);
                mi_query(miQ_FUNC_USERPTR, state, 0, &user);
                *user = torg;
        }
    }

    void point_emitter_exit(
        miState         *state,
        void            *p)
    {
        miVector        **user;

        mi_query(miQ_FUNC_USERPTR, state, 0, &user);
        if (*user && p) {
                mi_mem_release(*user);
                *user = NULL;
        }
    }

    miBoolean point_emitter(
        miColor         *energy,
        miState         *state,
        void            *p)
    {
        miVector        **user;

        mi_query(miQ_FUNC_USERPTR, state, 0, &user);
        state->org = *(miVector *)*user;
        mi_scattering_dir_diffuse(&state->dir, state);
        mi_photon_light(energy, state);
        return(miTRUE);
    }

This shader does not have any parameters. Instead it extracts all information about the light source with mi_query. This shader can be attached to a light definition using the photon statement. Note the choice of direction using mi_scattering_dir_diffuse, which is called to set the (normalized) photon direction. The org and dir or any other ray state variables are undefined when the shader is called, the shader must come up with its own directions.

If a photon emitter shader returns miFALSE, photon emission from the current light source is aborted at that point and no more photons are cast from this light source. It is recommended that photon emitter shaders return miTRUE only if they called mi_photon_light because otherwise mental ray will keep calling the shader for a long time without ever storing a photon.

Here is a more elaborate example that implements a spherical light emitter. It should be attached to a spherical area light source. The shader uses the radius of the area light source as the radius of the sphere that emits the photons. Similarly, it uses the light origin as the center of the emission sphere, so the shader needs no parameters. It uses an init shader to compute constant data only once per frame instead of once per photon.

    struct ball_emitter_data {
         miVector       org;
         miScalar       radius;
    };

    DLLEXPORT void ball_emitter_init(
        miState         *state,
        void            *p,
        miBoolean       *inst_init_req)
    {
        miTag           light_tag;
        miMatrix        *T;
        miVector        org;
        struct ball_emitter_data **user, *data;

        if (!p) {
                *inst_init_req = miTRUE;
                return;
        }
        mi_query(miQ_FUNC_USERPTR, state, 0, &user);
        data = *user = mi_mem_allocate(sizeof(struct ball_emitter_data));

        mi_query(miQ_INST_ITEM, state, state->light_instance, &light_tag);
        mi_query(miQ_INST_LOCAL_TO_GLOBAL, state, state->light_instance, &T);
        mi_query(miQ_LIGHT_ORIGIN, state, light_tag, &org);
        mi_query(miQ_LIGHT_AREA_S_RADIUS, state, light_tag, &data->radius);

        mi_point_transform(&data->org, &org, *T);
    }

    DLLEXPORT void ball_emitter_exit(
        miState         *state,
        void            *p)
    {
        if (p) {
                struct ball_emitter_data **user;

                mi_query(miQ_FUNC_USERPTR, state, 0, &user);
                mi_mem_release(*user);
                *user = NULL;
        }
    }

    DLLEXPORT miBoolean ball_emitter(
        miColor         *energy,
        miState         *state,
        void            *paras)            /* not used, no parameters */
    {
        struct ball_emitter_data **user;
        double          samples[3];
        miVector        shift;
        miColor         comp_energy;

        mi_query(miQ_FUNC_USERPTR, state, 0, &user);

        state->org = (*user)->org;

        /* get single 3-dimensional sample */
        mi_sample(samples, NULL, state, 3, NULL);
        shift.x = 2 * samples[0] - 1;
        shift.y = 2 * samples[1] - 1;
        shift.z = 2 * samples[2] - 1;

        /* rejected sample */
        if (mi_vector_norm(&shift) > 1.0)
                return(miTRUE);

        mi_vector_mul(&shift, (*user)->radius);
        mi_vector_add(&state->org, &state->org, &shift);

        /* compensate photon energy by the proportion of rejected samples */
        /* volume of cube / volume of ball = 8 / (4/3 PI) = 6 / PI */
        comp_energy.r = energy->r * 6 / M_PI;
        comp_energy.g = energy->g * 6 / M_PI;
        comp_energy.b = energy->b * 6 / M_PI;

        mi_scattering_dir_diffuse(&state->dir, state);

        mi_photon_light(&comp_energy, state);
        return(miTRUE);
    }

Like light shaders, emitter shaders also support geometric area light sources. No special changes need to be made if all points on the light object emit light uniformly, but the emitter shader also has access to the chosen emission point on the light object in state->child. The state is the normal emission shader state, and the child state is similar to the state a material shader would find when a visible ray intersects the object. In particular, the point and tex_list state variables are set correctly in state->child. See page shdgeolight for more detail.

[16] This is just a simple example shader - the built-in point light emitter is much more efficient.

Copyright © 1986-2008 by mental images GmbH