ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/m_bsdf.c
Revision: 2.6
Committed: Sun Feb 20 17:43:43 2011 UTC (13 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.5: +12 -10 lines
Log Message:
Fixed bug in source sampling for proxies

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: m_bsdf.c,v 2.5 2011/02/20 06:34:19 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|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; /* sqrt of BSDF projected solid angle */
71 RREAL toloc[3][3]; /* world to local BSDF coords */
72 RREAL fromloc[3][3]; /* local BSDF coords to world */
73 double thick; /* surface thickness */
74 SDData *sd; /* loaded BSDF data */
75 COLOR runsamp; /* BSDF hemispherical reflection */
76 COLOR rdiff; /* added diffuse reflection */
77 COLOR tunsamp; /* BSDF hemispherical transmission */
78 COLOR tdiff; /* added diffuse transmission */
79 } BSDFDAT; /* BSDF material data */
80
81 #define cvt_sdcolor(cv, svp) ccy2rgb(&(svp)->spec, (svp)->cieY, cv)
82
83 /* Jitter ray sample according to projected solid angle and specjitter */
84 static void
85 bsdf_jitter(FVECT vres, BSDFDAT *ndp)
86 {
87 double sr_psa = ndp->sr_vpsa;
88
89 VCOPY(vres, ndp->vray);
90 if (specjitter < 1.)
91 sr_psa *= specjitter;
92 if (sr_psa <= FTINY)
93 return;
94 vres[0] += sr_psa*(.5 - frandom());
95 vres[1] += sr_psa*(.5 - frandom());
96 normalize(vres);
97 }
98
99 /* Compute source contribution for BSDF (reflected & transmitted) */
100 static void
101 dir_bsdf(
102 COLOR cval, /* returned coefficient */
103 void *nnp, /* material data */
104 FVECT ldir, /* light source direction */
105 double omega /* light source size */
106 )
107 {
108 BSDFDAT *np = (BSDFDAT *)nnp;
109 SDError ec;
110 SDValue sv;
111 FVECT vsrc;
112 FVECT vjit;
113 double ldot;
114 double dtmp;
115 COLOR ctmp;
116
117 setcolor(cval, .0, .0, .0);
118
119 ldot = DOT(np->pnorm, ldir);
120 if ((-FTINY <= ldot) & (ldot <= FTINY))
121 return;
122
123 if (ldot > .0 && bright(np->rdiff) > FTINY) {
124 /*
125 * Compute added diffuse reflected component.
126 */
127 copycolor(ctmp, np->rdiff);
128 dtmp = ldot * omega * (1./PI);
129 scalecolor(ctmp, dtmp);
130 addcolor(cval, ctmp);
131 }
132 if (ldot < .0 && bright(np->tdiff) > FTINY) {
133 /*
134 * Compute added diffuse transmission.
135 */
136 copycolor(ctmp, np->tdiff);
137 dtmp = -ldot * omega * (1.0/PI);
138 scalecolor(ctmp, dtmp);
139 addcolor(cval, ctmp);
140 }
141 /*
142 * Compute scattering coefficient using BSDF.
143 */
144 if (SDmapDir(vsrc, np->toloc, ldir) != SDEnone)
145 return;
146 bsdf_jitter(vjit, np);
147 ec = SDevalBSDF(&sv, vjit, vsrc, np->sd);
148 if (ec)
149 objerror(np->mp, USER, transSDError(ec));
150
151 if (sv.cieY <= FTINY) /* not worth using? */
152 return;
153 cvt_sdcolor(ctmp, &sv);
154 if (ldot > .0) { /* pattern only diffuse reflection */
155 COLOR ctmp1, ctmp2;
156 dtmp = (np->pr->rod > .0) ? np->sd->rLambFront.cieY
157 : np->sd->rLambBack.cieY;
158 dtmp /= PI * sv.cieY; /* diffuse fraction */
159 copycolor(ctmp2, np->pr->pcol);
160 scalecolor(ctmp2, dtmp);
161 setcolor(ctmp1, 1.-dtmp, 1.-dtmp, 1.-dtmp);
162 addcolor(ctmp1, ctmp2);
163 multcolor(ctmp, ctmp1); /* apply derated pattern */
164 dtmp = ldot * omega;
165 } else { /* full pattern on transmission */
166 multcolor(ctmp, np->pr->pcol);
167 dtmp = -ldot * omega;
168 }
169 scalecolor(ctmp, dtmp);
170 addcolor(cval, ctmp);
171 }
172
173 /* Compute source contribution for BSDF (reflected only) */
174 static void
175 dir_brdf(
176 COLOR cval, /* returned coefficient */
177 void *nnp, /* material data */
178 FVECT ldir, /* light source direction */
179 double omega /* light source size */
180 )
181 {
182 BSDFDAT *np = (BSDFDAT *)nnp;
183 SDError ec;
184 SDValue sv;
185 FVECT vsrc;
186 FVECT vjit;
187 double ldot;
188 double dtmp;
189 COLOR ctmp, ctmp1, ctmp2;
190
191 setcolor(cval, .0, .0, .0);
192
193 ldot = DOT(np->pnorm, ldir);
194
195 if (ldot <= FTINY)
196 return;
197
198 if (bright(np->rdiff) > FTINY) {
199 /*
200 * Compute added diffuse reflected component.
201 */
202 copycolor(ctmp, np->rdiff);
203 dtmp = ldot * omega * (1./PI);
204 scalecolor(ctmp, dtmp);
205 addcolor(cval, ctmp);
206 }
207 /*
208 * Compute reflection coefficient using BSDF.
209 */
210 if (SDmapDir(vsrc, np->toloc, ldir) != SDEnone)
211 return;
212 bsdf_jitter(vjit, np);
213 ec = SDevalBSDF(&sv, vjit, vsrc, np->sd);
214 if (ec)
215 objerror(np->mp, USER, transSDError(ec));
216
217 if (sv.cieY <= FTINY) /* not worth using? */
218 return;
219 cvt_sdcolor(ctmp, &sv);
220 /* pattern only diffuse reflection */
221 dtmp = (np->pr->rod > .0) ? np->sd->rLambFront.cieY
222 : np->sd->rLambBack.cieY;
223 dtmp /= PI * sv.cieY; /* diffuse fraction */
224 copycolor(ctmp2, np->pr->pcol);
225 scalecolor(ctmp2, dtmp);
226 setcolor(ctmp1, 1.-dtmp, 1.-dtmp, 1.-dtmp);
227 addcolor(ctmp1, ctmp2);
228 multcolor(ctmp, ctmp1); /* apply derated pattern */
229 dtmp = ldot * omega;
230 scalecolor(ctmp, dtmp);
231 addcolor(cval, ctmp);
232 }
233
234 /* Compute source contribution for BSDF (transmitted only) */
235 static void
236 dir_btdf(
237 COLOR cval, /* returned coefficient */
238 void *nnp, /* material data */
239 FVECT ldir, /* light source direction */
240 double omega /* light source size */
241 )
242 {
243 BSDFDAT *np = (BSDFDAT *)nnp;
244 SDError ec;
245 SDValue sv;
246 FVECT vsrc;
247 FVECT vjit;
248 double ldot;
249 double dtmp;
250 COLOR ctmp;
251
252 setcolor(cval, .0, .0, .0);
253
254 ldot = DOT(np->pnorm, ldir);
255
256 if (ldot >= -FTINY)
257 return;
258
259 if (bright(np->tdiff) > FTINY) {
260 /*
261 * Compute added diffuse transmission.
262 */
263 copycolor(ctmp, np->tdiff);
264 dtmp = -ldot * omega * (1.0/PI);
265 scalecolor(ctmp, dtmp);
266 addcolor(cval, ctmp);
267 }
268 /*
269 * Compute scattering coefficient using BSDF.
270 */
271 if (SDmapDir(vsrc, np->toloc, ldir) != SDEnone)
272 return;
273 bsdf_jitter(vjit, np);
274 ec = SDevalBSDF(&sv, vjit, vsrc, np->sd);
275 if (ec)
276 objerror(np->mp, USER, transSDError(ec));
277
278 if (sv.cieY <= FTINY) /* not worth using? */
279 return;
280 cvt_sdcolor(ctmp, &sv);
281 /* full pattern on transmission */
282 multcolor(ctmp, np->pr->pcol);
283 dtmp = -ldot * omega;
284 scalecolor(ctmp, dtmp);
285 addcolor(cval, ctmp);
286 }
287
288 /* Sample separate BSDF component */
289 static int
290 sample_sdcomp(BSDFDAT *ndp, SDComponent *dcp, int usepat)
291 {
292 int nstarget = 1;
293 int nsent = 0;
294 SDError ec;
295 SDValue bsv;
296 double sthick;
297 FVECT vjit, vsmp;
298 RAY sr;
299 int ntrials;
300 /* multiple samples? */
301 if (specjitter > 1.5) {
302 nstarget = specjitter*ndp->pr->rweight + .5;
303 if (nstarget < 1)
304 nstarget = 1;
305 }
306 /* run through our trials */
307 for (ntrials = 0; nsent < nstarget && ntrials < 9*nstarget; ntrials++) {
308 SDerrorDetail[0] = '\0';
309 /* sample direction & coef. */
310 bsdf_jitter(vjit, ndp);
311 ec = SDsampComponent(&bsv, vsmp, vjit, ntrials ? frandom()
312 : urand(ilhash(dimlist,ndims)+samplendx), dcp);
313 if (ec)
314 objerror(ndp->mp, USER, transSDError(ec));
315 /* zero component? */
316 if (bsv.cieY <= FTINY)
317 break;
318 /* map vector to world */
319 if (SDmapDir(sr.rdir, ndp->fromloc, vsmp) != SDEnone)
320 break;
321 /* unintentional penetration? */
322 if (DOT(sr.rdir, ndp->pr->ron) > .0 ^ vsmp[2] > .0)
323 continue;
324 /* spawn a specular ray */
325 if (nstarget > 1)
326 bsv.cieY /= (double)nstarget;
327 cvt_sdcolor(sr.rcoef, &bsv); /* use color */
328 if (usepat) /* pattern on transmission */
329 multcolor(sr.rcoef, ndp->pr->pcol);
330 if (rayorigin(&sr, SPECULAR, ndp->pr, sr.rcoef) < 0) {
331 if (maxdepth > 0)
332 break;
333 ++nsent; /* Russian roulette victim */
334 continue;
335 }
336 /* need to offset origin? */
337 if (ndp->thick != .0 && ndp->pr->rod > .0 ^ vsmp[2] > .0)
338 VSUM(sr.rorg, sr.rorg, ndp->pr->ron, -ndp->thick);
339 rayvalue(&sr); /* send & evaluate sample */
340 multcolor(sr.rcol, sr.rcoef);
341 addcolor(ndp->pr->rcol, sr.rcol);
342 ++nsent;
343 }
344 return(nsent);
345 }
346
347 /* Sample non-diffuse components of BSDF */
348 static int
349 sample_sdf(BSDFDAT *ndp, int sflags)
350 {
351 int n, ntotal = 0;
352 SDSpectralDF *dfp;
353 COLORV *unsc;
354
355 if (sflags == SDsampSpT) {
356 unsc = ndp->tunsamp;
357 dfp = ndp->sd->tf;
358 cvt_sdcolor(unsc, &ndp->sd->tLamb);
359 } else /* sflags == SDsampSpR */ {
360 unsc = ndp->runsamp;
361 if (ndp->pr->rod > .0) {
362 dfp = ndp->sd->rf;
363 cvt_sdcolor(unsc, &ndp->sd->rLambFront);
364 } else {
365 dfp = ndp->sd->rb;
366 cvt_sdcolor(unsc, &ndp->sd->rLambBack);
367 }
368 }
369 multcolor(unsc, ndp->pr->pcol);
370 if (dfp == NULL) /* no specular component? */
371 return(0);
372 /* below sampling threshold? */
373 if (dfp->maxHemi <= specthresh+FTINY) {
374 if (dfp->maxHemi > FTINY) { /* XXX no color from BSDF */
375 FVECT vjit;
376 double d;
377 COLOR ctmp;
378 bsdf_jitter(vjit, ndp);
379 d = SDdirectHemi(vjit, sflags, ndp->sd);
380 if (sflags == SDsampSpT) {
381 copycolor(ctmp, ndp->pr->pcol);
382 scalecolor(ctmp, d);
383 } else /* no pattern on reflection */
384 setcolor(ctmp, d, d, d);
385 addcolor(unsc, ctmp);
386 }
387 return(0);
388 }
389 /* else need to sample */
390 dimlist[ndims++] = (int)(size_t)ndp->mp;
391 ndims++;
392 for (n = dfp->ncomp; n--; ) { /* loop over components */
393 dimlist[ndims-1] = n + 9438;
394 ntotal += sample_sdcomp(ndp, &dfp->comp[n], sflags==SDsampSpT);
395 }
396 ndims -= 2;
397 return(ntotal);
398 }
399
400 /* Color a ray that hit a BSDF material */
401 int
402 m_bsdf(OBJREC *m, RAY *r)
403 {
404 int hitfront;
405 COLOR ctmp;
406 SDError ec;
407 FVECT upvec, vtmp;
408 MFUNC *mf;
409 BSDFDAT nd;
410 /* check arguments */
411 if ((m->oargs.nsargs < 6) | (m->oargs.nfargs > 9) |
412 (m->oargs.nfargs % 3))
413 objerror(m, USER, "bad # arguments");
414 /* record surface struck */
415 hitfront = (r->rod > .0);
416 /* load cal file */
417 mf = getfunc(m, 5, 0x1d, 1);
418 /* get thickness */
419 nd.thick = evalue(mf->ep[0]);
420 if ((-FTINY <= nd.thick) & (nd.thick <= FTINY))
421 nd.thick = .0;
422 /* check shadow */
423 if (r->crtype & SHADOW) {
424 if (nd.thick != .0)
425 raytrans(r); /* pass-through */
426 return(1); /* or shadow */
427 }
428 /* check other rays to pass */
429 if (nd.thick != 0 && (!(r->crtype & (SPECULAR|AMBIENT)) ||
430 nd.thick > .0 ^ hitfront)) {
431 raytrans(r); /* hide our proxy */
432 return(1);
433 }
434 /* get BSDF data */
435 nd.sd = loadBSDF(m->oargs.sarg[1]);
436 /* diffuse reflectance */
437 if (hitfront) {
438 if (m->oargs.nfargs < 3)
439 setcolor(nd.rdiff, .0, .0, .0);
440 else
441 setcolor(nd.rdiff, m->oargs.farg[0],
442 m->oargs.farg[1],
443 m->oargs.farg[2]);
444 } else {
445 if (m->oargs.nfargs < 6) { /* check invisible backside */
446 if (!backvis && (nd.sd->rb == NULL) &
447 (nd.sd->tf == NULL)) {
448 SDfreeCache(nd.sd);
449 raytrans(r);
450 return(1);
451 }
452 setcolor(nd.rdiff, .0, .0, .0);
453 } else
454 setcolor(nd.rdiff, m->oargs.farg[3],
455 m->oargs.farg[4],
456 m->oargs.farg[5]);
457 }
458 /* diffuse transmittance */
459 if (m->oargs.nfargs < 9)
460 setcolor(nd.tdiff, .0, .0, .0);
461 else
462 setcolor(nd.tdiff, m->oargs.farg[6],
463 m->oargs.farg[7],
464 m->oargs.farg[8]);
465 nd.mp = m;
466 nd.pr = r;
467 /* get modifiers */
468 raytexture(r, m->omod);
469 /* modify diffuse values */
470 multcolor(nd.rdiff, r->pcol);
471 multcolor(nd.tdiff, r->pcol);
472 /* get up vector */
473 upvec[0] = evalue(mf->ep[1]);
474 upvec[1] = evalue(mf->ep[2]);
475 upvec[2] = evalue(mf->ep[3]);
476 /* return to world coords */
477 if (mf->f != &unitxf) {
478 multv3(upvec, upvec, mf->f->xfm);
479 nd.thick *= mf->f->sca;
480 }
481 raynormal(nd.pnorm, r);
482 /* compute local BSDF xform */
483 ec = SDcompXform(nd.toloc, nd.pnorm, upvec);
484 if (!ec) {
485 nd.vray[0] = -r->rdir[0];
486 nd.vray[1] = -r->rdir[1];
487 nd.vray[2] = -r->rdir[2];
488 ec = SDmapDir(nd.vray, nd.toloc, nd.vray);
489 }
490 if (!ec)
491 ec = SDinvXform(nd.fromloc, nd.toloc);
492 /* determine BSDF resolution */
493 if (!ec)
494 ec = SDsizeBSDF(&nd.sr_vpsa, nd.vray, SDqueryMin, nd.sd);
495 if (!ec)
496 nd.sr_vpsa = sqrt(nd.sr_vpsa);
497 else {
498 objerror(m, WARNING, transSDError(ec));
499 SDfreeCache(nd.sd);
500 return(1);
501 }
502 if (!hitfront) { /* perturb normal towards hit */
503 nd.pnorm[0] = -nd.pnorm[0];
504 nd.pnorm[1] = -nd.pnorm[1];
505 nd.pnorm[2] = -nd.pnorm[2];
506 }
507 /* sample reflection */
508 sample_sdf(&nd, SDsampSpR);
509 /* sample transmission */
510 sample_sdf(&nd, SDsampSpT);
511 /* compute indirect diffuse */
512 copycolor(ctmp, nd.rdiff);
513 addcolor(ctmp, nd.runsamp);
514 if (bright(ctmp) > FTINY) { /* ambient from reflection */
515 if (!hitfront)
516 flipsurface(r);
517 multambient(ctmp, r, nd.pnorm);
518 addcolor(r->rcol, ctmp);
519 if (!hitfront)
520 flipsurface(r);
521 }
522 copycolor(ctmp, nd.tdiff);
523 addcolor(ctmp, nd.tunsamp);
524 if (bright(ctmp) > FTINY) { /* ambient from other side */
525 FVECT bnorm;
526 if (hitfront)
527 flipsurface(r);
528 bnorm[0] = -nd.pnorm[0];
529 bnorm[1] = -nd.pnorm[1];
530 bnorm[2] = -nd.pnorm[2];
531 if (nd.thick != .0) { /* proxy with offset? */
532 VCOPY(vtmp, r->rop);
533 VSUM(r->rop, vtmp, r->ron, -nd.thick);
534 multambient(ctmp, r, bnorm);
535 VCOPY(r->rop, vtmp);
536 } else
537 multambient(ctmp, r, bnorm);
538 addcolor(r->rcol, ctmp);
539 if (hitfront)
540 flipsurface(r);
541 }
542 /* add direct component */
543 if ((bright(nd.tdiff) <= FTINY) & (nd.sd->tf == NULL)) {
544 direct(r, dir_brdf, &nd); /* reflection only */
545 } else if (nd.thick == .0) {
546 direct(r, dir_bsdf, &nd); /* thin surface scattering */
547 } else {
548 direct(r, dir_brdf, &nd); /* reflection first */
549 VCOPY(vtmp, r->rop); /* offset for transmitted */
550 VSUM(r->rop, vtmp, r->ron, -nd.thick);
551 direct(r, dir_btdf, &nd); /* separate transmission */
552 VCOPY(r->rop, vtmp);
553 }
554 /* clean up */
555 SDfreeCache(nd.sd);
556 return(1);
557 }