ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/m_bsdf.c
Revision: 2.12
Committed: Sun Aug 21 16:55:29 2011 UTC (12 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.11: +7 -7 lines
Log Message:
Minor fix to through component calculation

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: m_bsdf.c,v 2.11 2011/06/08 15:37:46 greg Exp $";
3 #endif
4 /*
5 * Shading for materials with BSDFs taken from XML data files
6 */
7
8 #include "copyright.h"
9
10 #include "ray.h"
11 #include "ambient.h"
12 #include "source.h"
13 #include "func.h"
14 #include "bsdf.h"
15 #include "random.h"
16
17 /*
18 * Arguments to this material include optional diffuse colors.
19 * String arguments include the BSDF and function files.
20 * A non-zero thickness causes the strange but useful behavior
21 * of translating transmitted rays this distance beneath the surface
22 * (opposite the surface normal) to bypass any intervening geometry.
23 * Translation only affects scattered, non-source-directed samples.
24 * A non-zero thickness has the further side-effect that an unscattered
25 * (view) ray will pass right through our material if it has any
26 * non-diffuse transmission, making the BSDF surface invisible. This
27 * shows the proxied geometry instead. Thickness has the further
28 * effect of turning off reflection on the hidden side so that rays
29 * heading in the opposite direction pass unimpeded through the BSDF
30 * surface. A paired surface may be placed on the opposide side of
31 * the detail geometry, less than this thickness away, if a two-way
32 * proxy is desired. Note that the sign of the thickness is important.
33 * A positive thickness hides geometry behind the BSDF surface and uses
34 * front reflectance and transmission properties. A negative thickness
35 * hides geometry in front of the surface when rays hit from behind,
36 * and applies only the transmission and backside reflectance properties.
37 * Reflection is ignored on the hidden side, as those rays pass through.
38 * The "up" vector for the BSDF is given by three variables, defined
39 * (along with the thickness) by the named function file, or '.' if none.
40 * Together with the surface normal, this defines the local coordinate
41 * system for the BSDF.
42 * We do not reorient the surface, so if the BSDF has no back-side
43 * reflectance and none is given in the real arguments, a BSDF surface
44 * with zero thickness will appear black when viewed from behind
45 * unless backface visibility is off.
46 * The diffuse arguments are added to components in the BSDF file,
47 * not multiplied. However, patterns affect this material as a multiplier
48 * on everything except non-diffuse reflection.
49 *
50 * Arguments for MAT_BSDF are:
51 * 6+ thick BSDFfile ux uy uz funcfile transform
52 * 0
53 * 0|3|6|9 rdf gdf bdf
54 * rdb gdb bdb
55 * rdt gdt bdt
56 */
57
58 /*
59 * Note that our reverse ray-tracing process means that the positions
60 * of incoming and outgoing vectors may be reversed in our calls
61 * to the BSDF library. This is fine, since the bidirectional nature
62 * of the BSDF (that's what the 'B' stands for) means it all works out.
63 */
64
65 typedef struct {
66 OBJREC *mp; /* material pointer */
67 RAY *pr; /* intersected ray */
68 FVECT pnorm; /* perturbed surface normal */
69 FVECT vray; /* local outgoing (return) vector */
70 double sr_vpsa[2]; /* sqrt of BSDF projected solid angle extrema */
71 double thru_r2; /* through rejection angle squared */
72 RREAL toloc[3][3]; /* world to local BSDF coords */
73 RREAL fromloc[3][3]; /* local BSDF coords to world */
74 double thick; /* surface thickness */
75 SDData *sd; /* loaded BSDF data */
76 COLOR runsamp; /* BSDF hemispherical reflection */
77 COLOR rdiff; /* added diffuse reflection */
78 COLOR tunsamp; /* BSDF hemispherical transmission */
79 COLOR tdiff; /* added diffuse transmission */
80 } BSDFDAT; /* BSDF material data */
81
82 #define cvt_sdcolor(cv, svp) ccy2rgb(&(svp)->spec, (svp)->cieY, cv)
83
84 /* Jitter ray sample according to projected solid angle and specjitter */
85 static void
86 bsdf_jitter(FVECT vres, BSDFDAT *ndp, int domax)
87 {
88 double sr_psa = ndp->sr_vpsa[domax];
89
90 VCOPY(vres, ndp->vray);
91 if (specjitter < 1.)
92 sr_psa *= specjitter;
93 if (sr_psa <= FTINY)
94 return;
95 vres[0] += sr_psa*(.5 - frandom());
96 vres[1] += sr_psa*(.5 - frandom());
97 normalize(vres);
98 }
99
100 /* Evaluate BSDF for direct component, returning true if OK to proceed */
101 static int
102 direct_bsdf_OK(COLOR cval, FVECT ldir, BSDFDAT *ndp)
103 {
104 FVECT vsrc, vjit;
105 SDValue sv;
106 SDError ec;
107 /* transform source direction */
108 if (SDmapDir(vsrc, ndp->toloc, ldir) != SDEnone)
109 return(0);
110 /* jitter query direction */
111 bsdf_jitter(vjit, ndp, 0);
112 /* avoid indirect over-counting */
113 if (ndp->thru_r2 > FTINY && vsrc[2] > 0 ^ vjit[2] > 0) {
114 double dx = vsrc[0] + vjit[0];
115 double dy = vsrc[1] + vjit[1];
116 if (dx*dx + dy*dy <= ndp->thru_r2)
117 return(0);
118 }
119 ec = SDevalBSDF(&sv, vjit, vsrc, ndp->sd);
120 if (ec)
121 objerror(ndp->mp, USER, transSDError(ec));
122
123 if (sv.cieY <= FTINY) /* not worth using? */
124 return(0);
125 /* else we're good to go */
126 cvt_sdcolor(cval, &sv);
127 return(1);
128 }
129
130 /* Compute source contribution for BSDF (reflected & transmitted) */
131 static void
132 dir_bsdf(
133 COLOR cval, /* returned coefficient */
134 void *nnp, /* material data */
135 FVECT ldir, /* light source direction */
136 double omega /* light source size */
137 )
138 {
139 BSDFDAT *np = (BSDFDAT *)nnp;
140 double ldot;
141 double dtmp;
142 COLOR ctmp;
143
144 setcolor(cval, .0, .0, .0);
145
146 ldot = DOT(np->pnorm, ldir);
147 if ((-FTINY <= ldot) & (ldot <= FTINY))
148 return;
149
150 if (ldot > 0 && bright(np->rdiff) > FTINY) {
151 /*
152 * Compute added diffuse reflected component.
153 */
154 copycolor(ctmp, np->rdiff);
155 dtmp = ldot * omega * (1./PI);
156 scalecolor(ctmp, dtmp);
157 addcolor(cval, ctmp);
158 }
159 if (ldot < 0 && bright(np->tdiff) > FTINY) {
160 /*
161 * Compute added diffuse transmission.
162 */
163 copycolor(ctmp, np->tdiff);
164 dtmp = -ldot * omega * (1.0/PI);
165 scalecolor(ctmp, dtmp);
166 addcolor(cval, ctmp);
167 }
168 /*
169 * Compute scattering coefficient using BSDF.
170 */
171 if (!direct_bsdf_OK(ctmp, ldir, np))
172 return;
173 if (ldot > 0) { /* pattern only diffuse reflection */
174 COLOR ctmp1, ctmp2;
175 dtmp = (np->pr->rod > 0) ? np->sd->rLambFront.cieY
176 : np->sd->rLambBack.cieY;
177 /* diffuse fraction */
178 dtmp /= PI * bright(ctmp);
179 copycolor(ctmp2, np->pr->pcol);
180 scalecolor(ctmp2, dtmp);
181 setcolor(ctmp1, 1.-dtmp, 1.-dtmp, 1.-dtmp);
182 addcolor(ctmp1, ctmp2);
183 multcolor(ctmp, ctmp1); /* apply derated pattern */
184 dtmp = ldot * omega;
185 } else { /* full pattern on transmission */
186 multcolor(ctmp, np->pr->pcol);
187 dtmp = -ldot * omega;
188 }
189 scalecolor(ctmp, dtmp);
190 addcolor(cval, ctmp);
191 }
192
193 /* Compute source contribution for BSDF (reflected only) */
194 static void
195 dir_brdf(
196 COLOR cval, /* returned coefficient */
197 void *nnp, /* material data */
198 FVECT ldir, /* light source direction */
199 double omega /* light source size */
200 )
201 {
202 BSDFDAT *np = (BSDFDAT *)nnp;
203 double ldot;
204 double dtmp;
205 COLOR ctmp, ctmp1, ctmp2;
206
207 setcolor(cval, .0, .0, .0);
208
209 ldot = DOT(np->pnorm, ldir);
210
211 if (ldot <= FTINY)
212 return;
213
214 if (bright(np->rdiff) > FTINY) {
215 /*
216 * Compute added diffuse reflected component.
217 */
218 copycolor(ctmp, np->rdiff);
219 dtmp = ldot * omega * (1./PI);
220 scalecolor(ctmp, dtmp);
221 addcolor(cval, ctmp);
222 }
223 /*
224 * Compute reflection coefficient using BSDF.
225 */
226 if (!direct_bsdf_OK(ctmp, ldir, np))
227 return;
228 /* pattern only diffuse reflection */
229 dtmp = (np->pr->rod > 0) ? np->sd->rLambFront.cieY
230 : np->sd->rLambBack.cieY;
231 dtmp /= PI * bright(ctmp); /* diffuse fraction */
232 copycolor(ctmp2, np->pr->pcol);
233 scalecolor(ctmp2, dtmp);
234 setcolor(ctmp1, 1.-dtmp, 1.-dtmp, 1.-dtmp);
235 addcolor(ctmp1, ctmp2);
236 multcolor(ctmp, ctmp1); /* apply derated pattern */
237 dtmp = ldot * omega;
238 scalecolor(ctmp, dtmp);
239 addcolor(cval, ctmp);
240 }
241
242 /* Compute source contribution for BSDF (transmitted only) */
243 static void
244 dir_btdf(
245 COLOR cval, /* returned coefficient */
246 void *nnp, /* material data */
247 FVECT ldir, /* light source direction */
248 double omega /* light source size */
249 )
250 {
251 BSDFDAT *np = (BSDFDAT *)nnp;
252 double ldot;
253 double dtmp;
254 COLOR ctmp;
255
256 setcolor(cval, .0, .0, .0);
257
258 ldot = DOT(np->pnorm, ldir);
259
260 if (ldot >= -FTINY)
261 return;
262
263 if (bright(np->tdiff) > FTINY) {
264 /*
265 * Compute added diffuse transmission.
266 */
267 copycolor(ctmp, np->tdiff);
268 dtmp = -ldot * omega * (1.0/PI);
269 scalecolor(ctmp, dtmp);
270 addcolor(cval, ctmp);
271 }
272 /*
273 * Compute scattering coefficient using BSDF.
274 */
275 if (!direct_bsdf_OK(ctmp, ldir, np))
276 return;
277 /* full pattern on transmission */
278 multcolor(ctmp, np->pr->pcol);
279 dtmp = -ldot * omega;
280 scalecolor(ctmp, dtmp);
281 addcolor(cval, ctmp);
282 }
283
284 /* Sample separate BSDF component */
285 static int
286 sample_sdcomp(BSDFDAT *ndp, SDComponent *dcp, int usepat)
287 {
288 int nstarget = 1;
289 int nsent;
290 SDError ec;
291 SDValue bsv;
292 double xrand;
293 FVECT vsmp;
294 RAY sr;
295 /* multiple samples? */
296 if (specjitter > 1.5) {
297 nstarget = specjitter*ndp->pr->rweight + .5;
298 if (nstarget < 1)
299 nstarget = 1;
300 }
301 /* run through our samples */
302 for (nsent = 0; nsent < nstarget; nsent++) {
303 if (nstarget == 1) /* stratify random variable */
304 xrand = urand(ilhash(dimlist,ndims)+samplendx);
305 else
306 xrand = (nsent + frandom())/(double)nstarget;
307 SDerrorDetail[0] = '\0'; /* sample direction & coef. */
308 bsdf_jitter(vsmp, ndp, 0);
309 ec = SDsampComponent(&bsv, vsmp, xrand, dcp);
310 if (ec)
311 objerror(ndp->mp, USER, transSDError(ec));
312 if (bsv.cieY <= FTINY) /* zero component? */
313 break;
314 /* map vector to world */
315 if (SDmapDir(sr.rdir, ndp->fromloc, vsmp) != SDEnone)
316 break;
317 /* spawn a specular ray */
318 if (nstarget > 1)
319 bsv.cieY /= (double)nstarget;
320 cvt_sdcolor(sr.rcoef, &bsv); /* use sample color */
321 if (usepat) /* apply pattern? */
322 multcolor(sr.rcoef, ndp->pr->pcol);
323 if (rayorigin(&sr, SPECULAR, ndp->pr, sr.rcoef) < 0) {
324 if (maxdepth > 0)
325 break;
326 continue; /* Russian roulette victim */
327 }
328 /* need to offset origin? */
329 if (ndp->thick != 0 && ndp->pr->rod > 0 ^ vsmp[2] > 0)
330 VSUM(sr.rorg, sr.rorg, ndp->pr->ron, -ndp->thick);
331 rayvalue(&sr); /* send & evaluate sample */
332 multcolor(sr.rcol, sr.rcoef);
333 addcolor(ndp->pr->rcol, sr.rcol);
334 }
335 return(nsent);
336 }
337
338 /* Sample non-diffuse components of BSDF */
339 static int
340 sample_sdf(BSDFDAT *ndp, int sflags)
341 {
342 int n, ntotal = 0;
343 SDSpectralDF *dfp;
344 COLORV *unsc;
345
346 if (sflags == SDsampSpT) {
347 unsc = ndp->tunsamp;
348 dfp = ndp->sd->tf;
349 cvt_sdcolor(unsc, &ndp->sd->tLamb);
350 } else /* sflags == SDsampSpR */ {
351 unsc = ndp->runsamp;
352 if (ndp->pr->rod > 0) {
353 dfp = ndp->sd->rf;
354 cvt_sdcolor(unsc, &ndp->sd->rLambFront);
355 } else {
356 dfp = ndp->sd->rb;
357 cvt_sdcolor(unsc, &ndp->sd->rLambBack);
358 }
359 }
360 multcolor(unsc, ndp->pr->pcol);
361 if (dfp == NULL) /* no specular component? */
362 return(0);
363 /* below sampling threshold? */
364 if (dfp->maxHemi <= specthresh+FTINY) {
365 if (dfp->maxHemi > FTINY) { /* XXX no color from BSDF */
366 FVECT vjit;
367 double d;
368 COLOR ctmp;
369 bsdf_jitter(vjit, ndp, 1);
370 d = SDdirectHemi(vjit, sflags, ndp->sd);
371 if (sflags == SDsampSpT) {
372 copycolor(ctmp, ndp->pr->pcol);
373 scalecolor(ctmp, d);
374 } else /* no pattern on reflection */
375 setcolor(ctmp, d, d, d);
376 addcolor(unsc, ctmp);
377 }
378 return(0);
379 }
380 /* else need to sample */
381 dimlist[ndims++] = (int)(size_t)ndp->mp;
382 ndims++;
383 for (n = dfp->ncomp; n--; ) { /* loop over components */
384 dimlist[ndims-1] = n + 9438;
385 ntotal += sample_sdcomp(ndp, &dfp->comp[n], sflags==SDsampSpT);
386 }
387 ndims -= 2;
388 return(ntotal);
389 }
390
391 /* Color a ray that hit a BSDF material */
392 int
393 m_bsdf(OBJREC *m, RAY *r)
394 {
395 int hitfront;
396 COLOR ctmp;
397 SDError ec;
398 FVECT upvec, vtmp;
399 MFUNC *mf;
400 BSDFDAT nd;
401 /* check arguments */
402 if ((m->oargs.nsargs < 6) | (m->oargs.nfargs > 9) |
403 (m->oargs.nfargs % 3))
404 objerror(m, USER, "bad # arguments");
405 /* record surface struck */
406 hitfront = (r->rod > 0);
407 /* load cal file */
408 mf = getfunc(m, 5, 0x1d, 1);
409 /* get thickness */
410 nd.thick = evalue(mf->ep[0]);
411 if ((-FTINY <= nd.thick) & (nd.thick <= FTINY))
412 nd.thick = .0;
413 /* check shadow */
414 if (r->crtype & SHADOW) {
415 if (nd.thick != 0)
416 raytrans(r); /* pass-through */
417 return(1); /* or shadow */
418 }
419 /* check other rays to pass */
420 if (nd.thick != 0 && (!(r->crtype & (SPECULAR|AMBIENT)) ||
421 nd.thick > 0 ^ hitfront)) {
422 raytrans(r); /* hide our proxy */
423 return(1);
424 }
425 /* get BSDF data */
426 nd.sd = loadBSDF(m->oargs.sarg[1]);
427 /* diffuse reflectance */
428 if (hitfront) {
429 if (m->oargs.nfargs < 3)
430 setcolor(nd.rdiff, .0, .0, .0);
431 else
432 setcolor(nd.rdiff, m->oargs.farg[0],
433 m->oargs.farg[1],
434 m->oargs.farg[2]);
435 } else {
436 if (m->oargs.nfargs < 6) { /* check invisible backside */
437 if (!backvis && (nd.sd->rb == NULL) &
438 (nd.sd->tf == NULL)) {
439 SDfreeCache(nd.sd);
440 raytrans(r);
441 return(1);
442 }
443 setcolor(nd.rdiff, .0, .0, .0);
444 } else
445 setcolor(nd.rdiff, m->oargs.farg[3],
446 m->oargs.farg[4],
447 m->oargs.farg[5]);
448 }
449 /* diffuse transmittance */
450 if (m->oargs.nfargs < 9)
451 setcolor(nd.tdiff, .0, .0, .0);
452 else
453 setcolor(nd.tdiff, m->oargs.farg[6],
454 m->oargs.farg[7],
455 m->oargs.farg[8]);
456 nd.mp = m;
457 nd.pr = r;
458 /* get modifiers */
459 raytexture(r, m->omod);
460 /* modify diffuse values */
461 multcolor(nd.rdiff, r->pcol);
462 multcolor(nd.tdiff, r->pcol);
463 /* get up vector */
464 upvec[0] = evalue(mf->ep[1]);
465 upvec[1] = evalue(mf->ep[2]);
466 upvec[2] = evalue(mf->ep[3]);
467 /* return to world coords */
468 if (mf->f != &unitxf) {
469 multv3(upvec, upvec, mf->f->xfm);
470 nd.thick *= mf->f->sca;
471 }
472 raynormal(nd.pnorm, r);
473 /* compute local BSDF xform */
474 ec = SDcompXform(nd.toloc, nd.pnorm, upvec);
475 if (!ec) {
476 nd.vray[0] = -r->rdir[0];
477 nd.vray[1] = -r->rdir[1];
478 nd.vray[2] = -r->rdir[2];
479 ec = SDmapDir(nd.vray, nd.toloc, nd.vray);
480 }
481 if (!ec)
482 ec = SDinvXform(nd.fromloc, nd.toloc);
483 /* determine BSDF resolution */
484 if (!ec)
485 ec = SDsizeBSDF(nd.sr_vpsa, nd.vray, NULL,
486 SDqueryMin+SDqueryMax, nd.sd);
487 nd.thru_r2 = .0;
488 if (!ec && nd.thick != 0 && r->crtype & (SPECULAR|AMBIENT)) {
489 FVECT vthru;
490 vthru[0] = -nd.vray[0];
491 vthru[1] = -nd.vray[1];
492 vthru[2] = -nd.vray[2];
493 ec = SDsizeBSDF(&nd.thru_r2, nd.vray, vthru,
494 SDqueryMin, nd.sd);
495 nd.thru_r2 *= 1./PI;
496 }
497 if (ec) {
498 objerror(m, WARNING, transSDError(ec));
499 SDfreeCache(nd.sd);
500 return(1);
501 }
502 nd.sr_vpsa[0] = sqrt(nd.sr_vpsa[0]);
503 nd.sr_vpsa[1] = sqrt(nd.sr_vpsa[1]);
504 if (!hitfront) { /* perturb normal towards hit */
505 nd.pnorm[0] = -nd.pnorm[0];
506 nd.pnorm[1] = -nd.pnorm[1];
507 nd.pnorm[2] = -nd.pnorm[2];
508 }
509 /* sample reflection */
510 sample_sdf(&nd, SDsampSpR);
511 /* sample transmission */
512 sample_sdf(&nd, SDsampSpT);
513 /* compute indirect diffuse */
514 copycolor(ctmp, nd.rdiff);
515 addcolor(ctmp, nd.runsamp);
516 if (bright(ctmp) > FTINY) { /* ambient from reflection */
517 if (!hitfront)
518 flipsurface(r);
519 multambient(ctmp, r, nd.pnorm);
520 addcolor(r->rcol, ctmp);
521 if (!hitfront)
522 flipsurface(r);
523 }
524 copycolor(ctmp, nd.tdiff);
525 addcolor(ctmp, nd.tunsamp);
526 if (bright(ctmp) > FTINY) { /* ambient from other side */
527 FVECT bnorm;
528 if (hitfront)
529 flipsurface(r);
530 bnorm[0] = -nd.pnorm[0];
531 bnorm[1] = -nd.pnorm[1];
532 bnorm[2] = -nd.pnorm[2];
533 if (nd.thick != 0) { /* proxy with offset? */
534 VCOPY(vtmp, r->rop);
535 VSUM(r->rop, vtmp, r->ron, -nd.thick);
536 multambient(ctmp, r, bnorm);
537 VCOPY(r->rop, vtmp);
538 } else
539 multambient(ctmp, r, bnorm);
540 addcolor(r->rcol, ctmp);
541 if (hitfront)
542 flipsurface(r);
543 }
544 /* add direct component */
545 if ((bright(nd.tdiff) <= FTINY) & (nd.sd->tf == NULL)) {
546 direct(r, dir_brdf, &nd); /* reflection only */
547 } else if (nd.thick == 0) {
548 direct(r, dir_bsdf, &nd); /* thin surface scattering */
549 } else {
550 direct(r, dir_brdf, &nd); /* reflection first */
551 VCOPY(vtmp, r->rop); /* offset for transmitted */
552 VSUM(r->rop, vtmp, r->ron, -nd.thick);
553 direct(r, dir_btdf, &nd); /* separate transmission */
554 VCOPY(r->rop, vtmp);
555 }
556 /* clean up */
557 SDfreeCache(nd.sd);
558 return(1);
559 }