ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/m_wgmdf.c
Revision: 2.5
Committed: Tue Dec 17 20:03:13 2024 UTC (4 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.4: +40 -13 lines
Log Message:
feat: Added setting of RdotP, NxP, NyP, and NzP variables

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: m_wgmdf.c,v 2.4 2024/12/11 18:32:26 greg Exp $";
3 #endif
4 /*
5 * Shading function for programmable Ward-Geisler-Moroder-Duer material.
6 */
7
8 #include "copyright.h"
9
10 #include "ray.h"
11 #include "ambient.h"
12 #include "otypes.h"
13 #include "rtotypes.h"
14 #include "source.h"
15 #include "func.h"
16 #include "random.h"
17 #include "pmapmat.h"
18
19 #ifndef MAXITER
20 #define MAXITER 10 /* maximum # specular ray attempts */
21 #endif
22 /* estimate of Fresnel function */
23 #define FRESNE(ci) (exp(-5.85*(ci)) - 0.00202943064)
24 #define FRESTHRESH 0.017999 /* minimum specularity for approx. */
25
26 /*
27 * This routine implements the anisotropic Gaussian
28 * model described by Ward in a 1992 Siggraph article and updated by
29 * Geisler-Moroder and Duer in a 2010 article in High Performance Graphics.
30 * We do not reorient incoming ray, using side in part to determine
31 * reflectance values. Most parameters are programmable with their own
32 * modifiers and/or value expressions.
33 *
34 * Arguments for MAT_WGMDF are:
35 * 13+ rs_mod rs rs_urough rs_vrough
36 * ts_mod ts ts_urough ts_vrough
37 * td_mod
38 * ux uy uz funcfile transform
39 * 0
40 * 9+ rfdif gfdif bfdif
41 * rbdif gbdif bbdif
42 * rtdif gtdif btdif
43 * A10 ..
44 *
45 * Where the rs_urough or rs_vrough expression yields zero, mirror-Fresnel
46 * effects are computed, similar to MAT_PLASTIC and MAT_METAL. The
47 * rs* expressions should not vary with incident angle, or the material
48 * will not be physically valid. Similarly, the ts* expressions should
49 * give the same value for coincident direction vectors from either side.
50 * There are independent modifiers for specular reflection,
51 * transmission, and diffuse transmission. Diffuse reflection
52 * applies the material's main modifier, which doesn't apply to
53 * anything else by default. However, any of the modifiers may be
54 * ALIASMOD, which will use the main material modifier, or VOIDID,
55 * which will just be white.
56 * Diffuse reflection and transmission colors and patterns add to
57 * the specular components, and are only adjusted with mirror-Fresnel
58 * reflection if specular reflection is greater than FRESHTHRESH. The
59 * specular transmission is likewise adjusted in such cases. Specified
60 * values for all components should sum to less than 1, but like other
61 * Radiance materials, this is not enforced, nor is a warning issued.
62 */
63 /* specularity flags */
64 #define SP_REFL 01 /* has reflected specular component */
65 #define SP_TRAN 02 /* has transmitted specular */
66 #define SP_RPURE 04 /* mirror reflection */
67 #define SP_TPURE 010 /* has view component */
68 #define SP_FLAT 020 /* flat reflecting surface */
69 #define SP_RBLT 040 /* reflection below sample threshold */
70 #define SP_TBLT 0100 /* transmission below threshold */
71
72 typedef struct {
73 char *nam; /* modifier name */
74 int hastexture; /* has a texture? */
75 FVECT pnorm; /* perturbed normal direction */
76 double pdot; /* perturbed dot product */
77 SCOLOR pcol; /* pattern color */
78 } MODVAL; /* modifier-derived values */
79
80 typedef struct {
81 MODVAL mo; /* modifier parameters */
82 SCOLOR scol; /* modified diffuse color */
83 } DCOMP; /* diffuse component parameters */
84
85 typedef struct {
86 MODVAL mo; /* modifier parameters */
87 SCOLOR scol; /* modified specular color */
88 FVECT u, v; /* u and v in-plane vectors */
89 double u_alpha; /* u roughness */
90 double v_alpha; /* v roughness */
91 } SCOMP; /* specular component parameters */
92
93 typedef struct {
94 RAY *rp; /* ray pointer */
95 OBJREC *mtp; /* material pointer */
96 MFUNC *mf; /* pointer to expression list */
97 int specfl; /* specularity flags, defined above */
98 FVECT ulocal; /* u-vector in local coordinates */
99 DCOMP rd, td; /* diffuse component params */
100 SCOMP rs, ts; /* specular component params */
101 FVECT prdir; /* vector in transmitted direction */
102 } WGMDDAT; /* WGMD material data */
103
104 #define clr_comps(wp) ((wp)->specfl = 0, \
105 (wp)->rd.mo.nam = (wp)->td.mo.nam = \
106 (wp)->rs.mo.nam = (wp)->ts.mo.nam = "")
107
108 /* assign modifier values */
109 static int
110 set_modval(MODVAL *mp, OBJECT omod, const RAY *r)
111 {
112 RAY tr;
113
114 if (!mp->nam[0])
115 mp->nam = (omod == OVOID) ? VOIDID : objptr(omod)->oname;
116 else if (!strcmp(mp->nam, VOIDID))
117 omod = OVOID;
118 else if (omod == OVOID)
119 return(0);
120 tr = *r; /* independent modifier */
121 raytexture(&tr, omod);
122 if (DOT(tr.pert,tr.pert) > FTINY*FTINY) {
123 mp->pdot = raynormal(mp->pnorm, &tr);
124 mp->hastexture = 1;
125 } else {
126 VCOPY(mp->pnorm, tr.ron);
127 mp->pdot = tr.rod;
128 mp->hastexture = 0;
129 }
130 copyscolor(mp->pcol, tr.pcol);
131 return(1);
132 }
133
134 /* fill modifier values, using previous setting if found */
135 static int
136 fill_modval(MODVAL *mp, const WGMDDAT *wp)
137 {
138 if (mp == &wp->rd.mo) { /* special case (should be first) */
139 set_modval(mp, wp->mtp->omod, wp->rp);
140 return(1);
141 } /* use main modifier? */
142 if (!strcmp(mp->nam, ALIASMOD) || !strcmp(mp->nam, wp->rd.mo.nam)) {
143 *mp = wp->rd.mo;
144 return(1);
145 } /* check others */
146 if (mp != &wp->td.mo && !strcmp(mp->nam, wp->td.mo.nam)) {
147 *mp = wp->td.mo;
148 return(1);
149 }
150 if (mp != &wp->rs.mo && !strcmp(mp->nam, wp->rs.mo.nam)) {
151 *mp = wp->rs.mo;
152 return(1);
153 }
154 if (mp != &wp->ts.mo && !strcmp(mp->nam, wp->ts.mo.nam)) {
155 *mp = wp->ts.mo;
156 return(1);
157 } /* new modifier */
158 return(set_modval(mp, lastmod(objndx(wp->mtp), mp->nam), wp->rp));
159 }
160
161 static int
162 setWGMDfunc(MODVAL *mp, const WGMDDAT *wp)
163 {
164 static char lastMod[MAXSTR] = "";
165 double sf;
166 FVECT vec;
167
168 if (setfunc(wp->mtp, wp->rp) == 0 &&
169 !strcmp(mp->nam, lastMod))
170 return(0); /* already set */
171 /* else (re)assign special variables */
172 strcpy(lastMod, mp->nam);
173 sf = (wp->rp->rod > 0) ? 1. : -1.;
174 varset("RdotP`", '=', (-1. < mp->pdot) & (mp->pdot < 1.)
175 ? sf*mp->pdot : 1.);
176 sf /= funcxf.sca;
177 multv3(vec, mp->pnorm, funcxf.xfm);
178 varset("NxP`", '=', vec[0]*sf);
179 varset("NyP`", '=', vec[1]*sf);
180 varset("NzP`", '=', vec[2]*sf);
181 return(1);
182 }
183
184 /* assign indicated diffuse component (do !trans first) */
185 static void
186 set_dcomp(WGMDDAT *wp, int trans)
187 {
188 DCOMP *dp = trans ? &wp->td : &wp->rd;
189 const int offs = trans ? 6 : 3*(wp->rp->rod < 0);
190
191 if (trans) { /* transmitted diffuse? */
192 if (intens(wp->mtp->oargs.farg+offs) <= FTINY) {
193 scolorblack(dp->scol);
194 return;
195 }
196 dp->mo.nam = wp->mtp->oargs.sarg[8];
197 if (!fill_modval(&dp->mo, wp)) {
198 sprintf(errmsg,
199 "unknown diffuse transmission modifier '%s'",
200 dp->mo.nam);
201 objerror(wp->mtp, USER, errmsg);
202 }
203 } else /* no priors for main mod */
204 fill_modval(&dp->mo, wp);
205
206 setscolor(dp->scol, wp->mtp->oargs.farg[offs],
207 wp->mtp->oargs.farg[offs+1],
208 wp->mtp->oargs.farg[offs+2]);
209 smultscolor(dp->scol, dp->mo.pcol);
210 }
211
212 /* assign indicated specular component */
213 static void
214 set_scomp(WGMDDAT *wp, int trans)
215 {
216 SCOMP *sp = trans ? &wp->ts : &wp->rs;
217 const int eoff = 3*(trans != 0);
218 double coef;
219 /* constant zero check */
220 if (wp->mf->ep[eoff]->type == NUM &&
221 wp->mf->ep[eoff]->v.num <= FTINY) {
222 scolorblack(sp->scol);
223 return;
224 } /* need modifier */
225 sp->mo.nam = wp->mtp->oargs.sarg[4*(trans != 0)];
226 if (!fill_modval(&sp->mo, wp)) {
227 sprintf(errmsg, "unknown specular %s modifier '%s'",
228 trans ? "transmission" : "reflection", sp->mo.nam);
229 objerror(wp->mtp, USER, errmsg);
230 }
231 setWGMDfunc(&sp->mo, wp);
232 errno = 0;
233 coef = evalue(wp->mf->ep[eoff]);
234 if ((errno == EDOM) | (errno == ERANGE)) {
235 objerror(wp->mtp, WARNING, "specular compute error");
236 scolorblack(sp->scol);
237 return;
238 }
239 if (coef <= FTINY) { /* negligible value? */
240 scolorblack(sp->scol);
241 return;
242 }
243 copyscolor(sp->scol, sp->mo.pcol);
244 scalescolor(sp->scol, coef);
245 if (sintens(sp->scol) <= FTINY) {
246 scolorblack(sp->scol);
247 return; /* got black pattern */
248 }
249 errno = 0; /* else get roughness */
250 sp->u_alpha = evalue(wp->mf->ep[eoff+1]);
251 sp->v_alpha = (sp->u_alpha > FTINY) ? evalue(wp->mf->ep[eoff+2]) : 0.0;
252 if ((errno == EDOM) | (errno == ERANGE)) {
253 objerror(wp->mtp, WARNING, "roughness compute error");
254 scolorblack(sp->scol);
255 return;
256 } /* we have something... */
257 wp->specfl |= trans ? SP_TRAN : SP_REFL;
258 if (sp->v_alpha <= FTINY) { /* is it pure specular? */
259 wp->specfl |= trans ? SP_TPURE : SP_RPURE;
260 sp->u_alpha = sp->v_alpha = 0.0;
261 return;
262 }
263 /* get anisotropic coordinates */
264 fcross(sp->v, sp->mo.pnorm, wp->ulocal);
265 if (normalize(sp->v) == 0.0) { /* orientation vector==normal? */
266 if (fabs(sp->u_alpha - sp->v_alpha) > 0.001)
267 objerror(wp->mtp, WARNING, "bad orientation vector");
268 getperpendicular(sp->u, sp->mo.pnorm, 1); /* punting */
269 fcross(sp->v, sp->mo.pnorm, sp->u);
270 sp->u_alpha = sp->v_alpha = sqrt( 0.5 *
271 (sp->u_alpha*sp->u_alpha + sp->v_alpha*sp->v_alpha) );
272 } else
273 fcross(sp->u, sp->v, sp->mo.pnorm);
274 }
275
276 /* sample anisotropic Gaussian specular */
277 static void
278 agaussamp(WGMDDAT *wp)
279 {
280 RAY sr;
281 FVECT h;
282 double rv[2];
283 double d, sinp, cosp;
284 int maxiter, ntrials, nstarget, nstaken;
285 int i;
286 /* compute reflection */
287 if ((wp->specfl & (SP_REFL|SP_RPURE|SP_RBLT)) == SP_REFL &&
288 rayorigin(&sr, RSPECULAR, wp->rp, wp->rs.scol) == 0) {
289 SCOLOR scol;
290 nstarget = 1;
291 if (specjitter > 1.5) { /* multiple samples? */
292 nstarget = specjitter*wp->rp->rweight + .5;
293 if (sr.rweight <= minweight*nstarget)
294 nstarget = sr.rweight/minweight;
295 if (nstarget > 1) {
296 d = 1./nstarget;
297 scalescolor(sr.rcoef, d);
298 sr.rweight *= d;
299 } else
300 nstarget = 1;
301 }
302 scolorblack(scol);
303 dimlist[ndims++] = (int)(size_t)wp->mtp;
304 maxiter = MAXITER*nstarget;
305 for (nstaken = ntrials = 0; (nstaken < nstarget) &
306 (ntrials < maxiter); ntrials++) {
307 if (ntrials)
308 d = frandom();
309 else
310 d = urand(ilhash(dimlist,ndims)+samplendx);
311 multisamp(rv, 2, d);
312 d = 2.0*PI * rv[0];
313 cosp = tcos(d) * wp->rs.u_alpha;
314 sinp = tsin(d) * wp->rs.v_alpha;
315 d = 1./sqrt(cosp*cosp + sinp*sinp);
316 cosp *= d;
317 sinp *= d;
318 if ((0. <= specjitter) & (specjitter < 1.))
319 rv[1] = 1.0 - specjitter*rv[1];
320 d = (rv[1] <= FTINY) ? 1.0 : sqrt( -log(rv[1]) /
321 (cosp*cosp/(wp->rs.u_alpha*wp->rs.u_alpha) +
322 sinp*sinp/(wp->rs.v_alpha*wp->rs.v_alpha)) );
323 for (i = 0; i < 3; i++)
324 h[i] = wp->rs.mo.pnorm[i] +
325 d*(cosp*wp->rs.u[i] + sinp*wp->rs.v[i]);
326 d = -2.0 * DOT(h, wp->rp->rdir) / (1.0 + d*d);
327 VSUM(sr.rdir, wp->rp->rdir, h, d);
328 /* sample rejection test */
329 d = DOT(sr.rdir, wp->rp->ron);
330 if ((d > 0) ^ (wp->rp->rod > 0))
331 continue;
332 checknorm(sr.rdir);
333 if (nstarget > 1) { /* W-G-M-D adjustment */
334 if (nstaken) rayclear(&sr);
335 rayvalue(&sr);
336 d = 2./(1. + wp->rp->rod/d);
337 scalescolor(sr.rcol, d);
338 saddscolor(scol, sr.rcol);
339 } else {
340 rayvalue(&sr);
341 smultscolor(sr.rcol, sr.rcoef);
342 saddscolor(wp->rp->rcol, sr.rcol);
343 }
344 ++nstaken;
345 }
346 if (nstarget > 1) { /* final W-G-M-D weighting */
347 smultscolor(scol, sr.rcoef);
348 d = (double)nstarget/ntrials;
349 scalescolor(scol, d);
350 saddscolor(wp->rp->rcol, scol);
351 }
352 ndims--;
353 }
354 /* compute transmission */
355 if ((wp->specfl & (SP_TRAN|SP_TPURE|SP_TBLT)) == SP_TRAN &&
356 rayorigin(&sr, TSPECULAR, wp->rp, wp->ts.scol) == 0) {
357 nstarget = 1;
358 if (specjitter > 1.5) { /* multiple samples? */
359 nstarget = specjitter*wp->rp->rweight + .5;
360 if (sr.rweight <= minweight*nstarget)
361 nstarget = sr.rweight/minweight;
362 if (nstarget > 1) {
363 d = 1./nstarget;
364 scalescolor(sr.rcoef, d);
365 sr.rweight *= d;
366 } else
367 nstarget = 1;
368 }
369 dimlist[ndims++] = (int)(size_t)wp->mtp;
370 maxiter = MAXITER*nstarget;
371 for (nstaken = ntrials = 0; (nstaken < nstarget) &
372 (ntrials < maxiter); ntrials++) {
373 if (ntrials)
374 d = frandom();
375 else
376 d = urand(ilhash(dimlist,ndims)+1823+samplendx);
377 multisamp(rv, 2, d);
378 d = 2.0*PI * rv[0];
379 cosp = tcos(d) * wp->ts.u_alpha;
380 sinp = tsin(d) * wp->ts.v_alpha;
381 d = 1./sqrt(cosp*cosp + sinp*sinp);
382 cosp *= d;
383 sinp *= d;
384 if ((0. <= specjitter) & (specjitter < 1.))
385 rv[1] = 1.0 - specjitter*rv[1];
386 if (rv[1] <= FTINY)
387 d = 1.0;
388 else
389 d = sqrt(-log(rv[1]) /
390 (cosp*cosp/(wp->ts.u_alpha*wp->ts.u_alpha) +
391 sinp*sinp/(wp->ts.v_alpha*wp->ts.v_alpha)));
392 for (i = 0; i < 3; i++)
393 sr.rdir[i] = wp->prdir[i] +
394 d*(cosp*wp->ts.u[i] + sinp*wp->ts.v[i]);
395 /* rejection test */
396 if ((DOT(sr.rdir,wp->rp->ron) > 0) == (wp->rp->rod > 0))
397 continue;
398 normalize(sr.rdir); /* OK, normalize */
399 if (nstaken) /* multi-sampling? */
400 rayclear(&sr);
401 rayvalue(&sr);
402 smultscolor(sr.rcol, sr.rcoef);
403 saddscolor(wp->rp->rcol, sr.rcol);
404 ++nstaken;
405 }
406 ndims--;
407 }
408 }
409
410 /* compute source contribution for MAT_WGMDF */
411 static void
412 dirwgmdf(SCOLOR scval, void *uwp, FVECT ldir, double omega)
413 {
414 WGMDDAT *wp = (WGMDDAT *)uwp;
415 const int hitfront = (wp->rp->rod > 0);
416 double fresadj = 1.;
417 double ldot;
418 double dtmp, dtmp1, dtmp2;
419 FVECT h;
420 double au2, av2;
421 SCOLOR sctmp;
422
423 scolorblack(scval); /* will add component coefficients */
424
425 /* XXX ignores which side is lit */
426 if (wp->specfl & SP_RPURE && pbright(wp->rs.scol) >= FRESTHRESH)
427 fresadj = 1. - FRESNE(fabs(DOT(wp->rs.mo.pnorm,ldir)));
428
429 if (sintens(wp->rd.scol) > FTINY &&
430 ((ldot = DOT(wp->rd.mo.pnorm,ldir)) > 0) == hitfront) {
431 /*
432 * Compute diffuse reflection coefficient for source.
433 */
434 copyscolor(sctmp, wp->rd.scol);
435 dtmp = fabs(ldot) * omega * (1.0/PI) * fresadj;
436 scalescolor(sctmp, dtmp);
437 saddscolor(scval, sctmp);
438 }
439 if (sintens(wp->td.scol) > FTINY &&
440 ((ldot = DOT(wp->td.mo.pnorm,ldir)) > 0) ^ hitfront) {
441 /*
442 * Compute diffuse transmission coefficient for source.
443 */
444 copyscolor(sctmp, wp->td.scol);
445 dtmp = fabs(ldot) * omega * (1.0/PI) * fresadj;
446 scalescolor(sctmp, dtmp);
447 saddscolor(scval, sctmp);
448 }
449 #if 0 /* XXX not yet implemented */
450 if (ambRayInPmap(wp->rp))
451 return; /* specular accounted for in photon map */
452 #endif
453 if ((wp->specfl & (SP_REFL|SP_RPURE)) == SP_REFL &&
454 ((ldot = DOT(wp->rs.mo.pnorm,ldir)) > 0) == hitfront) {
455 /*
456 * Compute specular reflection coefficient for source using
457 * anisotropic Gaussian distribution model.
458 */
459 /* add source width if flat */
460 if (wp->specfl & SP_FLAT)
461 au2 = av2 = omega * (0.25/PI);
462 else
463 au2 = av2 = 0.0;
464 au2 += wp->rs.u_alpha*wp->rs.u_alpha;
465 av2 += wp->rs.v_alpha*wp->rs.v_alpha;
466 /* half vector */
467 VSUB(h, ldir, wp->rp->rdir);
468 /* ellipse */
469 dtmp1 = DOT(wp->rs.u, h);
470 dtmp1 *= dtmp1 / au2;
471 dtmp2 = DOT(wp->rs.v, h);
472 dtmp2 *= dtmp2 / av2;
473 /* W-G-M-D model */
474 dtmp = DOT(wp->rs.mo.pnorm, h);
475 dtmp *= dtmp;
476 dtmp1 = (dtmp1 + dtmp2) / dtmp;
477 dtmp = exp(-dtmp1) * DOT(h,h) /
478 (PI * dtmp*dtmp * sqrt(au2*av2));
479
480 if (dtmp > FTINY) { /* worth using? */
481 copyscolor(sctmp, wp->rs.scol);
482 dtmp *= fabs(ldot) * omega;
483 scalescolor(sctmp, dtmp);
484 saddscolor(scval, sctmp);
485 }
486 }
487 if ((wp->specfl & (SP_TRAN|SP_TPURE)) == SP_TRAN &&
488 ((ldot = DOT(wp->ts.mo.pnorm,ldir)) > 0) ^ hitfront) {
489 /*
490 * Compute specular transmission coefficient for source.
491 */
492 /* roughness + source */
493 au2 = av2 = omega * (1.0/PI);
494 au2 += wp->ts.u_alpha*wp->ts.u_alpha;
495 av2 += wp->ts.v_alpha*wp->ts.v_alpha;
496 /* "half vector" */
497 VSUB(h, ldir, wp->prdir);
498 dtmp = DOT(h,h);
499 if (dtmp > FTINY*FTINY) {
500 dtmp1 = DOT(h,wp->ts.mo.pnorm);
501 dtmp = 1.0 - dtmp1*dtmp1/dtmp;
502 }
503 if (dtmp > FTINY*FTINY) {
504 dtmp1 = DOT(h,wp->ts.u);
505 dtmp1 *= dtmp1 / au2;
506 dtmp2 = DOT(h,wp->ts.v);
507 dtmp2 *= dtmp2 / av2;
508 dtmp = (dtmp1 + dtmp2) / dtmp;
509 dtmp = exp(-dtmp);
510 } else
511 dtmp = 1.0;
512 /* Gaussian */
513 dtmp *= (1.0/PI) * sqrt(-ldot/(wp->ts.mo.pdot*au2*av2));
514
515 if (dtmp > FTINY) { /* worth using? */
516 copyscolor(sctmp, wp->ts.scol);
517 dtmp *= omega;
518 scalescolor(sctmp, dtmp);
519 saddscolor(scval, sctmp);
520 }
521 }
522 }
523
524 /* color a ray that hit a programmable WGMD material */
525 int
526 m_wgmdf(OBJREC *m, RAY *r)
527 {
528 RAY lr;
529 WGMDDAT wd;
530 SCOLOR sctmp;
531 FVECT anorm;
532 int i;
533
534 if (!backvis & (r->rod < 0.0)) {
535 raytrans(r);
536 return(1); /* backside invisible */
537 }
538 if ((m->oargs.nsargs < 13) | (m->oargs.nfargs < 9))
539 objerror(m, USER, "bad number of arguments");
540
541 if (r->crtype & SHADOW && !strcmp(m->oargs.sarg[5], "0"))
542 return(1); /* first shadow test */
543 clr_comps(&wd);
544 wd.rp = r;
545 wd.mtp = m;
546 wd.mf = getfunc(m, 12, 0xEEE, 1);
547 set_dcomp(&wd, 0); /* gets main modifier */
548 setWGMDfunc(&wd.rd.mo, &wd); /* get local u vector */
549 errno = 0;
550 for (i = 0; i < 3; i++)
551 wd.ulocal[i] = evalue(wd.mf->ep[6+i]);
552 if ((errno == EDOM) | (errno == ERANGE))
553 wd.ulocal[0] = wd.ulocal[1] = wd.ulocal[2] = 0.0;
554 else if (wd.mf->fxp != &unitxf)
555 multv3(wd.ulocal, wd.ulocal, wd.mf->fxp->xfm);
556
557 set_scomp(&wd, 1); /* sets SP_TPURE */
558 if (r->crtype & SHADOW && !(wd.specfl & SP_TPURE))
559 return(1); /* second shadow test */
560 set_dcomp(&wd, 1);
561 set_scomp(&wd, 0);
562 wd.specfl |= SP_FLAT*(r->ro != NULL && isflat(r->ro->otype));
563 /* apply Fresnel adjustments? */
564 if (wd.specfl & SP_RPURE && pbright(wd.rs.scol) >= FRESTHRESH) {
565 const double fest = FRESNE(fabs(wd.rs.mo.pdot));
566 for (i = NCSAMP; i--; )
567 wd.rs.scol[i] += fest*(1. - wd.rs.scol[i]);
568 scalescolor(wd.rd.scol, 1.-fest);
569 scalescolor(wd.ts.scol, 1.-fest);
570 scalescolor(wd.td.scol, 1.-fest);
571 }
572 /* check specular thresholds */
573 wd.specfl |= SP_RBLT*((wd.specfl & (SP_REFL|SP_RPURE)) == SP_REFL &&
574 specthresh >= pbright(wd.rs.scol)-FTINY);
575 wd.specfl |= SP_TBLT*((wd.specfl & (SP_TRAN|SP_TPURE)) == SP_TRAN &&
576 specthresh >= pbright(wd.ts.scol)-FTINY);
577 /* get through direction */
578 if (wd.specfl & SP_TRAN && wd.ts.mo.hastexture &&
579 !(r->crtype & (SHADOW|AMBIENT))) {
580 for (i = 0; i < 3; i++) /* perturb */
581 wd.prdir[i] = r->rdir[i] - wd.ts.mo.pnorm[i] + r->ron[i];
582 if ((DOT(wd.prdir,r->ron) > 0) ^ (r->rod > 0))
583 normalize(wd.prdir); /* OK */
584 else /* too much */
585 VCOPY(wd.prdir, r->rdir);
586 } else
587 VCOPY(wd.prdir, r->rdir);
588 /* transmitted view ray? */
589 if ((wd.specfl & (SP_TRAN|SP_TPURE|SP_TBLT)) == (SP_TRAN|SP_TPURE) &&
590 rayorigin(&lr, TRANS, r, wd.ts.scol) == 0) {
591 VCOPY(lr.rdir, wd.prdir);
592 rayvalue(&lr);
593 smultscolor(lr.rcol, lr.rcoef);
594 saddscolor(r->rcol, lr.rcol);
595 if (scolor_mean(wd.ts.scol) >= 0.999) {
596 /* completely transparent */
597 smultscolor(lr.mcol, lr.rcoef);
598 copyscolor(r->mcol, lr.mcol);
599 r->rmt = r->rot + lr.rmt;
600 r->rxt = r->rot + lr.rxt;
601 } else if (pbright(wd.ts.scol) >
602 pbright(wd.td.scol) + pbright(wd.rd.scol))
603 r->rxt = r->rot + raydistance(&lr);
604 }
605 if (r->crtype & SHADOW)
606 return(1); /* the rest is shadow */
607 /* mirror ray? */
608 if ((wd.specfl & (SP_REFL|SP_RPURE|SP_RBLT)) == (SP_REFL|SP_RPURE) &&
609 rayorigin(&lr, REFLECTED, r, wd.rs.scol) == 0) {
610 VSUM(lr.rdir, r->rdir, wd.rs.mo.pnorm, 2.*wd.rs.mo.pdot);
611 /* fall back if would penetrate */
612 if (wd.rs.mo.hastexture &&
613 (DOT(lr.rdir,r->ron) > 0) ^ (r->rod > 0))
614 VSUM(lr.rdir, r->rdir, r->ron, 2.*r->rod);
615 checknorm(lr.rdir);
616 rayvalue(&lr);
617 smultscolor(lr.rcol, lr.rcoef);
618 copyscolor(r->mcol, lr.rcol);
619 saddscolor(r->rcol, lr.rcol);
620 r->rmt = r->rot;
621 if (wd.specfl & SP_FLAT &&
622 !wd.rs.mo.hastexture | (r->crtype & AMBIENT))
623 r->rmt += raydistance(&lr);
624 }
625 if (wd.specfl & (SP_REFL|SP_TRAN)) /* specularly scattered rays */
626 agaussamp(&wd); /* checks *BLT flags */
627
628 if (sintens(wd.rd.scol) > FTINY) { /* ambient from this side */
629 if (r->rod > 0) {
630 VCOPY(anorm, wd.rd.mo.pnorm);
631 } else {
632 anorm[0] = -wd.rd.mo.pnorm[0];
633 anorm[1] = -wd.rd.mo.pnorm[1];
634 anorm[2] = -wd.rd.mo.pnorm[2];
635 }
636 copyscolor(sctmp, wd.rd.scol);
637 if (wd.specfl & SP_RBLT) /* add in specular as well? */
638 saddscolor(sctmp, wd.rs.scol);
639 multambient(sctmp, r, anorm);
640 saddscolor(r->rcol, sctmp); /* add to returned color */
641 }
642 if (sintens(wd.td.scol) > FTINY) { /* ambient from other side */
643 if (r->rod > 0) {
644 anorm[0] = -wd.td.mo.pnorm[0];
645 anorm[1] = -wd.td.mo.pnorm[1];
646 anorm[2] = -wd.td.mo.pnorm[2];
647 } else {
648 VCOPY(anorm, wd.td.mo.pnorm);
649 }
650 copyscolor(sctmp, wd.td.scol);
651 if (wd.specfl & SP_TBLT) /* add in specular as well? */
652 saddscolor(sctmp, wd.ts.scol)
653 multambient(sctmp, r, anorm);
654 saddscolor(r->rcol, sctmp);
655 }
656 direct(r, dirwgmdf, &wd); /* add direct component last */
657 return(1);
658 }