/****************************************************************************** * Copyright 1986-2004 by mental images GmbH, Fasanenstr. 81, D-10623 Berlin, * Germany. All rights reserved. ****************************************************************************** * Created: 10.05.96 * Module: Physics * Purpose: diffuse, glossy, and specular reflection and transmission * * Exports: * dgs_material_version * dgs_material * dgs_material_photon * * History: * 14.01.98: removed install function, this is now a library * 19.05.98: added cylinder light source to physical_light(). * 26.05.98: changed computation of alpha. * 17.06.98: split off physical_light(). Added direct illumination * translucency (direct diffuse and glossy transmission). * 13.10.98: No longer supports textures -- base shaders take care of * that (also for anisotropic brushing direction). Use mi_eval(). * 06.07.00: major cleanup * 25.01.01: Fixed several bugs. Direct glossy is computed with ray * tracing instead of mi_sample_light. * * Description: * Material shader and photon shader with the following reflection types: * - Diffuse (Lambertian) * - Glossy (Ward model, isotropic and anisotropic, for anisotropic first * derivations are needed to obtain orientation) * - Specular (ideal mirror) * The material shader dgs_material() is used for rays originating from the * eye/camera. The photon shader dgs_material_photon() is used to trace photons * from the light sources. If called as a shadow shader, dgs_material() returns * miFALSE (i.e. completely opaque). This is because we cannot compute shadows * along bent rays. Instead, this type of illumination is handled by the photon * map. * * Known limitations: * An off-specular peak is not simulated by the Ward glossy reflection model. * To get that effect, the Torrance-Sparrow model should be used (but it might * be necessary to use rejection sampling for that model). * *****************************************************************************/ #include #include #include #include #include #include #include #define COLOR_ADD_PROD(R,A,B,F) do{\ (R)->r += (A)->r*(B)->r*(F);\ (R)->g += (A)->g*(B)->g*(F);\ (R)->b += (A)->b*(B)->b*(F);\ } while(0) char dgsshade_id[] = "==@@== compiled for ray " MI_VERSION_STRING " on " MI_DATE_STRING; static miColor solid_black = {0, 0, 0, 1}; static miColor transp_black = {0, 0, 0, 0}; /* * Material shader; direct and indirect illumination * * Calculate illumination of a material by its lights. m and paras are both * passed because m contains all the colors updated by textures, and paras * has the arrays (m is a copy of the fixed struct without appended arrays). * Phong and Blinn are glossy, *not* specular according to Hanrahan's * definition of terms! We don't want to model Phong and Blinn *glossy* * reflection in globillum --- these are left to mib_illum_*. Phong isn't * physical (not energy-preserving), and Blinn is hard to importance-sample. * This function calculates the front (reflective) and back (refractive) * sides as separate light loops. This may seem inefficient because of the * duplicate mi_sample_light calls, but each call rejects lights on the * wrong side of state->normal early, and state->normal is flipped. */ /* * Compute direct diffuse and glossy components of illumination. * m->diffuse is reflectance rho (0 <= rho <= 1). BRDF f_r = rho/pi. * We also have to multiply by cosine at the receiver. The cosine at the light * source and the square of the distance is taken care of by the light shader. * The area of the light source is irrelevant since its emission is specified * by power, not radiance. */ static void direct_illumination( miColor *result, /* returned illum color */ miState *state, /* ray tracer state */ struct dgs_material *m, /* mi_eval'd material paras */ miTag *light) /* light instance tag */ { int n; /* light counter */ int samples; /* # of samples taken */ miColor color; /* color from light source */ miColor sum; /* summed sample colors */ miVector dir; /* direction towards light */ miScalar dot_nl; /* dot prod of normal and dir */ miScalar f; /* various temp factors */ miBoolean diffuse, glossy;/* correspondent parts > 0 */ *result = solid_black; /* return immediately if no diffuse component */ diffuse = m->diffuse.r > 0 || m->diffuse.g > 0 || m->diffuse.b > 0; glossy = m->glossy.r>0 || m->glossy.g>0 || m->glossy.b > 0; if (!diffuse && !glossy) return; light += m->i_light; /* compute direct illumination on front side for each light source */ if (m->transp != 1.0) { for (n=0; n < m->n_light; n++) { sum = solid_black; samples = 0; while (mi_sample_light(&color, &dir, &dot_nl, state, light[n], &samples)) { if (diffuse) { /* albedo*cos/Pi */ f = (1 - m->transp) * dot_nl * (1/M_PI); COLOR_ADD_PROD(&sum, &m->diffuse, &color, f); } if (glossy) { f = (1 - m->transp) * dot_nl * dgs_ward_glossy(&state->dir, &dir, state, m); COLOR_ADD_PROD(&sum, &m->glossy, &color, f); } } /* Add contribution of light source */ if (samples) { result->r += sum.r / samples; result->g += sum.g / samples; result->b += sum.b / samples; } } } /* * Compute direct illumination on back side for each light source */ if (m->transp != 0.0) { miVector m_dir; /* mirror rel. to tangent plane */ miScalar m_ok = miFALSE; /* not full reflection */ if (mi_refraction_dir(&m_dir, state,state->ior_in,state->ior)) { /* m_dir = 2 (normal . m_dir) normal - m_dir */ miVector tmp = state->normal; miScalar dot = mi_vector_dot(&state->normal, &m_dir); mi_vector_mul(&tmp, 2 * dot); mi_vector_sub(&m_dir , &m_dir, &tmp); m_ok = miTRUE; } /* flip normals */ mi_vector_neg(&state->normal); mi_vector_neg(&state->normal_geom); for (n=0; n < m->n_light; n++) { sum = solid_black; samples = 0; while (mi_sample_light(&color, &dir, &dot_nl, state, light[n], &samples)) { if (diffuse) { /* albedo*cos/Pi */ f = m->transp * dot_nl * (1/M_PI); COLOR_ADD_PROD(&sum, &m->diffuse, &color, f); } if (glossy && m_ok) { f = m->transp * dot_nl * dgs_ward_glossy(&m_dir, &dir, state, m); COLOR_ADD_PROD(&sum, &m->glossy, &color, f); } } /* Add contribution of light source */ if (samples) { result->r += sum.r / samples; result->g += sum.g / samples; result->b += sum.b / samples; } } mi_vector_neg(&state->normal); mi_vector_neg(&state->normal_geom); } miASSERT(!isnan(result->r) && !isnan(result->g) && !isnan(result->b)); } /* * Glossy and specular paths are traced if there are non-zero * glossy and specular coefficients resp, and not rejected by Russiian roulette. * Diffuse reflection is computed from the photon map. */ static void indirect_illumination( miColor *result, /* returned illum color */ miState *state, /* ray tracer state */ struct dgs_material *m) /* textured material paras */ { miVector dir; miColor color; miScalar transp = m->transp; miScalar refl = 1 - m->transp; miScalar refl_spec = refl, refl_glos = refl; miScalar diff = m->diffuse.r + m->diffuse.g + m->diffuse.b; miScalar glos = m->glossy.r + m->glossy.g + m->glossy.b; miScalar spec = m->specular.r + m->specular.g + m->specular.b; miScalar total = diff + glos + spec; miScalar efftransp = 0; /* effective object transparancy */ *result = solid_black; if (transp > 0 && diff > 0) { mi_compute_irradiance_backside(&color, state); COLOR_ADD_PROD(result, &color, &m->diffuse, transp / M_PI); } if (refl > 0 && diff > 0) { mi_compute_irradiance(&color, state); COLOR_ADD_PROD(result, &color, &m->diffuse, refl / M_PI); } if (transp > 0 && spec > 0) { if (mi_transmission_dir_specular(&dir, state, state->ior_in, state->ior)) { if (mi_trace_refraction(&color, state, &dir)) COLOR_ADD_PROD(result, &color, &m->specular, transp); efftransp += spec / total * (1 - color.a); } else refl_spec = 1.0; /* full reflection */ } if (refl_spec > 0 && spec > 0) { mi_reflection_dir_specular(&dir, state); if (mi_trace_reflection(&color, state, &dir)) COLOR_ADD_PROD(result,&color, &m->specular, refl_spec); } /* glossy */ if (transp > 0 && glos > 0) { if (dgs_transmit_glossy_dir(state, &dir, m)) { /* not count area light sources here */ if (mi_trace_refraction(&color, state, &dir) && (state->pri || !mi_vector_norm(&state->normal))) COLOR_ADD_PROD(result, &color, &m->glossy, transp); efftransp += glos / total * (1 - color.a); } else refl_glos = 1.0; /* full reflection */ } if (refl_glos > 0 && glos > 0) { dgs_reflect_glossy_dir(state, &dir, m); /* not count area light sources here */ if (mi_trace_reflection(&color, state, &dir) && (state->pri || !mi_vector_norm(&state->normal))) COLOR_ADD_PROD(result, &color, &m->glossy, refl_glos); } result->a = 1 - (m->transp) * efftransp; miASSERT(!isnan(result->r) && !isnan(result->g) && !isnan(result->b)); } /* * Material shader: Lambert diffuse + Ward glossy + mirror specular */ int dgs_material_version(void) {return(2);} miBoolean dgs_material( miColor *result, miState *state, struct dgs_material *paras) { struct dgs_material m; /* work area for material */ miColor local, global; /* local and global illum */ miTag *light; /* tag of light instance */ /* Material shader called as a photon shader? Convenience only. */ if (miRAY_PHOTON(state->type)) return(dgs_material_photon(result, state, paras)); /* * Shadow ray: no illumination through object, transparent or not. * Instead, this type of illumination is handled by the photon map. */ if (state->type == miRAY_SHADOW || state->type == miRAY_DISPLACE ) { *result = transp_black; return(miTRUE); } dgs_set_parameters(state, &m, paras); /* evaluate light parameters */ m.i_light = *mi_eval_integer(¶s->i_light); m.n_light = *mi_eval_integer(¶s->n_light); light = mi_eval_tag (paras->light); dgs_refraction_index(state, &m); /* * Direct illumination from light sources. One migth think that it is * sufficient to only compute direct illum when not inside a transpar- * ent object, that is, when state->refraction_level is even. But that * fails when the light source is inside the same transparent object. */ direct_illumination(&local, state, &m, light); /* Indirect illumination. */ indirect_illumination(&global, state, &m); result->r = local.r + global.r; result->g = local.g + global.g; result->b = local.b + global.b; result->a = global.a; miASSERT(0 <= result->a && result->a <= 1); return(miTRUE); } /* * The dgs photon shader enforces energy-preserving scattering coefficients, * that is, the following three requirements: * diffuse.r + glossy.r + specular.r has to be <= 1, * diffuse.g + glossy.g + specular.g has to be <= 1, * diffuse.b + glossy.b + specular.b has to be <= 1. */ int dgs_material_photon_version(void) {return(2);} miBoolean dgs_material_photon( miColor *energy, miState *state, struct dgs_material *paras) { struct dgs_material m; miColor color; /* energy of photon after interaction */ miVector dir; miRay_type type; dgs_set_parameters(state, &m, paras); /* * Insert photon to map if this is a diffuse surface and not direct * illumination from a light source. DGS shoots no trans[arent photons, * but other materials may do so. */ if (m.diffuse.r > miEPS || m.diffuse.g > miEPS || m.diffuse.b > miEPS) { miState *parent = state; while(parent->type == miPHOTON_TRANSPARENT && parent->parent) { miASSERT(parent->parent != parent); parent = parent->parent; } if (parent->type != miPHOTON_LIGHT) mi_store_photon(energy, state); } /* compute iors */ dgs_refraction_index(state, &m); type = mi_choose_scatter_type(state, m.transp, &m.diffuse, &m.glossy, &m.specular); /* compute new photon color (compensating for Russian roulette) */ switch (type) { case miPHOTON_ABSORB: /* no reflection or transmission */ return(miTRUE); case miPHOTON_REFLECT_DIFFUSE: case miPHOTON_TRANSMIT_DIFFUSE: color.r = energy->r * m.diffuse.r; color.g = energy->g * m.diffuse.g; color.b = energy->b * m.diffuse.b; break; case miPHOTON_REFLECT_GLOSSY: case miPHOTON_TRANSMIT_GLOSSY: color.r = energy->r * m.glossy.r; color.g = energy->g * m.glossy.g; color.b = energy->b * m.glossy.b; break; case miPHOTON_REFLECT_SPECULAR: case miPHOTON_TRANSMIT_SPECULAR: color.r = energy->r * m.specular.r; color.g = energy->g * m.specular.g; color.b = energy->b * m.specular.b; break; default: /* Unknown scatter type */ mi_error("unknown scatter type in dgs photon shader"); return(miFALSE); } /* Shoot new photon in a direction determined by the scattering type */ switch (type) { case miPHOTON_TRANSMIT_DIFFUSE:/* diffuse transm. (translucency) */ mi_transmission_dir_diffuse(&dir, state); return(mi_photon_transmission_diffuse(&color, state, &dir)); case miPHOTON_REFLECT_DIFFUSE: /* diffuse reflection (Lambert) */ mi_reflection_dir_diffuse(&dir, state); return(mi_photon_reflection_diffuse(&color, state, &dir)); case miPHOTON_TRANSMIT_GLOSSY: /* glossy transmission (Ward model) */ if (dgs_transmit_glossy_dir(state, &dir, &m)) return(mi_photon_transmission_glossy(&color, state, &dir)); /* else: full reflection case, no break ! */ case miPHOTON_REFLECT_GLOSSY: /* glossy reflection (Ward model)*/ dgs_reflect_glossy_dir(state, &dir, &m); return(mi_photon_reflection_glossy(&color, state, &dir)); case miPHOTON_TRANSMIT_SPECULAR:/* specular transmission */ if (mi_transmission_dir_specular(&dir, state, state->ior_in, state->ior)) { return(mi_photon_transmission_specular(&color, state, &dir)); } /* full reflection case, no break !*/ case miPHOTON_REFLECT_SPECULAR: /* specular reflection (mirror) */ mi_reflection_dir_specular(&dir, state); return(mi_photon_reflection_specular(&color, state, &dir)); default: miASSERT(0); /* cannot be here */ return(miFALSE); } }