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 (6 days, 11 hours 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

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.11 static const char RCSid[] = "$Id: m_wgmdf.c,v 2.10 2025/05/23 17:09:26 greg Exp $";
3 greg 2.1 #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 greg 2.11 OBJECT mto; /* material object index (or -2) */
98 greg 2.1 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 greg 2.11 #define clr_comps(wp) ((wp)->specfl = 0, (wp)->mto = OVOID-1, \
106 greg 2.1 (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 greg 2.11 fill_modval(MODVAL *mp, WGMDDAT *wp)
138 greg 2.1 {
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 greg 2.11 }
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 greg 2.1 }
164    
165 greg 2.6 /* set calculation context for given component of MAT_WGMDF */
166 greg 2.5 static int
167     setWGMDfunc(MODVAL *mp, const WGMDDAT *wp)
168     {
169 greg 2.6 static char lastMod[MAXSTR];
170 greg 2.5 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 greg 2.6 strcpy(lastMod, mp->nam);
177 greg 2.5 /* else (re)assign special variables */
178 greg 2.6 sf = 1 - 2*(wp->rp->rod < 0);
179     varset("RdotP`", '=', mp->pdot*sf);
180     multv3(vec, mp->pnorm, funcxf.xfm);
181 greg 2.5 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 greg 2.1 /* 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 greg 2.6 SCOMP *sp = trans ? &wp->ts : &wp->rs;
221     EPNODE **exa = wp->mf->ep + 3*(trans != 0);
222     double coef;
223 greg 2.5 /* constant zero check */
224 greg 2.7 if (exa[0]->type == NUM && exa[0]->v.num <= FTINY)
225     goto blackout;
226     /* need modifier */
227 greg 2.5 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 greg 2.7 if (sintens(sp->mo.pcol) <= FTINY)
234     goto blackout; /* got black pattern */
235     setWGMDfunc(&sp->mo, wp); /* else compute coefficient */
236 greg 2.1 errno = 0;
237 greg 2.6 coef = evalue(exa[0]);
238 greg 2.1 if ((errno == EDOM) | (errno == ERANGE)) {
239     objerror(wp->mtp, WARNING, "specular compute error");
240 greg 2.7 goto blackout;
241 greg 2.1 }
242 greg 2.7 if (coef <= FTINY) /* negligible value? */
243     goto blackout;
244 greg 2.1 copyscolor(sp->scol, sp->mo.pcol);
245     scalescolor(sp->scol, coef);
246 greg 2.5 errno = 0; /* else get roughness */
247 greg 2.6 sp->u_alpha = evalue(exa[1]);
248     sp->v_alpha = (sp->u_alpha > FTINY) ? evalue(exa[2]) : 0.0;
249 greg 2.1 if ((errno == EDOM) | (errno == ERANGE)) {
250     objerror(wp->mtp, WARNING, "roughness compute error");
251 greg 2.7 goto blackout;
252 greg 2.1 } /* 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 greg 2.7 } /* else get aniso coordinates */
259 greg 2.1 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 greg 2.10 getperpendicular(sp->u, sp->mo.pnorm, 0); /* punting */
264 greg 2.1 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 greg 2.7 return;
270     blackout:
271     scolorblack(sp->scol); /* zero out component */
272 greg 2.1 }
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 greg 2.9 au2 = av2 = (1. - dstrsrc) * omega * (0.25/PI);
460 greg 2.1 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 greg 2.2
539     if (r->crtype & SHADOW && !strcmp(m->oargs.sarg[5], "0"))
540     return(1); /* first shadow test */
541 greg 2.1 clr_comps(&wd);
542     wd.rp = r;
543     wd.mtp = m;
544     wd.mf = getfunc(m, 12, 0xEEE, 1);
545 greg 2.5 set_dcomp(&wd, 0); /* gets main modifier */
546     setWGMDfunc(&wd.rd.mo, &wd); /* get local u vector */
547 greg 2.1 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 greg 2.2 return(1); /* second shadow test */
558     set_dcomp(&wd, 1);
559 greg 2.1 set_scomp(&wd, 0);
560 greg 2.8 wd.specfl |= SP_FLAT*(!wd.rs.mo.hastexture &&
561     r->ro != NULL && isflat(r->ro->otype));
562 greg 2.1 /* 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 greg 2.4 scalescolor(wd.rd.scol, 1.-fest);
568 greg 2.1 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 greg 2.8 if (wd.specfl & SP_FLAT && r->crtype & AMBIENT)
621 greg 2.1 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     }