ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/m_wgmdf.c
Revision: 2.11
Committed: Sat May 31 00:54:34 2025 UTC (44 hours, 29 minutes ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.10: +9 -5 lines
Log Message:
perf: Avoid calling objndx() multiple times for multiple modifiers

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: m_wgmdf.c,v 2.10 2025/05/23 17:09: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 OBJECT mto; /* material object index (or -2) */
98 int specfl; /* specularity flags, defined above */
99 FVECT ulocal; /* u-vector in local coordinates */
100 DCOMP rd, td; /* diffuse component params */
101 SCOMP rs, ts; /* specular component params */
102 FVECT prdir; /* vector in transmitted direction */
103 } WGMDDAT; /* WGMD material data */
104
105 #define clr_comps(wp) ((wp)->specfl = 0, (wp)->mto = OVOID-1, \
106 (wp)->rd.mo.nam = (wp)->td.mo.nam = \
107 (wp)->rs.mo.nam = (wp)->ts.mo.nam = "")
108
109 /* assign modifier values */
110 static int
111 set_modval(MODVAL *mp, OBJECT omod, const RAY *r)
112 {
113 RAY tr;
114
115 if (!mp->nam[0])
116 mp->nam = (omod == OVOID) ? VOIDID : objptr(omod)->oname;
117 else if (!strcmp(mp->nam, VOIDID))
118 omod = OVOID;
119 else if (omod == OVOID)
120 return(0);
121 tr = *r; /* independent modifier */
122 raytexture(&tr, omod);
123 if (DOT(tr.pert,tr.pert) > FTINY*FTINY) {
124 mp->pdot = raynormal(mp->pnorm, &tr);
125 mp->hastexture = 1;
126 } else {
127 VCOPY(mp->pnorm, tr.ron);
128 mp->pdot = tr.rod;
129 mp->hastexture = 0;
130 }
131 copyscolor(mp->pcol, tr.pcol);
132 return(1);
133 }
134
135 /* fill modifier values, using previous setting if found */
136 static int
137 fill_modval(MODVAL *mp, WGMDDAT *wp)
138 {
139 if (mp == &wp->rd.mo) { /* special case (should be first) */
140 set_modval(mp, wp->mtp->omod, wp->rp);
141 return(1);
142 } /* use main modifier? */
143 if (!strcmp(mp->nam, ALIASMOD) || !strcmp(mp->nam, wp->rd.mo.nam)) {
144 *mp = wp->rd.mo;
145 return(1);
146 } /* check others */
147 if (mp != &wp->td.mo && !strcmp(mp->nam, wp->td.mo.nam)) {
148 *mp = wp->td.mo;
149 return(1);
150 }
151 if (mp != &wp->rs.mo && !strcmp(mp->nam, wp->rs.mo.nam)) {
152 *mp = wp->rs.mo;
153 return(1);
154 }
155 if (mp != &wp->ts.mo && !strcmp(mp->nam, wp->ts.mo.nam)) {
156 *mp = wp->ts.mo;
157 return(1);
158 }
159 if (wp->mto < OVOID)
160 wp->mto = objndx(wp->mtp);
161 /* new modifier */
162 return(set_modval(mp, lastmod(wp->mto, mp->nam), wp->rp));
163 }
164
165 /* set calculation context for given component of MAT_WGMDF */
166 static int
167 setWGMDfunc(MODVAL *mp, const WGMDDAT *wp)
168 {
169 static char lastMod[MAXSTR];
170 double sf;
171 FVECT vec;
172
173 if (setfunc(wp->mtp, wp->rp) == 0 &&
174 !strcmp(mp->nam, lastMod))
175 return(0); /* already set */
176 strcpy(lastMod, mp->nam);
177 /* else (re)assign special variables */
178 sf = 1 - 2*(wp->rp->rod < 0);
179 varset("RdotP`", '=', mp->pdot*sf);
180 multv3(vec, mp->pnorm, funcxf.xfm);
181 sf /= funcxf.sca;
182 varset("NxP`", '=', vec[0]*sf);
183 varset("NyP`", '=', vec[1]*sf);
184 varset("NzP`", '=', vec[2]*sf);
185 return(1);
186 }
187
188 /* assign indicated diffuse component (do !trans first) */
189 static void
190 set_dcomp(WGMDDAT *wp, int trans)
191 {
192 DCOMP *dp = trans ? &wp->td : &wp->rd;
193 const int offs = trans ? 6 : 3*(wp->rp->rod < 0);
194
195 if (trans) { /* transmitted diffuse? */
196 if (intens(wp->mtp->oargs.farg+offs) <= FTINY) {
197 scolorblack(dp->scol);
198 return;
199 }
200 dp->mo.nam = wp->mtp->oargs.sarg[8];
201 if (!fill_modval(&dp->mo, wp)) {
202 sprintf(errmsg,
203 "unknown diffuse transmission modifier '%s'",
204 dp->mo.nam);
205 objerror(wp->mtp, USER, errmsg);
206 }
207 } else /* no priors for main mod */
208 fill_modval(&dp->mo, wp);
209
210 setscolor(dp->scol, wp->mtp->oargs.farg[offs],
211 wp->mtp->oargs.farg[offs+1],
212 wp->mtp->oargs.farg[offs+2]);
213 smultscolor(dp->scol, dp->mo.pcol);
214 }
215
216 /* assign indicated specular component */
217 static void
218 set_scomp(WGMDDAT *wp, int trans)
219 {
220 SCOMP *sp = trans ? &wp->ts : &wp->rs;
221 EPNODE **exa = wp->mf->ep + 3*(trans != 0);
222 double coef;
223 /* constant zero check */
224 if (exa[0]->type == NUM && exa[0]->v.num <= FTINY)
225 goto blackout;
226 /* need modifier */
227 sp->mo.nam = wp->mtp->oargs.sarg[4*(trans != 0)];
228 if (!fill_modval(&sp->mo, wp)) {
229 sprintf(errmsg, "unknown specular %s modifier '%s'",
230 trans ? "transmission" : "reflection", sp->mo.nam);
231 objerror(wp->mtp, USER, errmsg);
232 }
233 if (sintens(sp->mo.pcol) <= FTINY)
234 goto blackout; /* got black pattern */
235 setWGMDfunc(&sp->mo, wp); /* else compute coefficient */
236 errno = 0;
237 coef = evalue(exa[0]);
238 if ((errno == EDOM) | (errno == ERANGE)) {
239 objerror(wp->mtp, WARNING, "specular compute error");
240 goto blackout;
241 }
242 if (coef <= FTINY) /* negligible value? */
243 goto blackout;
244 copyscolor(sp->scol, sp->mo.pcol);
245 scalescolor(sp->scol, coef);
246 errno = 0; /* else get roughness */
247 sp->u_alpha = evalue(exa[1]);
248 sp->v_alpha = (sp->u_alpha > FTINY) ? evalue(exa[2]) : 0.0;
249 if ((errno == EDOM) | (errno == ERANGE)) {
250 objerror(wp->mtp, WARNING, "roughness compute error");
251 goto blackout;
252 } /* we have something... */
253 wp->specfl |= trans ? SP_TRAN : SP_REFL;
254 if (sp->v_alpha <= FTINY) { /* is it pure specular? */
255 wp->specfl |= trans ? SP_TPURE : SP_RPURE;
256 sp->u_alpha = sp->v_alpha = 0.0;
257 return;
258 } /* else get aniso coordinates */
259 fcross(sp->v, sp->mo.pnorm, wp->ulocal);
260 if (normalize(sp->v) == 0.0) { /* orientation vector==normal? */
261 if (fabs(sp->u_alpha - sp->v_alpha) > 0.001)
262 objerror(wp->mtp, WARNING, "bad orientation vector");
263 getperpendicular(sp->u, sp->mo.pnorm, 0); /* punting */
264 fcross(sp->v, sp->mo.pnorm, sp->u);
265 sp->u_alpha = sp->v_alpha = sqrt( 0.5 *
266 (sp->u_alpha*sp->u_alpha + sp->v_alpha*sp->v_alpha) );
267 } else
268 fcross(sp->u, sp->v, sp->mo.pnorm);
269 return;
270 blackout:
271 scolorblack(sp->scol); /* zero out component */
272 }
273
274 /* sample anisotropic Gaussian specular */
275 static void
276 agaussamp(WGMDDAT *wp)
277 {
278 RAY sr;
279 FVECT h;
280 double rv[2];
281 double d, sinp, cosp;
282 int maxiter, ntrials, nstarget, nstaken;
283 int i;
284 /* compute reflection */
285 if ((wp->specfl & (SP_REFL|SP_RPURE|SP_RBLT)) == SP_REFL &&
286 rayorigin(&sr, RSPECULAR, wp->rp, wp->rs.scol) == 0) {
287 SCOLOR scol;
288 nstarget = 1;
289 if (specjitter > 1.5) { /* multiple samples? */
290 nstarget = specjitter*wp->rp->rweight + .5;
291 if (sr.rweight <= minweight*nstarget)
292 nstarget = sr.rweight/minweight;
293 if (nstarget > 1) {
294 d = 1./nstarget;
295 scalescolor(sr.rcoef, d);
296 sr.rweight *= d;
297 } else
298 nstarget = 1;
299 }
300 scolorblack(scol);
301 dimlist[ndims++] = (int)(size_t)wp->mtp;
302 maxiter = MAXITER*nstarget;
303 for (nstaken = ntrials = 0; (nstaken < nstarget) &
304 (ntrials < maxiter); ntrials++) {
305 if (ntrials)
306 d = frandom();
307 else
308 d = urand(ilhash(dimlist,ndims)+samplendx);
309 multisamp(rv, 2, d);
310 d = 2.0*PI * rv[0];
311 cosp = tcos(d) * wp->rs.u_alpha;
312 sinp = tsin(d) * wp->rs.v_alpha;
313 d = 1./sqrt(cosp*cosp + sinp*sinp);
314 cosp *= d;
315 sinp *= d;
316 if ((0. <= specjitter) & (specjitter < 1.))
317 rv[1] = 1.0 - specjitter*rv[1];
318 d = (rv[1] <= FTINY) ? 1.0 : sqrt( -log(rv[1]) /
319 (cosp*cosp/(wp->rs.u_alpha*wp->rs.u_alpha) +
320 sinp*sinp/(wp->rs.v_alpha*wp->rs.v_alpha)) );
321 for (i = 0; i < 3; i++)
322 h[i] = wp->rs.mo.pnorm[i] +
323 d*(cosp*wp->rs.u[i] + sinp*wp->rs.v[i]);
324 d = -2.0 * DOT(h, wp->rp->rdir) / (1.0 + d*d);
325 VSUM(sr.rdir, wp->rp->rdir, h, d);
326 /* sample rejection test */
327 d = DOT(sr.rdir, wp->rp->ron);
328 if ((d > 0) ^ (wp->rp->rod > 0))
329 continue;
330 checknorm(sr.rdir);
331 if (nstarget > 1) { /* W-G-M-D adjustment */
332 if (nstaken) rayclear(&sr);
333 rayvalue(&sr);
334 d = 2./(1. + wp->rp->rod/d);
335 scalescolor(sr.rcol, d);
336 saddscolor(scol, sr.rcol);
337 } else {
338 rayvalue(&sr);
339 smultscolor(sr.rcol, sr.rcoef);
340 saddscolor(wp->rp->rcol, sr.rcol);
341 }
342 ++nstaken;
343 }
344 if (nstarget > 1) { /* final W-G-M-D weighting */
345 smultscolor(scol, sr.rcoef);
346 d = (double)nstarget/ntrials;
347 scalescolor(scol, d);
348 saddscolor(wp->rp->rcol, scol);
349 }
350 ndims--;
351 }
352 /* compute transmission */
353 if ((wp->specfl & (SP_TRAN|SP_TPURE|SP_TBLT)) == SP_TRAN &&
354 rayorigin(&sr, TSPECULAR, wp->rp, wp->ts.scol) == 0) {
355 nstarget = 1;
356 if (specjitter > 1.5) { /* multiple samples? */
357 nstarget = specjitter*wp->rp->rweight + .5;
358 if (sr.rweight <= minweight*nstarget)
359 nstarget = sr.rweight/minweight;
360 if (nstarget > 1) {
361 d = 1./nstarget;
362 scalescolor(sr.rcoef, d);
363 sr.rweight *= d;
364 } else
365 nstarget = 1;
366 }
367 dimlist[ndims++] = (int)(size_t)wp->mtp;
368 maxiter = MAXITER*nstarget;
369 for (nstaken = ntrials = 0; (nstaken < nstarget) &
370 (ntrials < maxiter); ntrials++) {
371 if (ntrials)
372 d = frandom();
373 else
374 d = urand(ilhash(dimlist,ndims)+1823+samplendx);
375 multisamp(rv, 2, d);
376 d = 2.0*PI * rv[0];
377 cosp = tcos(d) * wp->ts.u_alpha;
378 sinp = tsin(d) * wp->ts.v_alpha;
379 d = 1./sqrt(cosp*cosp + sinp*sinp);
380 cosp *= d;
381 sinp *= d;
382 if ((0. <= specjitter) & (specjitter < 1.))
383 rv[1] = 1.0 - specjitter*rv[1];
384 if (rv[1] <= FTINY)
385 d = 1.0;
386 else
387 d = sqrt(-log(rv[1]) /
388 (cosp*cosp/(wp->ts.u_alpha*wp->ts.u_alpha) +
389 sinp*sinp/(wp->ts.v_alpha*wp->ts.v_alpha)));
390 for (i = 0; i < 3; i++)
391 sr.rdir[i] = wp->prdir[i] +
392 d*(cosp*wp->ts.u[i] + sinp*wp->ts.v[i]);
393 /* rejection test */
394 if ((DOT(sr.rdir,wp->rp->ron) > 0) == (wp->rp->rod > 0))
395 continue;
396 normalize(sr.rdir); /* OK, normalize */
397 if (nstaken) /* multi-sampling? */
398 rayclear(&sr);
399 rayvalue(&sr);
400 smultscolor(sr.rcol, sr.rcoef);
401 saddscolor(wp->rp->rcol, sr.rcol);
402 ++nstaken;
403 }
404 ndims--;
405 }
406 }
407
408 /* compute source contribution for MAT_WGMDF */
409 static void
410 dirwgmdf(SCOLOR scval, void *uwp, FVECT ldir, double omega)
411 {
412 WGMDDAT *wp = (WGMDDAT *)uwp;
413 const int hitfront = (wp->rp->rod > 0);
414 double fresadj = 1.;
415 double ldot;
416 double dtmp, dtmp1, dtmp2;
417 FVECT h;
418 double au2, av2;
419 SCOLOR sctmp;
420
421 scolorblack(scval); /* will add component coefficients */
422
423 /* XXX ignores which side is lit */
424 if (wp->specfl & SP_RPURE && pbright(wp->rs.scol) >= FRESTHRESH)
425 fresadj = 1. - FRESNE(fabs(DOT(wp->rs.mo.pnorm,ldir)));
426
427 if (sintens(wp->rd.scol) > FTINY &&
428 ((ldot = DOT(wp->rd.mo.pnorm,ldir)) > 0) == hitfront) {
429 /*
430 * Compute diffuse reflection coefficient for source.
431 */
432 copyscolor(sctmp, wp->rd.scol);
433 dtmp = fabs(ldot) * omega * (1.0/PI) * fresadj;
434 scalescolor(sctmp, dtmp);
435 saddscolor(scval, sctmp);
436 }
437 if (sintens(wp->td.scol) > FTINY &&
438 ((ldot = DOT(wp->td.mo.pnorm,ldir)) > 0) ^ hitfront) {
439 /*
440 * Compute diffuse transmission coefficient for source.
441 */
442 copyscolor(sctmp, wp->td.scol);
443 dtmp = fabs(ldot) * omega * (1.0/PI) * fresadj;
444 scalescolor(sctmp, dtmp);
445 saddscolor(scval, sctmp);
446 }
447 #if 0 /* XXX not yet implemented */
448 if (ambRayInPmap(wp->rp))
449 return; /* specular accounted for in photon map */
450 #endif
451 if ((wp->specfl & (SP_REFL|SP_RPURE)) == SP_REFL &&
452 ((ldot = DOT(wp->rs.mo.pnorm,ldir)) > 0) == hitfront) {
453 /*
454 * Compute specular reflection coefficient for source using
455 * anisotropic Gaussian distribution model.
456 */
457 /* add source width if flat */
458 if (wp->specfl & SP_FLAT)
459 au2 = av2 = (1. - dstrsrc) * omega * (0.25/PI);
460 else
461 au2 = av2 = 0.0;
462 au2 += wp->rs.u_alpha*wp->rs.u_alpha;
463 av2 += wp->rs.v_alpha*wp->rs.v_alpha;
464 /* half vector */
465 VSUB(h, ldir, wp->rp->rdir);
466 /* ellipse */
467 dtmp1 = DOT(wp->rs.u, h);
468 dtmp1 *= dtmp1 / au2;
469 dtmp2 = DOT(wp->rs.v, h);
470 dtmp2 *= dtmp2 / av2;
471 /* W-G-M-D model */
472 dtmp = DOT(wp->rs.mo.pnorm, h);
473 dtmp *= dtmp;
474 dtmp1 = (dtmp1 + dtmp2) / dtmp;
475 dtmp = exp(-dtmp1) * DOT(h,h) /
476 (PI * dtmp*dtmp * sqrt(au2*av2));
477
478 if (dtmp > FTINY) { /* worth using? */
479 copyscolor(sctmp, wp->rs.scol);
480 dtmp *= fabs(ldot) * omega;
481 scalescolor(sctmp, dtmp);
482 saddscolor(scval, sctmp);
483 }
484 }
485 if ((wp->specfl & (SP_TRAN|SP_TPURE)) == SP_TRAN &&
486 ((ldot = DOT(wp->ts.mo.pnorm,ldir)) > 0) ^ hitfront) {
487 /*
488 * Compute specular transmission coefficient for source.
489 */
490 /* roughness + source */
491 au2 = av2 = omega * (1.0/PI);
492 au2 += wp->ts.u_alpha*wp->ts.u_alpha;
493 av2 += wp->ts.v_alpha*wp->ts.v_alpha;
494 /* "half vector" */
495 VSUB(h, ldir, wp->prdir);
496 dtmp = DOT(h,h);
497 if (dtmp > FTINY*FTINY) {
498 dtmp1 = DOT(h,wp->ts.mo.pnorm);
499 dtmp = 1.0 - dtmp1*dtmp1/dtmp;
500 }
501 if (dtmp > FTINY*FTINY) {
502 dtmp1 = DOT(h,wp->ts.u);
503 dtmp1 *= dtmp1 / au2;
504 dtmp2 = DOT(h,wp->ts.v);
505 dtmp2 *= dtmp2 / av2;
506 dtmp = (dtmp1 + dtmp2) / dtmp;
507 dtmp = exp(-dtmp);
508 } else
509 dtmp = 1.0;
510 /* Gaussian */
511 dtmp *= (1.0/PI) * sqrt(-ldot/(wp->ts.mo.pdot*au2*av2));
512
513 if (dtmp > FTINY) { /* worth using? */
514 copyscolor(sctmp, wp->ts.scol);
515 dtmp *= omega;
516 scalescolor(sctmp, dtmp);
517 saddscolor(scval, sctmp);
518 }
519 }
520 }
521
522 /* color a ray that hit a programmable WGMD material */
523 int
524 m_wgmdf(OBJREC *m, RAY *r)
525 {
526 RAY lr;
527 WGMDDAT wd;
528 SCOLOR sctmp;
529 FVECT anorm;
530 int i;
531
532 if (!backvis & (r->rod < 0.0)) {
533 raytrans(r);
534 return(1); /* backside invisible */
535 }
536 if ((m->oargs.nsargs < 13) | (m->oargs.nfargs < 9))
537 objerror(m, USER, "bad number of arguments");
538
539 if (r->crtype & SHADOW && !strcmp(m->oargs.sarg[5], "0"))
540 return(1); /* first shadow test */
541 clr_comps(&wd);
542 wd.rp = r;
543 wd.mtp = m;
544 wd.mf = getfunc(m, 12, 0xEEE, 1);
545 set_dcomp(&wd, 0); /* gets main modifier */
546 setWGMDfunc(&wd.rd.mo, &wd); /* get local u vector */
547 errno = 0;
548 for (i = 0; i < 3; i++)
549 wd.ulocal[i] = evalue(wd.mf->ep[6+i]);
550 if ((errno == EDOM) | (errno == ERANGE))
551 wd.ulocal[0] = wd.ulocal[1] = wd.ulocal[2] = 0.0;
552 else if (wd.mf->fxp != &unitxf)
553 multv3(wd.ulocal, wd.ulocal, wd.mf->fxp->xfm);
554
555 set_scomp(&wd, 1); /* sets SP_TPURE */
556 if (r->crtype & SHADOW && !(wd.specfl & SP_TPURE))
557 return(1); /* second shadow test */
558 set_dcomp(&wd, 1);
559 set_scomp(&wd, 0);
560 wd.specfl |= SP_FLAT*(!wd.rs.mo.hastexture &&
561 r->ro != NULL && isflat(r->ro->otype));
562 /* apply Fresnel adjustments? */
563 if (wd.specfl & SP_RPURE && pbright(wd.rs.scol) >= FRESTHRESH) {
564 const double fest = FRESNE(fabs(wd.rs.mo.pdot));
565 for (i = NCSAMP; i--; )
566 wd.rs.scol[i] += fest*(1. - wd.rs.scol[i]);
567 scalescolor(wd.rd.scol, 1.-fest);
568 scalescolor(wd.ts.scol, 1.-fest);
569 scalescolor(wd.td.scol, 1.-fest);
570 }
571 /* check specular thresholds */
572 wd.specfl |= SP_RBLT*((wd.specfl & (SP_REFL|SP_RPURE)) == SP_REFL &&
573 specthresh >= pbright(wd.rs.scol)-FTINY);
574 wd.specfl |= SP_TBLT*((wd.specfl & (SP_TRAN|SP_TPURE)) == SP_TRAN &&
575 specthresh >= pbright(wd.ts.scol)-FTINY);
576 /* get through direction */
577 if (wd.specfl & SP_TRAN && wd.ts.mo.hastexture &&
578 !(r->crtype & (SHADOW|AMBIENT))) {
579 for (i = 0; i < 3; i++) /* perturb */
580 wd.prdir[i] = r->rdir[i] - wd.ts.mo.pnorm[i] + r->ron[i];
581 if ((DOT(wd.prdir,r->ron) > 0) ^ (r->rod > 0))
582 normalize(wd.prdir); /* OK */
583 else /* too much */
584 VCOPY(wd.prdir, r->rdir);
585 } else
586 VCOPY(wd.prdir, r->rdir);
587 /* transmitted view ray? */
588 if ((wd.specfl & (SP_TRAN|SP_TPURE|SP_TBLT)) == (SP_TRAN|SP_TPURE) &&
589 rayorigin(&lr, TRANS, r, wd.ts.scol) == 0) {
590 VCOPY(lr.rdir, wd.prdir);
591 rayvalue(&lr);
592 smultscolor(lr.rcol, lr.rcoef);
593 saddscolor(r->rcol, lr.rcol);
594 if (scolor_mean(wd.ts.scol) >= 0.999) {
595 /* completely transparent */
596 smultscolor(lr.mcol, lr.rcoef);
597 copyscolor(r->mcol, lr.mcol);
598 r->rmt = r->rot + lr.rmt;
599 r->rxt = r->rot + lr.rxt;
600 } else if (pbright(wd.ts.scol) >
601 pbright(wd.td.scol) + pbright(wd.rd.scol))
602 r->rxt = r->rot + raydistance(&lr);
603 }
604 if (r->crtype & SHADOW)
605 return(1); /* the rest is shadow */
606 /* mirror ray? */
607 if ((wd.specfl & (SP_REFL|SP_RPURE|SP_RBLT)) == (SP_REFL|SP_RPURE) &&
608 rayorigin(&lr, REFLECTED, r, wd.rs.scol) == 0) {
609 VSUM(lr.rdir, r->rdir, wd.rs.mo.pnorm, 2.*wd.rs.mo.pdot);
610 /* fall back if would penetrate */
611 if (wd.rs.mo.hastexture &&
612 (DOT(lr.rdir,r->ron) > 0) ^ (r->rod > 0))
613 VSUM(lr.rdir, r->rdir, r->ron, 2.*r->rod);
614 checknorm(lr.rdir);
615 rayvalue(&lr);
616 smultscolor(lr.rcol, lr.rcoef);
617 copyscolor(r->mcol, lr.rcol);
618 saddscolor(r->rcol, lr.rcol);
619 r->rmt = r->rot;
620 if (wd.specfl & SP_FLAT && r->crtype & AMBIENT)
621 r->rmt += raydistance(&lr);
622 }
623 if (wd.specfl & (SP_REFL|SP_TRAN)) /* specularly scattered rays */
624 agaussamp(&wd); /* checks *BLT flags */
625
626 if (sintens(wd.rd.scol) > FTINY) { /* ambient from this side */
627 if (r->rod > 0) {
628 VCOPY(anorm, wd.rd.mo.pnorm);
629 } else {
630 anorm[0] = -wd.rd.mo.pnorm[0];
631 anorm[1] = -wd.rd.mo.pnorm[1];
632 anorm[2] = -wd.rd.mo.pnorm[2];
633 }
634 copyscolor(sctmp, wd.rd.scol);
635 if (wd.specfl & SP_RBLT) /* add in specular as well? */
636 saddscolor(sctmp, wd.rs.scol);
637 multambient(sctmp, r, anorm);
638 saddscolor(r->rcol, sctmp); /* add to returned color */
639 }
640 if (sintens(wd.td.scol) > FTINY) { /* ambient from other side */
641 if (r->rod > 0) {
642 anorm[0] = -wd.td.mo.pnorm[0];
643 anorm[1] = -wd.td.mo.pnorm[1];
644 anorm[2] = -wd.td.mo.pnorm[2];
645 } else {
646 VCOPY(anorm, wd.td.mo.pnorm);
647 }
648 copyscolor(sctmp, wd.td.scol);
649 if (wd.specfl & SP_TBLT) /* add in specular as well? */
650 saddscolor(sctmp, wd.ts.scol)
651 multambient(sctmp, r, anorm);
652 saddscolor(r->rcol, sctmp);
653 }
654 direct(r, dirwgmdf, &wd); /* add direct component last */
655 return(1);
656 }