home *** CD-ROM | disk | FTP | other *** search
- /* ****************************************************************
- * illum_mod.c
- * ****************************************************************
- * MODULE PURPOSE:
- * This module contains the source code for the illumination
- * models discussed for in Section 4.2, Incremental Shading,
- * Empirical Models, and in Section 4.3, Ray Tracing,
- * Transitional Models.
- *
- * MODULE CONTENTS:
- * IM_init - initialize the illumination models
- * IM_bouknight - Bouknight (1970) illumination model
- * IM_phong - Phong (1975) illumination model
- * IM_blinn - Blinn (1976) illumination model
- * IM_whitted - Whitted (1980) illumination model
- * IM_hall - Hall (1983) illumination model
- * IM_exit - finish with the illumination models
- *
- * NOTES:
- * > The illumination model is called once a
- * point on a surface has been identified and the list
- * of illuminating light sources has been generated.
- *
- * > There exists a routine that the illumination models can
- * call to get a color from any direction. Specifically
- * this is used for inquiring about the reflected or
- * transmitted directions in the ray tracing models.
- * This routine is passed the view vector for which the
- * color is required.
- *
- * > a common calling convention is used for ease in
- * interchanging the illumination model routines. Each
- * routine is passed the location and orientation of the
- * surface, a view vector, an interface material, a
- * description of the lighting, and an array to receive
- * the computed color.
- *
- * The orientation of the surface is given by the surface
- * normal which is ALWAYS directed to the 'outside' or
- * reflected side of the material.
- *
- * The view vector is specified by start position and
- * direction vector. During visibility computations the
- * view vector is typically directed towards the surface.
- * The direction cosines MUST be negated prior to calling
- * the illumination model for consistency with the vector
- * conventions used.
- *
- * See 'illum_mod.h' for material details.
- *
- * The light vector is a list of pointers to ILLUM_LGT
- * structures terminated by a NULL pointer. The first
- * entry is taken as the ambient illumination. Only
- * light that is incident from the air side of a material
- * can provide illumination.
- *
- * > These models assume that the material structure is
- * correctly loaded and that the surface is facing the
- * viewer (N.V > 0) for illumination models that do not
- * consider transparency.
- */
- #include <stdio.h>
- #include <math.h>
- #include "illum_mod.h"
- #include "F.h"
-
- static int num_samples = 0;
- static int (*get_color)() = NULL;
- static double *Fr_buffer = NULL;
- static double *Ft_buffer = NULL;
-
- /* ****************************************************************
- * int IM_init (num_clr_samples, get_clr_routine)
- * int num_clr_samples (in) - number of color samples
- * int (*get_clr_routine)() (in) - routine to call to get
- * the color from some direction
- *
- * Initializes the illumination model routine set, returns TRUE
- * if successful and FALSE upon failure.
- */
- IM_init (num_clr_samples, get_clr_routine)
- int num_clr_samples, (*get_clr_routine)();
- { char *malloc();
- if (((num_samples = num_clr_samples) <= 0) ||
- ((Fr_buffer = (double *)malloc((unsigned)(num_samples *
- sizeof(double)))) == NULL) ||
- ((Ft_buffer = (double *)malloc((unsigned)(num_samples *
- sizeof(double)))) == NULL)) {
- (void)IM_exit();
- return FALSE;
- }
- get_color = get_clr_routine;
- return TRUE;
- }
-
- /* ****************************************************************
- * double *IM_bouknight (surface, V, matl, lgts, color)
- * LINE *surface (in) - surface for color computation
- * LINE *V (in) - view vector
- * ILLUM_MATL *matl (in) - material properties
- * ILLUM_LGT **lgts (in) - illuminating sources
- * double *color (mod) - array to receive the color
- *
- * Evaluates the color using the Bouknight (1970) illumination
- * model as described in Eq.(\*(sE). Returns 'color' upon
- * success and NULL upon failure.
- */
- double *IM_bouknight (surface, V, matl, lgts, color)
- LINE *surface, *V;
- ILLUM_MATL *matl;
- ILLUM_LGT **lgts;
- double *color;
- { int ct;
- double N_dot_L;
- LINE L;
-
- /* load the ambient illumination */
- for (ct=0; ct<num_samples; ct++) {
- color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
- (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- lgts++;
-
- /* load the diffuse component of the illumination. Loop
- * through the lights and compute (N.L). If it is positive
- * then the surface is illuminated.
- */
- while (*lgts != NULL) {
- if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
- && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)) {
-
- /* The surface is illuminated by this light. Loop through
- * the color samples and sum the diffuse contribution for
- * this light to the the color.
- */
- for (ct=0; ct<num_samples; ct++) {
- color[ct] += matl->Kd_scale * matl->Kd_spectral[ct]
- * N_dot_L * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- }
- lgts++;
- }
-
- return color;
- }
-
- /* ****************************************************************
- * double *IM_phong (surface, V, matl, lgts, color)
- * LINE *surface (in) - surface for color computation
- * LINE *V (in) - view vector
- * ILLUM_MATL *matl (in) - material properties
- * ILLUM_LGT **lgts (in) - illuminating sources
- * double *color (mod) - array to receive the color
- *
- * Evaluates the color using the Phong (1975) illumination
- * model as described in Eq.(\*(rU). Returns 'color' upon
- * success and NULL upon failure.
- *
- * The actual Phong model results when the microfacet distribution
- * D_phong() is used, and matl->Ks_spectral is the identity
- * material.
- *
- * Using the microfacet distribution D_blinn() gives Blinn's
- * interpretation of the Phong model per Eq.(\*(rI).
- */
- double *IM_phong (surface, V, matl, lgts, color)
- LINE *surface, *V;
- ILLUM_MATL *matl;
- ILLUM_LGT **lgts;
- double *color;
- { int ct;
- double N_dot_L, D;
- LINE L;
-
- /* load the ambient illumination */
- for (ct=0; ct<num_samples; ct++) {
- color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
- (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- lgts++;
-
- /* load the diffuse and specular illumination components.
- * Loop through the lights and compute (N.L). If it is
- * positive then the surface is illuminated.
- */
- while (*lgts != NULL) {
- if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
- && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)) {
-
- /* The surface is illuminated. Compute the microfacet
- * distribution function.
- */
- D = (*(matl->D))(&(surface->dir), &(L.dir), &(V->dir),
- matl->D_coeff);
-
- /* Loop through the color samples and sum the diffuse
- * and specular contribution for this light to the the
- * color.
- */
- for (ct=0; ct<num_samples; ct++) {
- color[ct] +=
- ((N_dot_L * matl->Kd_scale * matl->Kd_spectral[ct])
- + (D * matl->Ks_scale * matl->Ks_spectral[ct]))
- * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- }
- lgts++;
- }
-
- return color;
- }
-
- /* ****************************************************************
- * double *IM_blinn (surface, V, matl, lgts, color)
- * LINE *surface (in) - surface for color computation
- * LINE *V (in) - view vector
- * ILLUM_MATL *matl (in) - material properties
- * ILLUM_LGT **lgts (in) - illuminating sources
- * double *color (mod) - array to receive the color
- *
- * Evaluates the color using the Blinn (1977) illumination
- * model as described in Eq.(\*(rV). Returns 'color' upon
- * success and NULL upon failure. The microfacet distribution
- * functions D_blinn(), D_gaussian(), and D_reitz are the three
- * functions presented by Blinn. The geometric attenuation
- * function G_torrance() is the attenuation function used by Blinn.
- * If matl->G is NULL then the geometric attenuation is omitted.
- * The Fresnel reflectance is approximated using the Cook (1983)
- * technique.
- */
- double *IM_blinn (surface, V, matl, lgts, color)
- LINE *surface, *V;
- ILLUM_MATL *matl;
- ILLUM_LGT **lgts;
- double *color;
- { int ct;
- double N_dot_L, N_dot_V, D, G, *Fr;
- LINE L;
- DIR_VECT *T, *H;
-
- /* load the ambient illumination */
- for (ct=0; ct<num_samples; ct++) {
- color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
- (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- lgts++;
-
- /* load the diffuse and specular illumination components.
- * Loop through the lights and compute (N.L). If it is
- * positive then the surface is illuminated.
- */
- while (*lgts != NULL) {
- if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
- && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)) {
-
- /* The surface is illuminated. Compute the microfacet
- * distribution, geometric attenuation, Fresnel
- * reflectance and (N.V) for the specular function.
- */
- D = (*(matl->D))(&(surface->dir), &(L.dir), &(V->dir),
- matl->D_coeff);
- if (matl->G == NULL)
- G = 1.0;
- else
- G = (*(matl->G))(&(surface->dir), &(L.dir),
- &(V->dir), matl->G_coeff);
- H = geo_H(&(L.dir), &(V->dir));
- if (matl->conductor) {
- Fr = F_approx_Fr(H, &(L.dir), matl->Ro, matl->nt,
- matl->k, num_samples, matl->Ks_spectral, Fr_buffer);
- }
- else {
- T = geo_rfr(&(V->dir),H, matl->nr, matl->nt);
- Fr = F_approx_Fr_Ft(H, &(L.dir), T,
- matl->Ro, matl->nr, matl->nt, num_samples,
- matl->Ks_spectral, Fr_buffer, Ft_buffer);
- }
-
- /* Loop through the color samples and sum the diffuse
- * and specular contribution for this light to the the
- * color. Note the threshold on N_dot_V to prevent
- * divide by zero at grazing.
- */
- if ((N_dot_V=geo_dot(&(surface->dir),&(V->dir))) > 0.0001){
- for (ct=0; ct<num_samples; ct++) {
- color[ct] +=
- ((N_dot_L * matl->Kd_scale * matl->Kd_spectral[ct])
- + ((D * G * matl->Ks_scale * Fr[ct]) / N_dot_V))
- * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- }
- }
- lgts++;
- }
-
- return color;
- }
-
- /* ****************************************************************
- * double *IM_whitted (surface, V, matl, lgts, color)
- * LINE *surface (in) - surface for color computation
- * LINE *V (in) - view vector
- * ILLUM_MATL *matl (in) - material properties
- * ILLUM_LGT *lgts (in) - illuminating sources
- * double *color (mod) - array to receive the color
- *
- * Evaluates the color using the Whitted (1980) illumination
- * model as described in Eq.(\*(e6). Returns 'color' upon
- * success and NULL upon failure.
- *
- * The actual Whitted model results when the microfacet
- * distribution D_blinn() is used, and when both matl->Ks_spectral
- * and matl->Kt_spectral are the identity material.
- *
- * The matl->Kt_scale and matl->Kt_spectral are required for this
- * illumination model only.
- */
- double *IM_whitted (surface, V, matl, lgts, color)
- LINE *surface, *V;
- ILLUM_MATL *matl;
- ILLUM_LGT **lgts;
- double *color;
- { int inside = FALSE;
- int ct;
- double N_dot_L, D;
- LINE L;
-
- /* figure out whether we are on the reflected or transmitted
- * side of the material interface (outside or inside). If
- * we are inside a material, then there is no illumination
- * from lights and such - skip directly to reflection and
- * refraction contribution.
- */
- if ((geo_dot(&(surface->dir),&(V->dir)) < 0) ||
- (!matl->r_is_air)) {
- for (ct=0; ct<num_samples; ct++) color[ct] = 0.0;
- inside = TRUE;
- goto rfl_rfr;
- }
-
- /* If we are at interface between materials, neither of which
- * is air, then skip directly to reflection and refraction
- */
- if (!matl->r_is_air) goto rfl_rfr;
-
- /* load the ambient illumination */
- for (ct=0; ct<num_samples; ct++) {
- color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
- (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- lgts++;
-
- /* load the diffuse and specular illumination components.
- * Loop through the lights and compute (N.L). If it is
- * positive then the surface is illuminated.
- */
- while (*lgts != NULL) {
- if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
- && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)) {
-
- /* The surface is illuminated. Compute the microfacet
- * distribution function.
- */
- D = (*(matl->D))(&(surface->dir), &(L.dir), &(V->dir),
- matl->D_coeff);
-
- /* Loop through the color samples and sum the diffuse
- * and specular contribution for this light to the the
- * color.
- */
- for (ct=0; ct<num_samples; ct++) {
- color[ct] +=
- ((N_dot_L * matl->Kd_scale * matl->Kd_spectral[ct])
- + (D * matl->Ks_scale * matl->Ks_spectral[ct]))
- * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- }
- lgts++;
- }
-
- /* compute the contribution from the reflection and
- * refraction directions. Get a buffer to hold the
- * computed colors, then process the reflected direction
- * and the refracted direction.
- */
- rfl_rfr:
- if (get_color != NULL) {
- char *malloc();
- double *Ir_or_It;
- LINE V_new;
- DIR_VECT *T;
-
- if ((Ir_or_It = (double *)malloc((unsigned)(num_samples *
- sizeof(double)))) == NULL) return color;
-
- /* get the reflected vector then ask for the color from
- * the reflected direction. If there is a color, then
- * sum it with the current color
- */
- V_new.start = surface->start;
- V_new.dir = *(geo_rfl(&(V->dir),&(surface->dir)));
- if ((*get_color)(&V_new, Ir_or_It) != NULL)
- for (ct=0; ct<num_samples; ct++) color[ct] +=
- Ir_or_It[ct] * matl->Ks_scale * matl->Ks_spectral[ct];
-
- /* if the material is transparent then get the refracted
- * vector and ask for the color from the refracted
- * direction. If there is a color, then sum it with
- * the current color
- */
- if (matl->transparent) {
- V_new.start = surface->start;
- if (inside)
- T = geo_rfr(&(V->dir),&(surface->dir),
- matl->nt, matl->nr);
- else
- T = geo_rfr(&(V->dir),&(surface->dir),
- matl->nr, matl->nt);
- if (T != NULL) {
- V_new.dir = *T;
- if ((*get_color)(&V_new, Ir_or_It) != NULL)
- for (ct=0; ct<num_samples; ct++) color[ct] +=
- Ir_or_It[ct] * matl->Kt_scale *
- matl->Kt_spectral[ct];
- }
- }
- (void)free((char *)Ir_or_It);
- }
-
- return color;
- }
-
- /* ****************************************************************
- * double *IM_hall (surface, V, matl, lgts, color)
- * LINE *surface (in) - surface for color computation
- * LINE *V (in) - view vector
- * ILLUM_MATL *matl (in) - material properties
- * ILLUM_LGT *lgts (in) - illuminating sources
- * double *color (mod) - array to receive the color
- *
- * Evaluates the color using the Hall (1983) illumination
- * model as described in Eq.(\*(sF). Returns 'color' upon
- * success and NULL upon failure.
- *
- * The actual Hall model results when the microfacet
- * distribution D_blinn() is used, and matl->D = NULL.
- *
- * The transmittance is computed from the reflectance, so
- * matl->Kt_scale and matl->Kt_spectral are not used in the model.
- */
- double *IM_hall (surface, V, matl, lgts, color)
- LINE *surface, *V;
- ILLUM_MATL *matl;
- ILLUM_LGT **lgts;
- double *color;
- { int inside = FALSE;
- int ct;
- double N_dot_L, D, G, *Fr;
- LINE L;
- DIR_VECT *T, *H, *Ht, pseudo_V;
-
- /* figure out whether we are on the reflected or transmitted
- * side of the material interface (outside or inside). If
- * we are inside a material, then there may be illumination
- * from outside.
- */
- if (geo_dot(&(surface->dir),&(V->dir)) < 0) {
- for (ct=0; ct<num_samples; ct++) color[ct] = 0.0;
- inside = TRUE;
- }
-
- /* If we are at interface between materials, neither of which
- * is air, then skip directly to reflection and refraction
- */
- if (!matl->r_is_air) goto rfl_rfr;
-
- /* load the ambient illumination if we are not inside
- * inside the material
- */
- if (!inside) {
- for (ct=0; ct<num_samples; ct++) {
- color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
- (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- }
- lgts++;
-
- /* load the diffuse and specular illumination components.
- * Loop through the lights and compute (N.L). If it is
- * positive then the surface is illuminated. If it is
- * negative, then there may be transmitted illumination.
- */
- while (*lgts != NULL) {
- if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
- && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)
- && !inside) {
-
- /* The surface is illuminated. Compute the microfacet
- * distribution, geometric attenuation, Fresnel
- * reflectance and (N.V) for the specular function.
- */
- D = (*(matl->D))(&(surface->dir), &(L.dir), &(V->dir),
- matl->D_coeff);
- if (matl->G == NULL)
- G = 1.0;
- else
- G = (*(matl->G))(&(surface->dir), &(L.dir),
- &(V->dir), matl->G_coeff);
- H = geo_H(&(L.dir), &(V->dir));
- if (matl->conductor) {
- Fr = F_approx_Fr(H, &(L.dir), matl->Ro, matl->nt,
- matl->k, num_samples, matl->Ks_spectral, Fr_buffer);
- }
- else {
- T = geo_rfr(&(L.dir), H, matl->nr, matl->nt);
- Fr = F_approx_Fr_Ft(H, &(L.dir), T,
- matl->Ro, matl->nr, matl->nt, num_samples,
- matl->Ks_spectral, Fr_buffer, Ft_buffer);
- }
-
-
- /* Loop through the color samples and sum the diffuse
- * and specular contribution for this light to the the
- * color.
- */
- for (ct=0; ct<num_samples; ct++) {
- color[ct] +=
- ((N_dot_L * matl->Kd_scale * matl->Kd_spectral[ct])
- + (D * G * matl->Ks_scale * Fr[ct]))
- * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- }
- else if ((N_dot_L > 0) && inside) {
- /* We are inside and the light is outside. Compute
- * the transmitted contribution from the light
- */
- if ((Ht = geo_Ht(&(L.dir),&(V->dir),matl->nr,
- matl->nt)) != NULL) {
- /* The microfacet distribution functions could
- * only be equated when cast in terms of the primary
- * vectors L, V, and N. A pseudo_V vector is required
- * so that any of the distribution functions can be
- * applied. Ht is the vector bisector between of the
- * angle between L and pseudo_V, thus pseudo_V can be
- * computed by reflecting L about Ht. Refer to the
- * text for details.
- */
- pseudo_V = *(geo_rfl(&(L.dir), Ht));
- D = (*(matl->D))(&(surface->dir), &(L.dir),
- &pseudo_V, matl->D_coeff);
- Fr = F_approx_Fr_Ft(Ht, &(L.dir), &(V->dir),
- matl->Ro, matl->nr, matl->nt, num_samples,
- matl->Ks_spectral, Fr_buffer, Ft_buffer);
- if (matl->G == NULL)
- G = 1.0;
- else {
- /* To include the geometric attenuation, the view
- * vector direction must be reversed so that it
- * is to the same side of the surface as the
- * normal, see text for details.
- */
- pseudo_V.i = -V->dir.i;
- pseudo_V.j = -V->dir.j;
- pseudo_V.k = -V->dir.k;
- G = (*(matl->G))(&(surface->dir), &(L.dir),
- &pseudo_V, matl->G_coeff);
- }
- for (ct=0; ct<num_samples; ct++) {
- color[ct] += (D * G * matl->Ks_scale * Ft_buffer[ct])
- * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
- }
- }
- }
- lgts++;
- }
-
- /* compute the contribution from the reflection and
- * refraction directions. Get a buffer to hold the
- * computed colors, then process the reflected direction
- * and the refracted direction.
- */
- rfl_rfr:
- if (get_color != NULL) {
- char *malloc();
- double *Ir_or_It;
- LINE V_new;
- DIR_VECT pseudo_N;
-
- if ((Ir_or_It = (double *)malloc((unsigned)(num_samples *
- sizeof(double)))) == NULL) return color;
-
- /* Determine the Fresnel reflectance and transmittance.
- * If we are inside the material, then a pseudo normal
- * is required that faces to the same side of the
- * interface as the view vector.
- */
- if (matl->conductor) {
- Fr = F_approx_Fr(&(surface->dir), &(V->dir), matl->Ro,
- matl->nt, matl->k, num_samples, matl->Ks_spectral,
- Fr_buffer);
- }
- else if (inside) {
- T = geo_rfr(&(V->dir),&(surface->dir),
- matl->nt, matl->nr);
- pseudo_N.i = -surface->dir.i;
- pseudo_N.j = -surface->dir.j;
- pseudo_N.k = -surface->dir.k;
- Fr = F_approx_Fr_Ft(&pseudo_N, &(V->dir), T,
- matl->Ro, matl->nt, matl->nr, num_samples,
- matl->Ks_spectral, Fr_buffer, Ft_buffer);
- }
- else {
- T = geo_rfr(&(V->dir),&(surface->dir),
- matl->nr, matl->nt);
- Fr = F_approx_Fr_Ft(&(surface->dir), &(V->dir),
- T, matl->Ro, matl->nr, matl->nt, num_samples,
- matl->Ks_spectral, Fr_buffer, Ft_buffer);
- }
-
- /* get the reflected vector then ask for the color from
- * the reflected direction. If there is a color, then
- * sum it with the current color
- */
- V_new.start = surface->start;
- V_new.dir = *(geo_rfl(&(V->dir),&(surface->dir)));
- if ((*get_color)(&V_new, Ir_or_It) != NULL) {
- for (ct=0; ct<num_samples; ct++) color[ct] +=
- Ir_or_It[ct] * matl->Ks_scale * Fr[ct];
- }
-
- /* if the material is transparent then get the refracted
- * vector and ask for the color from the refracted
- * direction. If there is a color, then sum it with
- * the current color
- */
- if (matl->transparent && (T != NULL)) {
- V_new.start = surface->start;
- V_new.dir = *T;
- if ((*get_color)(&V_new, Ir_or_It) != NULL)
- for (ct=0; ct<num_samples; ct++) color[ct] +=
- Ir_or_It[ct] * matl->Kt_scale * Ft_buffer[ct];
- }
- (void)free((char *)Ir_or_It);
- }
-
- /* If we are inside a material that has a filter attenuation
- * then apply the attenuation to the color.
- */
- if ( ((!inside) && ((Fr = matl->Ar_spectral) != NULL)) ||
- ((inside) && ((Fr = matl->At_spectral) != NULL)) ) {
- double dist;
- dist = sqrt ( ((surface->start.x - V->start.x) *
- (surface->start.x - V->start.x)) +
- ((surface->start.y - V->start.y) *
- (surface->start.y - V->start.y)) +
- ((surface->start.z - V->start.z) *
- (surface->start.z - V->start.z)) );
- for (ct=0; ct<num_samples; ct++)
- color[ct] *= pow (Fr[ct], dist);
- }
-
- return color;
- }
-
- /* ****************************************************************
- * int IM_exit ()
- *
- * Finishes use of the illumination models routines.
- */
- IM_exit()
- {
- if (Fr_buffer != NULL) {
- (void)free((char *)Fr_buffer); Fr_buffer = NULL;
- }
- if (Ft_buffer != NULL) {
- (void)free((char *)Ft_buffer); Ft_buffer = NULL;
- }
- num_samples = 0;
- get_color = NULL;
- return TRUE;
- }
- /* ************************************************************* */
-