ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.16
Committed: Sat Apr 7 20:25:10 2018 UTC (6 years, 2 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R2
Changes since 2.15: +5 -3 lines
Log Message:
Fixed erroneous bailout in getPhotonPorts() when ports *aren't* used.

File Contents

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