| 1 | greg | 1.1 | #ifndef lint | 
| 2 | greg | 2.15 | static const char       RCSid[] = "$Id$"; | 
| 3 | greg | 1.1 | #endif | 
| 4 |  |  | /* | 
| 5 |  |  | *  dielectric.c - shading function for transparent materials. | 
| 6 | greg | 2.15 | */ | 
| 7 |  |  |  | 
| 8 | greg | 2.16 | #include "copyright.h" | 
| 9 | greg | 1.1 |  | 
| 10 |  |  | #include  "ray.h" | 
| 11 |  |  |  | 
| 12 |  |  | #include  "otypes.h" | 
| 13 |  |  |  | 
| 14 |  |  | #ifdef  DISPERSE | 
| 15 |  |  | #include  "source.h" | 
| 16 | greg | 2.5 | static  disperse(); | 
| 17 | greg | 2.6 | static int  lambda(); | 
| 18 | greg | 1.1 | #endif | 
| 19 |  |  |  | 
| 20 |  |  | /* | 
| 21 |  |  | *     Explicit calculations for Fresnel's equation are performed, | 
| 22 |  |  | *  but only one square root computation is necessary. | 
| 23 |  |  | *     The index of refraction is given as a Hartmann equation | 
| 24 |  |  | *  with lambda0 equal to zero.  If the slope of Hartmann's | 
| 25 |  |  | *  equation is non-zero, the material disperses light upon | 
| 26 |  |  | *  refraction.  This condition is examined on rays traced to | 
| 27 |  |  | *  light sources.  If a ray is exiting a dielectric material, we | 
| 28 |  |  | *  check the sources to see if any would cause bright color to be | 
| 29 |  |  | *  directed to the viewer due to dispersion.  This gives colorful | 
| 30 |  |  | *  sparkle to crystals, etc.  (Only if DISPERSE is defined!) | 
| 31 |  |  | * | 
| 32 |  |  | *  Arguments for MAT_DIELECTRIC are: | 
| 33 |  |  | *      red     grn     blu     rndx    Hartmann | 
| 34 |  |  | * | 
| 35 |  |  | *  Arguments for MAT_INTERFACE are: | 
| 36 |  |  | *      red1    grn1    blu1    rndx1   red2    grn2    blu2    rndx2 | 
| 37 |  |  | * | 
| 38 |  |  | *  The primaries are material transmission per unit length. | 
| 39 |  |  | *  MAT_INTERFACE uses dielectric1 for inside and dielectric2 for | 
| 40 |  |  | *  outside. | 
| 41 |  |  | */ | 
| 42 |  |  |  | 
| 43 |  |  |  | 
| 44 |  |  | #define  MLAMBDA        500             /* mean lambda */ | 
| 45 |  |  | #define  MAXLAMBDA      779             /* maximum lambda */ | 
| 46 |  |  | #define  MINLAMBDA      380             /* minimum lambda */ | 
| 47 |  |  |  | 
| 48 |  |  | #define  MINCOS         0.997           /* minimum dot product for dispersion */ | 
| 49 |  |  |  | 
| 50 | greg | 2.9 |  | 
| 51 | greg | 2.10 | static double | 
| 52 |  |  | mylog(x)                /* special log for extinction coefficients */ | 
| 53 |  |  | double  x; | 
| 54 |  |  | { | 
| 55 |  |  | if (x < 1e-40) | 
| 56 |  |  | return(-100.); | 
| 57 |  |  | if (x >= 1.) | 
| 58 |  |  | return(0.); | 
| 59 |  |  | return(log(x)); | 
| 60 |  |  | } | 
| 61 |  |  |  | 
| 62 |  |  |  | 
| 63 | greg | 2.9 | m_dielectric(m, r)      /* color a ray which hit a dielectric interface */ | 
| 64 | greg | 1.1 | OBJREC  *m; | 
| 65 |  |  | register RAY  *r; | 
| 66 |  |  | { | 
| 67 |  |  | double  cos1, cos2, nratio; | 
| 68 | greg | 2.9 | COLOR  ctrans; | 
| 69 | greg | 2.11 | COLOR  talb; | 
| 70 | gwlarson | 2.14 | int  hastexture; | 
| 71 | greg | 1.5 | double  refl, trans; | 
| 72 | greg | 1.1 | FVECT  dnorm; | 
| 73 |  |  | double  d1, d2; | 
| 74 |  |  | RAY  p; | 
| 75 |  |  | register int  i; | 
| 76 |  |  |  | 
| 77 |  |  | if (m->oargs.nfargs != (m->otype==MAT_DIELECTRIC ? 5 : 8)) | 
| 78 |  |  | objerror(m, USER, "bad arguments"); | 
| 79 |  |  |  | 
| 80 |  |  | raytexture(r, m->omod);                 /* get modifiers */ | 
| 81 |  |  |  | 
| 82 | gwlarson | 2.14 | if (hastexture = DOT(r->pert,r->pert) > FTINY*FTINY) | 
| 83 |  |  | cos1 = raynormal(dnorm, r);     /* perturb normal */ | 
| 84 |  |  | else { | 
| 85 |  |  | VCOPY(dnorm, r->ron); | 
| 86 |  |  | cos1 = r->rod; | 
| 87 |  |  | } | 
| 88 | greg | 1.1 | /* index of refraction */ | 
| 89 |  |  | if (m->otype == MAT_DIELECTRIC) | 
| 90 |  |  | nratio = m->oargs.farg[3] + m->oargs.farg[4]/MLAMBDA; | 
| 91 |  |  | else | 
| 92 |  |  | nratio = m->oargs.farg[3] / m->oargs.farg[7]; | 
| 93 |  |  |  | 
| 94 |  |  | if (cos1 < 0.0) {                       /* inside */ | 
| 95 | gwlarson | 2.14 | hastexture = -hastexture; | 
| 96 | greg | 1.1 | cos1 = -cos1; | 
| 97 |  |  | dnorm[0] = -dnorm[0]; | 
| 98 |  |  | dnorm[1] = -dnorm[1]; | 
| 99 |  |  | dnorm[2] = -dnorm[2]; | 
| 100 | greg | 2.10 | setcolor(r->cext, -mylog(m->oargs.farg[0]*colval(r->pcol,RED)), | 
| 101 |  |  | -mylog(m->oargs.farg[1]*colval(r->pcol,GRN)), | 
| 102 |  |  | -mylog(m->oargs.farg[2]*colval(r->pcol,BLU))); | 
| 103 | greg | 2.11 | setcolor(r->albedo, 0., 0., 0.); | 
| 104 | greg | 2.9 | r->gecc = 0.; | 
| 105 |  |  | if (m->otype == MAT_INTERFACE) { | 
| 106 |  |  | setcolor(ctrans, | 
| 107 | greg | 2.10 | -mylog(m->oargs.farg[4]*colval(r->pcol,RED)), | 
| 108 |  |  | -mylog(m->oargs.farg[5]*colval(r->pcol,GRN)), | 
| 109 |  |  | -mylog(m->oargs.farg[6]*colval(r->pcol,BLU))); | 
| 110 | greg | 2.11 | setcolor(talb, 0., 0., 0.); | 
| 111 | greg | 2.9 | } else { | 
| 112 |  |  | copycolor(ctrans, cextinction); | 
| 113 | greg | 2.11 | copycolor(talb, salbedo); | 
| 114 | greg | 2.9 | } | 
| 115 | greg | 1.1 | } else {                                /* outside */ | 
| 116 |  |  | nratio = 1.0 / nratio; | 
| 117 | greg | 2.9 |  | 
| 118 | greg | 2.10 | setcolor(ctrans, -mylog(m->oargs.farg[0]*colval(r->pcol,RED)), | 
| 119 |  |  | -mylog(m->oargs.farg[1]*colval(r->pcol,GRN)), | 
| 120 |  |  | -mylog(m->oargs.farg[2]*colval(r->pcol,BLU))); | 
| 121 | greg | 2.11 | setcolor(talb, 0., 0., 0.); | 
| 122 | greg | 2.9 | if (m->otype == MAT_INTERFACE) { | 
| 123 |  |  | setcolor(r->cext, | 
| 124 | greg | 2.10 | -mylog(m->oargs.farg[4]*colval(r->pcol,RED)), | 
| 125 |  |  | -mylog(m->oargs.farg[5]*colval(r->pcol,GRN)), | 
| 126 |  |  | -mylog(m->oargs.farg[6]*colval(r->pcol,BLU))); | 
| 127 | greg | 2.11 | setcolor(r->albedo, 0., 0., 0.); | 
| 128 | greg | 2.9 | r->gecc = 0.; | 
| 129 |  |  | } | 
| 130 | greg | 1.1 | } | 
| 131 |  |  |  | 
| 132 |  |  | d2 = 1.0 - nratio*nratio*(1.0 - cos1*cos1);     /* compute cos theta2 */ | 
| 133 |  |  |  | 
| 134 |  |  | if (d2 < FTINY)                 /* total reflection */ | 
| 135 |  |  |  | 
| 136 |  |  | refl = 1.0; | 
| 137 |  |  |  | 
| 138 |  |  | else {                          /* refraction occurs */ | 
| 139 |  |  | /* compute Fresnel's equations */ | 
| 140 |  |  | cos2 = sqrt(d2); | 
| 141 |  |  | d1 = cos1; | 
| 142 |  |  | d2 = nratio*cos2; | 
| 143 |  |  | d1 = (d1 - d2) / (d1 + d2); | 
| 144 |  |  | refl = d1 * d1; | 
| 145 |  |  |  | 
| 146 |  |  | d1 = 1.0 / cos1; | 
| 147 |  |  | d2 = nratio / cos2; | 
| 148 |  |  | d1 = (d1 - d2) / (d1 + d2); | 
| 149 |  |  | refl += d1 * d1; | 
| 150 |  |  |  | 
| 151 | greg | 2.9 | refl *= 0.5; | 
| 152 | greg | 1.1 | trans = 1.0 - refl; | 
| 153 | greg | 2.15 |  | 
| 154 |  |  | trans *= nratio*nratio;         /* solid angle ratio */ | 
| 155 | greg | 1.1 |  | 
| 156 | gwlarson | 2.13 | if (rayorigin(&p, r, REFRACTED, trans) == 0) { | 
| 157 | greg | 1.1 |  | 
| 158 |  |  | /* compute refracted ray */ | 
| 159 |  |  | d1 = nratio*cos1 - cos2; | 
| 160 |  |  | for (i = 0; i < 3; i++) | 
| 161 |  |  | p.rdir[i] = nratio*r->rdir[i] + d1*dnorm[i]; | 
| 162 | gwlarson | 2.14 | /* accidental reflection? */ | 
| 163 |  |  | if (hastexture && | 
| 164 |  |  | DOT(p.rdir,r->ron)*hastexture >= -FTINY) { | 
| 165 |  |  | d1 *= (double)hastexture; | 
| 166 |  |  | for (i = 0; i < 3; i++) /* ignore texture */ | 
| 167 |  |  | p.rdir[i] = nratio*r->rdir[i] + | 
| 168 |  |  | d1*r->ron[i]; | 
| 169 |  |  | normalize(p.rdir);      /* not exact */ | 
| 170 |  |  | } | 
| 171 | greg | 1.1 | #ifdef  DISPERSE | 
| 172 |  |  | if (m->otype != MAT_DIELECTRIC | 
| 173 |  |  | || r->rod > 0.0 | 
| 174 |  |  | || r->crtype & SHADOW | 
| 175 | greg | 2.3 | || !directvis | 
| 176 | greg | 1.1 | || m->oargs.farg[4] == 0.0 | 
| 177 | greg | 2.12 | || !disperse(m, r, p.rdir, | 
| 178 |  |  | trans, ctrans, talb)) | 
| 179 | greg | 1.1 | #endif | 
| 180 |  |  | { | 
| 181 | greg | 2.9 | copycolor(p.cext, ctrans); | 
| 182 | greg | 2.11 | copycolor(p.albedo, talb); | 
| 183 | greg | 1.1 | rayvalue(&p); | 
| 184 |  |  | scalecolor(p.rcol, trans); | 
| 185 |  |  | addcolor(r->rcol, p.rcol); | 
| 186 | greg | 2.4 | if (nratio >= 1.0-FTINY && nratio <= 1.0+FTINY) | 
| 187 |  |  | r->rt = r->rot + p.rt; | 
| 188 | greg | 1.1 | } | 
| 189 |  |  | } | 
| 190 |  |  | } | 
| 191 |  |  |  | 
| 192 |  |  | if (!(r->crtype & SHADOW) && | 
| 193 | gwlarson | 2.13 | rayorigin(&p, r, REFLECTED, refl) == 0) { | 
| 194 | greg | 1.1 |  | 
| 195 |  |  | /* compute reflected ray */ | 
| 196 |  |  | for (i = 0; i < 3; i++) | 
| 197 |  |  | p.rdir[i] = r->rdir[i] + 2.0*cos1*dnorm[i]; | 
| 198 | gwlarson | 2.14 | /* accidental penetration? */ | 
| 199 |  |  | if (hastexture && DOT(p.rdir,r->ron)*hastexture <= FTINY) | 
| 200 |  |  | for (i = 0; i < 3; i++)         /* ignore texture */ | 
| 201 |  |  | p.rdir[i] = r->rdir[i] + 2.0*r->rod*r->ron[i]; | 
| 202 | greg | 1.1 |  | 
| 203 |  |  | rayvalue(&p);                   /* reflected ray value */ | 
| 204 |  |  |  | 
| 205 |  |  | scalecolor(p.rcol, refl);       /* color contribution */ | 
| 206 |  |  | addcolor(r->rcol, p.rcol); | 
| 207 |  |  | } | 
| 208 | greg | 2.9 | /* rayvalue() computes absorption */ | 
| 209 | greg | 2.7 | return(1); | 
| 210 | greg | 1.1 | } | 
| 211 |  |  |  | 
| 212 |  |  |  | 
| 213 |  |  | #ifdef  DISPERSE | 
| 214 |  |  |  | 
| 215 |  |  | static | 
| 216 | greg | 2.12 | disperse(m, r, vt, tr, cet, abt)  /* check light sources for dispersion */ | 
| 217 | greg | 1.1 | OBJREC  *m; | 
| 218 |  |  | RAY  *r; | 
| 219 |  |  | FVECT  vt; | 
| 220 |  |  | double  tr; | 
| 221 | greg | 2.12 | COLOR  cet, abt; | 
| 222 | greg | 1.1 | { | 
| 223 |  |  | RAY  sray, *entray; | 
| 224 |  |  | FVECT  v1, v2, n1, n2; | 
| 225 |  |  | FVECT  dv, v2Xdv; | 
| 226 |  |  | double  v2Xdvv2Xdv; | 
| 227 | greg | 1.7 | int  success = 0; | 
| 228 |  |  | SRCINDEX  si; | 
| 229 | greg | 1.1 | FVECT  vtmp1, vtmp2; | 
| 230 |  |  | double  dtmp1, dtmp2; | 
| 231 |  |  | int  l1, l2; | 
| 232 |  |  | COLOR  ctmp; | 
| 233 |  |  | int  i; | 
| 234 |  |  |  | 
| 235 |  |  | /* | 
| 236 |  |  | *     This routine computes dispersion to the first order using | 
| 237 |  |  | *  the following assumptions: | 
| 238 |  |  | * | 
| 239 |  |  | *      1) The dependency of the index of refraction on wavelength | 
| 240 |  |  | *              is approximated by Hartmann's equation with lambda0 | 
| 241 |  |  | *              equal to zero. | 
| 242 |  |  | *      2) The entry and exit locations are constant with respect | 
| 243 |  |  | *              to dispersion. | 
| 244 |  |  | * | 
| 245 |  |  | *     The second assumption permits us to model dispersion without | 
| 246 |  |  | *  having to sample refracted directions.  We assume that the | 
| 247 |  |  | *  geometry inside the material is constant, and concern ourselves | 
| 248 |  |  | *  only with the relationship between the entering and exiting ray. | 
| 249 |  |  | *  We compute the first derivatives of the entering and exiting | 
| 250 |  |  | *  refraction with respect to the index of refraction.  This | 
| 251 |  |  | *  is then used in a first order Taylor series to determine the | 
| 252 |  |  | *  index of refraction necessary to send the exiting ray to each | 
| 253 |  |  | *  light source. | 
| 254 |  |  | *     If an exiting ray hits a light source within the refraction | 
| 255 |  |  | *  boundaries, we sum all the frequencies over the disc of the | 
| 256 |  |  | *  light source to determine the resulting color.  A smaller light | 
| 257 |  |  | *  source will therefore exhibit a sharper spectrum. | 
| 258 |  |  | */ | 
| 259 |  |  |  | 
| 260 |  |  | if (!(r->crtype & REFRACTED)) {         /* ray started in material */ | 
| 261 |  |  | VCOPY(v1, r->rdir); | 
| 262 |  |  | n1[0] = -r->rdir[0]; n1[1] = -r->rdir[1]; n1[2] = -r->rdir[2]; | 
| 263 |  |  | } else { | 
| 264 |  |  | /* find entry point */ | 
| 265 |  |  | for (entray = r; entray->rtype != REFRACTED; | 
| 266 |  |  | entray = entray->parent) | 
| 267 |  |  | ; | 
| 268 |  |  | entray = entray->parent; | 
| 269 |  |  | if (entray->crtype & REFRACTED)         /* too difficult */ | 
| 270 |  |  | return(0); | 
| 271 |  |  | VCOPY(v1, entray->rdir); | 
| 272 |  |  | VCOPY(n1, entray->ron); | 
| 273 |  |  | } | 
| 274 |  |  | VCOPY(v2, vt);                  /* exiting ray */ | 
| 275 |  |  | VCOPY(n2, r->ron); | 
| 276 |  |  |  | 
| 277 |  |  | /* first order dispersion approx. */ | 
| 278 |  |  | dtmp1 = DOT(n1, v1); | 
| 279 |  |  | dtmp2 = DOT(n2, v2); | 
| 280 |  |  | for (i = 0; i < 3; i++) | 
| 281 |  |  | dv[i] = v1[i] + v2[i] - n1[i]/dtmp1 - n2[i]/dtmp2; | 
| 282 |  |  |  | 
| 283 |  |  | if (DOT(dv, dv) <= FTINY)       /* null effect */ | 
| 284 |  |  | return(0); | 
| 285 |  |  | /* compute plane normal */ | 
| 286 |  |  | fcross(v2Xdv, v2, dv); | 
| 287 |  |  | v2Xdvv2Xdv = DOT(v2Xdv, v2Xdv); | 
| 288 |  |  |  | 
| 289 |  |  | /* check sources */ | 
| 290 | greg | 1.7 | initsrcindex(&si); | 
| 291 |  |  | while (srcray(&sray, r, &si)) { | 
| 292 | greg | 1.1 |  | 
| 293 | greg | 1.7 | if (DOT(sray.rdir, v2) < MINCOS) | 
| 294 | greg | 1.1 | continue;                       /* bad source */ | 
| 295 |  |  | /* adjust source ray */ | 
| 296 |  |  |  | 
| 297 |  |  | dtmp1 = DOT(v2Xdv, sray.rdir) / v2Xdvv2Xdv; | 
| 298 |  |  | sray.rdir[0] -= dtmp1 * v2Xdv[0]; | 
| 299 |  |  | sray.rdir[1] -= dtmp1 * v2Xdv[1]; | 
| 300 |  |  | sray.rdir[2] -= dtmp1 * v2Xdv[2]; | 
| 301 |  |  |  | 
| 302 |  |  | l1 = lambda(m, v2, dv, sray.rdir);      /* mean lambda */ | 
| 303 |  |  |  | 
| 304 |  |  | if (l1 > MAXLAMBDA || l1 < MINLAMBDA)   /* not visible */ | 
| 305 |  |  | continue; | 
| 306 |  |  | /* trace source ray */ | 
| 307 | greg | 2.12 | copycolor(sray.cext, cet); | 
| 308 |  |  | copycolor(sray.albedo, abt); | 
| 309 | greg | 1.1 | normalize(sray.rdir); | 
| 310 |  |  | rayvalue(&sray); | 
| 311 | greg | 1.2 | if (bright(sray.rcol) <= FTINY) /* missed it */ | 
| 312 | greg | 1.1 | continue; | 
| 313 |  |  |  | 
| 314 |  |  | /* | 
| 315 |  |  | *      Compute spectral sum over diameter of source. | 
| 316 |  |  | *  First find directions for rays going to opposite | 
| 317 |  |  | *  sides of source, then compute wavelengths for each. | 
| 318 |  |  | */ | 
| 319 |  |  |  | 
| 320 |  |  | fcross(vtmp1, v2Xdv, sray.rdir); | 
| 321 | greg | 1.7 | dtmp1 = sqrt(si.dom  / v2Xdvv2Xdv / PI); | 
| 322 | greg | 1.1 |  | 
| 323 |  |  | /* compute first ray */ | 
| 324 |  |  | for (i = 0; i < 3; i++) | 
| 325 |  |  | vtmp2[i] = sray.rdir[i] + dtmp1*vtmp1[i]; | 
| 326 |  |  |  | 
| 327 |  |  | l1 = lambda(m, v2, dv, vtmp2);          /* first lambda */ | 
| 328 |  |  | if (l1 < 0) | 
| 329 |  |  | continue; | 
| 330 |  |  | /* compute second ray */ | 
| 331 |  |  | for (i = 0; i < 3; i++) | 
| 332 |  |  | vtmp2[i] = sray.rdir[i] - dtmp1*vtmp1[i]; | 
| 333 |  |  |  | 
| 334 |  |  | l2 = lambda(m, v2, dv, vtmp2);          /* second lambda */ | 
| 335 |  |  | if (l2 < 0) | 
| 336 |  |  | continue; | 
| 337 |  |  | /* compute color from spectrum */ | 
| 338 |  |  | if (l1 < l2) | 
| 339 |  |  | spec_rgb(ctmp, l1, l2); | 
| 340 |  |  | else | 
| 341 |  |  | spec_rgb(ctmp, l2, l1); | 
| 342 |  |  | multcolor(ctmp, sray.rcol); | 
| 343 |  |  | scalecolor(ctmp, tr); | 
| 344 |  |  | addcolor(r->rcol, ctmp); | 
| 345 |  |  | success++; | 
| 346 |  |  | } | 
| 347 |  |  | return(success); | 
| 348 |  |  | } | 
| 349 |  |  |  | 
| 350 |  |  |  | 
| 351 |  |  | static int | 
| 352 |  |  | lambda(m, v2, dv, lr)                   /* compute lambda for material */ | 
| 353 |  |  | register OBJREC  *m; | 
| 354 |  |  | FVECT  v2, dv, lr; | 
| 355 |  |  | { | 
| 356 |  |  | FVECT  lrXdv, v2Xlr; | 
| 357 |  |  | double  dtmp, denom; | 
| 358 |  |  | int  i; | 
| 359 |  |  |  | 
| 360 |  |  | fcross(lrXdv, lr, dv); | 
| 361 |  |  | for (i = 0; i < 3; i++) | 
| 362 |  |  | if (lrXdv[i] > FTINY || lrXdv[i] < -FTINY) | 
| 363 |  |  | break; | 
| 364 |  |  | if (i >= 3) | 
| 365 |  |  | return(-1); | 
| 366 |  |  |  | 
| 367 |  |  | fcross(v2Xlr, v2, lr); | 
| 368 |  |  |  | 
| 369 |  |  | dtmp = m->oargs.farg[4] / MLAMBDA; | 
| 370 |  |  | denom = dtmp + v2Xlr[i]/lrXdv[i] * (m->oargs.farg[3] + dtmp); | 
| 371 |  |  |  | 
| 372 |  |  | if (denom < FTINY) | 
| 373 |  |  | return(-1); | 
| 374 |  |  |  | 
| 375 |  |  | return(m->oargs.farg[4] / denom); | 
| 376 |  |  | } | 
| 377 |  |  |  | 
| 378 |  |  | #endif  /* DISPERSE */ |