ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.19
Committed: Mon Aug 10 19:51:20 2020 UTC (3 years, 8 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R3
Changes since 2.18: +26 -20 lines
Log Message:
fix(mkpmap): undefined port in setPhotonPortNormal()

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pmapsrc.c,v 2.18 2020/08/07 01:21:13 rschregle Exp $";
3 #endif
4 /*
5 ======================================================================
6 Photon map support routines for emission from light sources
7
8 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
9 (c) Fraunhofer Institute for Solar Energy Systems,
10 supported by the German Research Foundation (DFG)
11 under the FARESYS project.
12 (c) Lucerne University of Applied Sciences and Arts,
13 supported by the Swiss National Science Foundation (SNSF #147053).
14 (c) Tokyo University of Science,
15 supported by the JSPS KAKENHI Grant Number JP19KK0115.
16 ======================================================================
17
18 $Id: pmapsrc.c,v 2.18 2020/08/07 01:21:13 rschregle Exp $"
19 */
20
21
22
23 #include "pmapsrc.h"
24 #include "pmap.h"
25 #include "pmaprand.h"
26 #include "otypes.h"
27 #include "otspecial.h"
28
29
30
31 /* List of photon port modifier names */
32 char *photonPortList [MAXSET + 1] = {NULL};
33 /* Photon port objects (with modifiers in photonPortMods) */
34 SRCREC *photonPorts = NULL;
35 unsigned numPhotonPorts = 0;
36
37 void (*photonPartition [NUMOTYPE]) (EmissionMap*);
38 void (*photonOrigin [NUMOTYPE]) (EmissionMap*);
39
40
41
42 /* PHOTON PORT SUPPORT ROUTINES ------------------------------------------ */
43
44
45
46 /* Get/set photon port orientation flags from/in source flags.
47 * HACK: the port orientation flags are embedded in the source flags and
48 * shifted so they won't clobber the latter, since these are interpreted
49 * by the *PhotonPartition() and *PhotonOrigin() routines! */
50 #define PMAP_SETPORTFLAGS(portdir) ((int)(portdir) << 14)
51 #define PMAP_GETPORTFLAGS(sflags) ((sflags) >> 14)
52
53 /* Set number of source partitions.
54 * HACK: this is doubled if the source acts as a bidirectionally
55 * emitting photon port, resulting in alternating front/backside partitions,
56 * although essentially each partition is just used twice with opposing
57 * normals. */
58 #define PMAP_SETNUMPARTITIONS(emap) ( \
59 (emap) -> numPartitions <<= ( \
60 (emap) -> port && \
61 PMAP_GETPORTFLAGS((emap) -> port -> sflags) == PMAP_PORTBI \
62 ) \
63 )
64
65 /* Get current source partition and numer of partitions
66 * HACK: halve the number partitions if the source acts as a bidrectionally
67 * emitting photon port, since each partition is used twice with opposing
68 * normals. */
69 #define PMAP_GETNUMPARTITIONS(emap) (\
70 (emap) -> numPartitions >> ( \
71 (emap) -> port && \
72 PMAP_GETPORTFLAGS((emap) -> port -> sflags) == PMAP_PORTBI \
73 ) \
74 )
75 #define PMAP_GETPARTITION(emap) ( \
76 (emap) -> partitionCnt >> ( \
77 (emap) -> port && \
78 PMAP_GETPORTFLAGS((emap) -> port -> sflags) == PMAP_PORTBI \
79 ) \
80 )
81
82
83
84 void getPhotonPorts (char **portList)
85 /* Find geometry declared as photon ports from modifiers in portList */
86 {
87 OBJECT i;
88 OBJREC *obj, *mat;
89 int mLen;
90 char **lp;
91
92 /* Init photon port objects */
93 photonPorts = NULL;
94
95 if (!portList [0])
96 return;
97
98 for (i = numPhotonPorts = 0; i < nobjects; i++) {
99 obj = objptr(i);
100 mat = findmaterial(obj);
101
102 /* Check if object is a surface and NOT a light source (duh) and
103 * resolve its material (if any) via any aliases, then check for
104 * inclusion in modifier list; note use of strncmp() to ignore port
105 * flags */
106 if (issurface(obj -> otype) && mat && !islight(mat -> otype)) {
107 mLen = strlen(mat -> oname);
108 for (lp = portList; *lp && strncmp(mat -> oname, *lp, mLen); lp++);
109
110 if (*lp) {
111 /* Add photon port */
112 photonPorts = (SRCREC*)realloc(
113 photonPorts, (numPhotonPorts + 1) * sizeof(SRCREC)
114 );
115 if (!photonPorts)
116 error(USER, "can't allocate photon ports");
117
118 photonPorts [numPhotonPorts].so = obj;
119 /* Extract port orientation flags and embed in source flags.
120 * Note setsource() combines (i.e. ORs) these with the actual
121 * source flags below. */
122 photonPorts [numPhotonPorts].sflags =
123 PMAP_SETPORTFLAGS((*lp) [mLen]);
124
125 if (!sfun [obj -> otype].of || !sfun[obj -> otype].of -> setsrc)
126 objerror(obj, USER, "illegal photon port");
127
128 setsource(photonPorts + numPhotonPorts, obj);
129 numPhotonPorts++;
130 }
131 }
132 }
133 if (!numPhotonPorts)
134 error(USER, "no valid photon ports found");
135 }
136
137
138
139 static void setPhotonPortNormal (EmissionMap* emap)
140 /* Set normal for current photon port partition (if defined) based on its
141 * orientation */
142 {
143
144 int i, portFlags;
145
146 if (emap -> port) {
147 /* Extract photon port orientation flags, set surface normal as follows:
148 -- Port oriented forwards --> flip surface normal to point outwards,
149 since normal points inwards per mkillum convention)
150 -- Port oriented backwards --> surface normal is NOT flipped, since
151 it already points inwards.
152 -- Port is bidirectionally/bilaterally oriented --> flip normal based
153 on the parity of the current partition emap -> partitionCnt. In
154 this case, photon emission alternates between port front/back
155 faces for consecutive partitions.
156 */
157 portFlags = PMAP_GETPORTFLAGS(emap -> port -> sflags);
158
159 if (
160 portFlags == PMAP_PORTFWD ||
161 portFlags == PMAP_PORTBI && !(emap -> partitionCnt & 1)
162 )
163 for (i = 0; i < 3; i++)
164 emap -> ws [i] = -emap -> ws [i];
165 }
166 }
167
168
169
170 /* SOURCE / PHOTON PORT PARTITIONING ROUTINES----------------------------- */
171
172
173
174 static int flatPhotonPartition2 (
175 EmissionMap* emap, unsigned long mp, FVECT cent, FVECT u, FVECT v,
176 double du2, double dv2
177 )
178 /* Recursive part of flatPhotonPartition(..) */
179 {
180 FVECT newct, newax;
181 unsigned long npl, npu;
182
183 if (mp > emap -> maxPartitions) {
184 /* Enlarge partition array */
185 emap -> maxPartitions <<= 1;
186 emap -> partitions = (unsigned char*)realloc(
187 emap -> partitions, emap -> maxPartitions >> 1
188 );
189
190 if (!emap -> partitions)
191 error(USER, "can't allocate source partitions");
192
193 memset(
194 emap -> partitions + (emap -> maxPartitions >> 2), 0,
195 emap -> maxPartitions >> 2
196 );
197 }
198
199 if (du2 * dv2 <= 1) { /* hit limit? */
200 setpart(emap -> partitions, emap -> partitionCnt, S0);
201 emap -> partitionCnt++;
202 return 1;
203 }
204
205 if (du2 > dv2) { /* subdivide in U */
206 setpart(emap -> partitions, emap -> partitionCnt, SU);
207 emap -> partitionCnt++;
208 newax [0] = 0.5 * u [0];
209 newax [1] = 0.5 * u [1];
210 newax [2] = 0.5 * u [2];
211 u = newax;
212 du2 *= 0.25;
213 }
214
215 else { /* subdivide in V */
216 setpart(emap -> partitions, emap -> partitionCnt, SV);
217 emap -> partitionCnt++;
218 newax [0] = 0.5 * v [0];
219 newax [1] = 0.5 * v [1];
220 newax [2] = 0.5 * v [2];
221 v = newax;
222 dv2 *= 0.25;
223 }
224
225 /* lower half */
226 newct [0] = cent [0] - newax [0];
227 newct [1] = cent [1] - newax [1];
228 newct [2] = cent [2] - newax [2];
229 npl = flatPhotonPartition2(emap, mp << 1, newct, u, v, du2, dv2);
230
231 /* upper half */
232 newct [0] = cent [0] + newax [0];
233 newct [1] = cent [1] + newax [1];
234 newct [2] = cent [2] + newax [2];
235 npu = flatPhotonPartition2(emap, mp << 1, newct, u, v, du2, dv2);
236
237 /* return total */
238 return npl + npu;
239 }
240
241
242
243 static void flatPhotonPartition (EmissionMap* emap)
244 /* Partition flat source for photon emission */
245 {
246 RREAL *vp;
247 double du2, dv2;
248
249 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
250 emap -> partArea = srcsizerat * thescene.cusize;
251 emap -> partArea *= emap -> partArea;
252 vp = emap -> src -> ss [SU];
253 du2 = DOT(vp, vp) / emap -> partArea;
254 vp = emap -> src -> ss [SV];
255 dv2 = DOT(vp, vp) / emap -> partArea;
256 emap -> partitionCnt = 0;
257 emap -> numPartitions = flatPhotonPartition2(
258 emap, 1, emap -> src -> sloc,
259 emap -> src -> ss [SU], emap -> src -> ss [SV], du2, dv2
260 );
261 emap -> partitionCnt = 0;
262 emap -> partArea = emap -> src -> ss2 / emap -> numPartitions;
263 }
264
265
266
267 static void sourcePhotonPartition (EmissionMap* emap)
268 /* Partition scene cube faces or photon port for photon emission from
269 distant source */
270 {
271 if (emap -> port) {
272 /* Relay partitioning to photon port */
273 SRCREC *src = emap -> src;
274 emap -> src = emap -> port;
275 photonPartition [emap -> src -> so -> otype] (emap);
276 PMAP_SETNUMPARTITIONS(emap);
277 emap -> src = src;
278 }
279
280 else {
281 /* No photon ports defined; partition scene cube faces (SUBOPTIMAL) */
282 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
283 setpart(emap -> partitions, 0, S0);
284 emap -> partitionCnt = 0;
285 emap -> numPartitions = 1 / srcsizerat;
286 emap -> numPartitions *= emap -> numPartitions;
287 emap -> partArea = sqr(thescene.cusize) / emap -> numPartitions;
288 emap -> numPartitions *= 6;
289 }
290 }
291
292
293
294 static void spherePhotonPartition (EmissionMap* emap)
295 /* Partition spherical source into equal solid angles using uniform
296 mapping for photon emission */
297 {
298 unsigned numTheta, numPhi;
299
300 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
301 setpart(emap -> partitions, 0, S0);
302 emap -> partArea = 4 * PI * sqr(emap -> src -> srad);
303 emap -> numPartitions =
304 emap -> partArea / sqr(srcsizerat * thescene.cusize);
305
306 numTheta = max(sqrt(2 * emap -> numPartitions / PI) + 0.5, 1);
307 numPhi = 0.5 * PI * numTheta + 0.5;
308
309 emap -> numPartitions = (unsigned long)numTheta * numPhi;
310 emap -> partitionCnt = 0;
311 emap -> partArea /= emap -> numPartitions;
312 }
313
314
315
316 static int cylPhotonPartition2 (
317 EmissionMap* emap, unsigned long mp, FVECT cent, FVECT axis, double d2
318 )
319 /* Recursive part of cyPhotonPartition(..) */
320 {
321 FVECT newct, newax;
322 unsigned long npl, npu;
323
324 if (mp > emap -> maxPartitions) {
325 /* Enlarge partition array */
326 emap -> maxPartitions <<= 1;
327 emap -> partitions = (unsigned char*)realloc(
328 emap -> partitions, emap -> maxPartitions >> 1
329 );
330 if (!emap -> partitions)
331 error(USER, "can't allocate source partitions");
332
333 memset(
334 emap -> partitions + (emap -> maxPartitions >> 2), 0,
335 emap -> maxPartitions >> 2
336 );
337 }
338
339 if (d2 <= 1) {
340 /* hit limit? */
341 setpart(emap -> partitions, emap -> partitionCnt, S0);
342 emap -> partitionCnt++;
343 return 1;
344 }
345
346 /* subdivide */
347 setpart(emap -> partitions, emap -> partitionCnt, SU);
348 emap -> partitionCnt++;
349 newax [0] = 0.5 * axis [0];
350 newax [1] = 0.5 * axis [1];
351 newax [2] = 0.5 * axis [2];
352 d2 *= 0.25;
353
354 /* lower half */
355 newct [0] = cent [0] - newax [0];
356 newct [1] = cent [1] - newax [1];
357 newct [2] = cent [2] - newax [2];
358 npl = cylPhotonPartition2(emap, mp << 1, newct, newax, d2);
359
360 /* upper half */
361 newct [0] = cent [0] + newax [0];
362 newct [1] = cent [1] + newax [1];
363 newct [2] = cent [2] + newax [2];
364 npu = cylPhotonPartition2(emap, mp << 1, newct, newax, d2);
365
366 /* return total */
367 return npl + npu;
368 }
369
370
371
372 static void cylPhotonPartition (EmissionMap* emap)
373 /* Partition cylindrical source for photon emission */
374 {
375 double d2;
376
377 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
378 d2 = srcsizerat * thescene.cusize;
379 d2 = PI * emap -> src -> ss2 / (2 * emap -> src -> srad * sqr(d2));
380 d2 *= d2 * DOT(emap -> src -> ss [SU], emap -> src -> ss [SU]);
381
382 emap -> partitionCnt = 0;
383 emap -> numPartitions = cylPhotonPartition2(
384 emap, 1, emap -> src -> sloc, emap -> src -> ss [SU], d2
385 );
386 emap -> partitionCnt = 0;
387 emap -> partArea = PI * emap -> src -> ss2 / emap -> numPartitions;
388 }
389
390
391
392 /* PHOTON ORIGIN ROUTINES ------------------------------------------------ */
393
394
395
396 static void flatPhotonOrigin (EmissionMap* emap)
397 /* Init emission map with photon origin and associated surface axes on
398 flat light source surface. Also sets source aperture and sampling
399 hemisphere axes at origin */
400 {
401 int i, cent[3], size[3], parr[2];
402 FVECT vpos;
403
404 cent [0] = cent [1] = cent [2] = 0;
405 size [0] = size [1] = size [2] = emap -> maxPartitions;
406 parr [0] = 0;
407 parr [1] = PMAP_GETPARTITION(emap);
408
409 if (!skipparts(cent, size, parr, emap -> partitions))
410 error(CONSISTENCY, "bad source partition in flatPhotonOrigin");
411
412 vpos [0] = (1 - 2 * pmapRandom(partState)) * size [0] /
413 emap -> maxPartitions;
414 vpos [1] = (1 - 2 * pmapRandom(partState)) * size [1] /
415 emap -> maxPartitions;
416 vpos [2] = 0;
417
418 for (i = 0; i < 3; i++)
419 vpos [i] += (double)cent [i] / emap -> maxPartitions;
420
421 /* Get origin */
422 for (i = 0; i < 3; i++)
423 emap -> photonOrg [i] =
424 emap -> src -> sloc [i] +
425 vpos [SU] * emap -> src -> ss [SU][i] +
426 vpos [SV] * emap -> src -> ss [SV][i] +
427 vpos [SW] * emap -> src -> ss [SW][i];
428
429 /* Get surface axes */
430 VCOPY(emap -> us, emap -> src -> ss [SU]);
431 normalize(emap -> us);
432 VCOPY(emap -> ws, emap -> src -> ss [SW]);
433 /* Flip normal emap -> ws if port and required by its orientation */
434 setPhotonPortNormal(emap);
435 fcross(emap -> vs, emap -> ws, emap -> us);
436
437 /* Get hemisphere axes & aperture */
438 if (emap -> src -> sflags & SSPOT) {
439 VCOPY(emap -> wh, emap -> src -> sl.s -> aim);
440 i = 0;
441
442 do {
443 emap -> vh [0] = emap -> vh [1] = emap -> vh [2] = 0;
444 emap -> vh [i++] = 1;
445 fcross(emap -> uh, emap -> vh, emap -> wh);
446 } while (normalize(emap -> uh) < FTINY);
447
448 fcross(emap -> vh, emap -> wh, emap -> uh);
449 emap -> cosThetaMax = 1 - emap -> src -> sl.s -> siz / (2 * PI);
450 }
451
452 else {
453 VCOPY(emap -> uh, emap -> us);
454 VCOPY(emap -> vh, emap -> vs);
455 VCOPY(emap -> wh, emap -> ws);
456 emap -> cosThetaMax = 0;
457 }
458 }
459
460
461
462 static void spherePhotonOrigin (EmissionMap* emap)
463 /* Init emission map with photon origin and associated surface axes on
464 spherical light source. Also sets source aperture and sampling
465 hemisphere axes at origin */
466 {
467 int i = 0;
468 unsigned numTheta, numPhi, t, p;
469 RREAL cosTheta, sinTheta, phi;
470
471 /* Get current partition */
472 numTheta = max(sqrt(2 * PMAP_GETNUMPARTITIONS(emap) / PI) + 0.5, 1);
473 numPhi = 0.5 * PI * numTheta + 0.5;
474
475 t = PMAP_GETPARTITION(emap) / numPhi;
476 p = PMAP_GETPARTITION(emap) - t * numPhi;
477
478 emap -> ws [2] = cosTheta = 1 - 2 * (t + pmapRandom(partState)) / numTheta;
479 sinTheta = sqrt(1 - sqr(cosTheta));
480 phi = 2 * PI * (p + pmapRandom(partState)) / numPhi;
481 emap -> ws [0] = cos(phi) * sinTheta;
482 emap -> ws [1] = sin(phi) * sinTheta;
483 /* Flip normal emap -> ws if port and required by its orientation */
484 setPhotonPortNormal(emap);
485
486 /* Get surface axes us & vs perpendicular to ws */
487 do {
488 emap -> vs [0] = emap -> vs [1] = emap -> vs [2] = 0;
489 emap -> vs [i++] = 1;
490 fcross(emap -> us, emap -> vs, emap -> ws);
491 } while (normalize(emap -> us) < FTINY);
492
493 fcross(emap -> vs, emap -> ws, emap -> us);
494
495 /* Get origin */
496 for (i = 0; i < 3; i++)
497 emap -> photonOrg [i] = emap -> src -> sloc [i] +
498 emap -> src -> srad * emap -> ws [i];
499
500 /* Get hemisphere axes & aperture */
501 if (emap -> src -> sflags & SSPOT) {
502 VCOPY(emap -> wh, emap -> src -> sl.s -> aim);
503 i = 0;
504
505 do {
506 emap -> vh [0] = emap -> vh [1] = emap -> vh [2] = 0;
507 emap -> vh [i++] = 1;
508 fcross(emap -> uh, emap -> vh, emap -> wh);
509 } while (normalize(emap -> uh) < FTINY);
510
511 fcross(emap -> vh, emap -> wh, emap -> uh);
512 emap -> cosThetaMax = 1 - emap -> src -> sl.s -> siz / (2 * PI);
513 }
514
515 else {
516 VCOPY(emap -> uh, emap -> us);
517 VCOPY(emap -> vh, emap -> vs);
518 VCOPY(emap -> wh, emap -> ws);
519 emap -> cosThetaMax = 0;
520 }
521 }
522
523
524
525 static void sourcePhotonOrigin (EmissionMap* emap)
526 /* Init emission map with photon origin and associated surface axes
527 on scene cube face for distant light source. Also sets source
528 aperture (solid angle) and sampling hemisphere axes at origin */
529 {
530 unsigned long i, partsPerDim, partsPerFace;
531 unsigned face;
532 RREAL du, dv;
533
534 if (emap -> port) {
535 /* Relay to photon port; get origin on its surface */
536 SRCREC *src = emap -> src;
537 emap -> src = emap -> port;
538 photonOrigin [emap -> src -> so -> otype] (emap);
539 emap -> src = src;
540 }
541
542 else {
543 /* No ports defined, so get origin on scene cube face (SUBOPTIMAL) */
544 /* Get current face from partition number */
545 partsPerDim = 1 / srcsizerat;
546 partsPerFace = sqr(partsPerDim);
547 face = emap -> partitionCnt / partsPerFace;
548 if (!(emap -> partitionCnt % partsPerFace)) {
549 /* Skipped to a new face; get its normal */
550 emap -> ws [0] = emap -> ws [1] = emap -> ws [2] = 0;
551 emap -> ws [face >> 1] = face & 1 ? 1 : -1;
552
553 /* Get surface axes us & vs perpendicular to ws */
554 face >>= 1;
555 emap -> vs [0] = emap -> vs [1] = emap -> vs [2] = 0;
556 emap -> vs [(face + (emap -> ws [face] > 0 ? 2 : 1)) % 3] = 1;
557 fcross(emap -> us, emap -> vs, emap -> ws);
558 }
559
560 /* Get jittered offsets within face from partition number
561 (in range [-0.5, 0.5]) */
562 i = emap -> partitionCnt % partsPerFace;
563 du = (i / partsPerDim + pmapRandom(partState)) / partsPerDim - 0.5;
564 dv = (i % partsPerDim + pmapRandom(partState)) / partsPerDim - 0.5;
565
566 /* Jittered destination point within partition */
567 for (i = 0; i < 3; i++)
568 emap -> photonOrg [i] = thescene.cuorg [i] + thescene.cusize * (
569 0.5 + du * emap -> us [i] + dv * emap -> vs [i] +
570 0.5 * emap -> ws [i]
571 );
572 }
573
574 /* Get hemisphere axes & aperture */
575 VCOPY(emap -> wh, emap -> src -> sloc);
576 i = 0;
577
578 do {
579 emap -> vh [0] = emap -> vh [1] = emap -> vh [2] = 0;
580 emap -> vh [i++] = 1;
581 fcross(emap -> uh, emap -> vh, emap -> wh);
582 } while (normalize(emap -> uh) < FTINY);
583
584 fcross(emap -> vh, emap -> wh, emap -> uh);
585
586 /* Get aperture */
587 emap -> cosThetaMax = 1 - emap -> src -> ss2 / (2 * PI);
588 emap -> cosThetaMax = min(1, max(-1, emap -> cosThetaMax));
589 }
590
591
592
593 static void cylPhotonOrigin (EmissionMap* emap)
594 /* Init emission map with photon origin and associated surface axes
595 on cylindrical light source surface. Also sets source aperture
596 and sampling hemisphere axes at origin */
597 {
598 int i, cent[3], size[3], parr[2];
599 FVECT v;
600
601 cent [0] = cent [1] = cent [2] = 0;
602 size [0] = size [1] = size [2] = emap -> maxPartitions;
603 parr [0] = 0;
604 parr [1] = PMAP_GETPARTITION(emap);
605
606 if (!skipparts(cent, size, parr, emap -> partitions))
607 error(CONSISTENCY, "bad source partition in cylPhotonOrigin");
608
609 v [SU] = 0;
610 v [SV] = (1 - 2 * pmapRandom(partState)) * (double)size [1] /
611 emap -> maxPartitions;
612 v [SW] = (1 - 2 * pmapRandom(partState)) * (double)size [2] /
613 emap -> maxPartitions;
614 normalize(v);
615 v [SU] = (1 - 2 * pmapRandom(partState)) * (double)size [1] /
616 emap -> maxPartitions;
617
618 for (i = 0; i < 3; i++)
619 v [i] += (double)cent [i] / emap -> maxPartitions;
620
621 /* Get surface axes */
622 for (i = 0; i < 3; i++)
623 emap -> photonOrg [i] = emap -> ws [i] = (
624 v [SV] * emap -> src -> ss [SV][i] +
625 v [SW] * emap -> src -> ss [SW][i]
626 ) / 0.8559;
627
628 /* Flip normal emap -> ws if port and required by its orientation */
629 setPhotonPortNormal(emap);
630
631 normalize(emap -> ws);
632 VCOPY(emap -> us, emap -> src -> ss [SU]);
633 normalize(emap -> us);
634 fcross(emap -> vs, emap -> ws, emap -> us);
635
636 /* Get origin */
637 for (i = 0; i < 3; i++)
638 emap -> photonOrg [i] +=
639 v [SU] * emap -> src -> ss [SU][i] + emap -> src -> sloc [i];
640
641 /* Get hemisphere axes & aperture */
642 if (emap -> src -> sflags & SSPOT) {
643 VCOPY(emap -> wh, emap -> src -> sl.s -> aim);
644 i = 0;
645
646 do {
647 emap -> vh [0] = emap -> vh [1] = emap -> vh [2] = 0;
648 emap -> vh [i++] = 1;
649 fcross(emap -> uh, emap -> vh, emap -> wh);
650 } while (normalize(emap -> uh) < FTINY);
651
652 fcross(emap -> vh, emap -> wh, emap -> uh);
653 emap -> cosThetaMax = 1 - emap -> src -> sl.s -> siz / (2 * PI);
654 }
655
656 else {
657 VCOPY(emap -> uh, emap -> us);
658 VCOPY(emap -> vh, emap -> vs);
659 VCOPY(emap -> wh, emap -> ws);
660 emap -> cosThetaMax = 0;
661 }
662 }
663
664
665
666 /* PHOTON EMISSION ROUTINES ---------------------------------------------- */
667
668
669
670 static void defaultEmissionFunc (EmissionMap* emap)
671 /* Default behaviour when no emission funcs defined for this source type */
672 {
673 objerror(emap -> src -> so, INTERNAL,
674 "undefined photon emission function");
675 }
676
677
678
679 void initPhotonEmissionFuncs ()
680 /* Init photonPartition[] and photonOrigin[] dispatch tables */
681 {
682 int i;
683
684 for (i = 0; i < NUMOTYPE; i++)
685 photonPartition [i] = photonOrigin [i] = defaultEmissionFunc;
686
687 photonPartition [OBJ_FACE] = photonPartition [OBJ_RING] = flatPhotonPartition;
688 photonPartition [OBJ_SOURCE] = sourcePhotonPartition;
689 photonPartition [OBJ_SPHERE] = spherePhotonPartition;
690 photonPartition [OBJ_CYLINDER] = cylPhotonPartition;
691 photonOrigin [OBJ_FACE] = photonOrigin [OBJ_RING] = flatPhotonOrigin;
692 photonOrigin [OBJ_SOURCE] = sourcePhotonOrigin;
693 photonOrigin [OBJ_SPHERE] = spherePhotonOrigin;
694 photonOrigin [OBJ_CYLINDER] = cylPhotonOrigin;
695 }
696
697
698
699 void initPhotonEmission (EmissionMap *emap, float numPdfSamples)
700 /* Initialise photon emission from partitioned light source emap -> src;
701 * this involves integrating the flux emitted from the current photon
702 * origin emap -> photonOrg and setting up a PDF to sample the emission
703 * distribution with numPdfSamples samples */
704 {
705 unsigned i, t, p;
706 double phi, cosTheta, sinTheta, du, dv, dOmega, thetaScale;
707 EmissionSample* sample;
708 const OBJREC* mod = findmaterial(emap -> src -> so);
709 static RAY r;
710
711 photonOrigin [emap -> src -> so -> otype] (emap);
712 setcolor(emap -> partFlux, 0, 0, 0);
713 emap -> cdf = 0;
714 emap -> numSamples = 0;
715 cosTheta = DOT(emap -> ws, emap -> wh);
716
717 if (cosTheta <= 0 && sqrt(1-sqr(cosTheta)) <= emap -> cosThetaMax + FTINY)
718 /* Aperture completely below surface; no emission from current origin */
719 return;
720
721 if (
722 mod -> omod == OVOID && !emap -> port && (
723 cosTheta >= 1 - FTINY || (
724 emap -> src -> sflags & SDISTANT &&
725 acos(cosTheta) + acos(emap -> cosThetaMax) <= 0.5 * PI
726 )
727 )
728 ) {
729 /* Source is unmodified and has no port (which requires testing for
730 occlusion), and is either local with normal aligned aperture or
731 distant with aperture above surface
732 --> get flux & PDF via analytical solution */
733 setcolor(
734 emap -> partFlux, mod -> oargs.farg [0], mod -> oargs.farg [1],
735 mod -> oargs.farg [2]
736 );
737
738 /* Multiply radiance by projected Omega * dA to get flux */
739 scalecolor(
740 emap -> partFlux,
741 PI * cosTheta * (1 - sqr(max(emap -> cosThetaMax, 0))) *
742 emap -> partArea
743 );
744 }
745
746 else {
747 /* Source is either modified, has a port, is local with off-normal
748 aperture, or distant with aperture partly below surface
749 --> get flux via numerical integration */
750 thetaScale = (1 - emap -> cosThetaMax);
751
752 /* Figga out numba of aperture samples for integration;
753 numTheta / numPhi ratio is 1 / PI */
754 du = sqrt(pdfSamples * 2 * thetaScale);
755 emap -> numTheta = max(du + 0.5, 1);
756 emap -> numPhi = max(PI * du + 0.5, 1);
757
758 dOmega = 2 * PI * thetaScale / (emap -> numTheta * emap -> numPhi);
759 thetaScale /= emap -> numTheta;
760
761 /* Allocate PDF, baby */
762 sample = emap -> samples = (EmissionSample*)realloc(
763 emap -> samples,
764 sizeof(EmissionSample) * emap -> numTheta * emap -> numPhi
765 );
766 if (!emap -> samples)
767 error(USER, "can't allocate emission PDF");
768
769 VCOPY(r.rorg, emap -> photonOrg);
770 VCOPY(r.rop, emap -> photonOrg);
771 r.rmax = 0;
772
773 for (t = 0; t < emap -> numTheta; t++) {
774 for (p = 0; p < emap -> numPhi; p++) {
775 /* This uniform mapping handles 0 <= thetaMax <= PI */
776 cosTheta = 1 - (t + pmapRandom(emitState)) * thetaScale;
777 sinTheta = sqrt(1 - sqr(cosTheta));
778 phi = 2 * PI * (p + pmapRandom(emitState)) / emap -> numPhi;
779 du = cos(phi) * sinTheta;
780 dv = sin(phi) * sinTheta;
781 rayorigin(&r, PRIMARY, NULL, NULL);
782
783 for (i = 0; i < 3; i++)
784 r.rdir [i] = (
785 du * emap -> uh [i] + dv * emap -> vh [i] +
786 cosTheta * emap -> wh [i]
787 );
788
789 /* Sample behind surface? */
790 VCOPY(r.ron, emap -> ws);
791 if ((r.rod = DOT(r.rdir, r.ron)) <= 0)
792 continue;
793
794 /* Get radiance emitted in this direction; to get flux we
795 multiply by cos(theta_surface), dOmega, and dA. Ray
796 is directed towards light source for raytexture(). */
797 if (!(emap -> src -> sflags & SDISTANT))
798 for (i = 0; i < 3; i++)
799 r.rdir [i] = -r.rdir [i];
800
801 /* Port occluded in this direction? */
802 if (emap -> port && localhit(&r, &thescene))
803 continue;
804
805 raytexture(&r, mod -> omod);
806 setcolor(
807 r.rcol, mod -> oargs.farg [0], mod -> oargs.farg [1],
808 mod -> oargs.farg [2]
809 );
810 multcolor(r.rcol, r.pcol);
811
812 /* Multiply by cos(theta_surface) */
813 scalecolor(r.rcol, r.rod);
814
815 /* Add PDF sample if nonzero; importance info for photon emission
816 * could go here... */
817 if (colorAvg(r.rcol)) {
818 copycolor(sample -> pdf, r.rcol);
819 sample -> cdf = emap -> cdf += colorAvg(sample -> pdf);
820 sample -> theta = t;
821 sample++ -> phi = p;
822 emap -> numSamples++;
823 addcolor(emap -> partFlux, r.rcol);
824 }
825 }
826 }
827
828 /* Multiply by dOmega * dA */
829 scalecolor(emap -> partFlux, dOmega * emap -> partArea);
830 }
831 }
832
833
834
835 #define vomitPhoton emitPhoton
836 #define bluarrrghPhoton vomitPhoton
837
838 void emitPhoton (const EmissionMap* emap, RAY* ray)
839 /* Emit photon from current partition emap -> partitionCnt based on
840 emission distribution. Returns new photon ray. */
841 {
842 unsigned long i, lo, hi;
843 const EmissionSample* sample = emap -> samples;
844 RREAL du, dv, cosTheta, cosThetaSqr, sinTheta, phi;
845 const OBJREC* mod = findmaterial(emap -> src -> so);
846
847 /* Choose a new origin within current partition for every
848 emitted photon to break up clustering artifacts */
849 photonOrigin [emap -> src -> so -> otype] ((EmissionMap*)emap);
850 /* If we have a local glow source with a maximum radius, then
851 restrict our photon to the specified distance, otherwise we set
852 the limit imposed by photonMaxDist (or no limit if 0) */
853 if (
854 mod -> otype == MAT_GLOW &&
855 !(emap -> src -> sflags & SDISTANT) && mod -> oargs.farg[3] > FTINY
856 )
857 ray -> rmax = mod -> oargs.farg[3];
858 else
859 ray -> rmax = photonMaxDist;
860 rayorigin(ray, PRIMARY, NULL, NULL);
861
862 if (!emap -> numSamples) {
863 /* Source is unmodified and has no port, and either local with
864 normal aligned aperture, or distant with aperture above surface
865 --> use cosine weighted distribution */
866 cosThetaSqr = (1 -
867 pmapRandom(emitState) * (1 - sqr(max(emap -> cosThetaMax, 0)))
868 );
869 cosTheta = sqrt(cosThetaSqr);
870 sinTheta = sqrt(1 - cosThetaSqr);
871 phi = 2 * PI * pmapRandom(emitState);
872 setcolor(
873 ray -> rcol, mod -> oargs.farg [0], mod -> oargs.farg [1],
874 mod -> oargs.farg [2]
875 );
876 }
877
878 else {
879 /* Source is either modified, has a port, is local with off-normal
880 aperture, or distant with aperture partly below surface
881 --> choose direction from constructed cumulative distribution
882 function with Monte Carlo inversion using binary search. */
883 du = pmapRandom(emitState) * emap -> cdf;
884 lo = 1;
885 hi = emap -> numSamples;
886
887 while (hi > lo) {
888 i = (lo + hi) >> 1;
889 sample = emap -> samples + i - 1;
890
891 if (sample -> cdf >= du)
892 hi = i;
893 if (sample -> cdf < du)
894 lo = i + 1;
895 }
896
897 /* This is a uniform mapping, mon */
898 cosTheta = (1 -
899 (sample -> theta + pmapRandom(emitState)) *
900 (1 - emap -> cosThetaMax) / emap -> numTheta
901 );
902 sinTheta = sqrt(1 - sqr(cosTheta));
903 phi = 2 * PI * (sample -> phi + pmapRandom(emitState)) / emap -> numPhi;
904 copycolor(ray -> rcol, sample -> pdf);
905 }
906
907 /* Normalize photon flux so that average over RGB is 1 */
908 colorNorm(ray -> rcol);
909
910 VCOPY(ray -> rorg, emap -> photonOrg);
911 du = cos(phi) * sinTheta;
912 dv = sin(phi) * sinTheta;
913
914 for (i = 0; i < 3; i++)
915 ray -> rdir [i] = du * emap -> uh [i] + dv * emap -> vh [i] +
916 cosTheta * emap -> wh [i];
917
918 if (emap -> src -> sflags & SDISTANT)
919 /* Distant source; reverse ray direction to point into the scene. */
920 for (i = 0; i < 3; i++)
921 ray -> rdir [i] = -ray -> rdir [i];
922
923 if (emap -> port)
924 /* Photon emitted from port; move origin just behind port so it
925 will be scattered */
926 for (i = 0; i < 3; i++)
927 ray -> rorg [i] -= 2 * FTINY * ray -> rdir [i];
928
929 /* Assign emitting light source index */
930 ray -> rsrc = emap -> src - source;
931 }
932
933
934
935 /* SOURCE CONTRIBS FROM DIRECT / VOLUME PHOTONS -------------------------- */
936
937
938
939 void multDirectPmap (RAY *r)
940 /* Factor irradiance from direct photons into r -> rcol; interface to
941 * direct() */
942 {
943 COLOR photonIrrad;
944
945 /* Lookup direct photon irradiance */
946 (directPmap -> lookup)(directPmap, r, photonIrrad);
947
948 /* Multiply with coefficient in ray */
949 multcolor(r -> rcol, photonIrrad);
950
951 return;
952 }
953
954
955
956 void inscatterVolumePmap (RAY *r, COLOR inscatter)
957 /* Add inscattering from volume photon map; interface to srcscatter() */
958 {
959 /* Add ambient in-scattering via lookup callback */
960 (volumePmap -> lookup)(volumePmap, r, inscatter);
961 }
962