ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.15
Committed: Tue Mar 20 19:55:33 2018 UTC (6 years, 1 month ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.14: +28 -25 lines
Log Message:
Added -ae/-ai ambient exclude options to mkpmap, cleaned up opt parsing.

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pmapsrc.c,v 2.14 2018/02/02 19:47:55 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 /* Check for missing port modifiers */
531 if (!portList [0])
532 error(USER, "no photon ports");
533
534 for (i = 0; i < nobjects; i++) {
535 obj = objptr(i);
536 mat = findmaterial(obj);
537
538 /* Check if object is a surface and NOT a light source (duh) and
539 * resolve its material via any aliases, then check for inclusion in
540 * modifier list */
541 if (issurface(obj -> otype) && mat && !islight(mat -> otype)) {
542 for (lp = portList; *lp && strcmp(mat -> oname, *lp); lp++);
543
544 if (*lp) {
545 /* Add photon port */
546 photonPorts = (SRCREC*)realloc(photonPorts,
547 (numPhotonPorts + 1) *
548 sizeof(SRCREC));
549 if (!photonPorts)
550 error(USER, "can't allocate photon ports");
551
552 photonPorts [numPhotonPorts].so = obj;
553 photonPorts [numPhotonPorts].sflags = 0;
554
555 if (!sfun [obj -> otype].of || !sfun[obj -> otype].of -> setsrc)
556 objerror(obj, USER, "illegal photon port");
557
558 setsource(photonPorts + numPhotonPorts, obj);
559 numPhotonPorts++;
560 }
561 }
562 }
563
564 if (!numPhotonPorts)
565 error(USER, "no valid photon ports found");
566 }
567
568
569
570 static void defaultEmissionFunc (EmissionMap* emap)
571 /* Default behaviour when no emission funcs defined for this source type */
572 {
573 objerror(emap -> src -> so, INTERNAL,
574 "undefined photon emission function");
575 }
576
577
578
579 void initPhotonEmissionFuncs ()
580 /* Init photonPartition[] and photonOrigin[] dispatch tables */
581 {
582 int i;
583
584 for (i = 0; i < NUMOTYPE; i++)
585 photonPartition [i] = photonOrigin [i] = defaultEmissionFunc;
586
587 photonPartition [OBJ_FACE] = photonPartition [OBJ_RING] = flatPhotonPartition;
588 photonPartition [OBJ_SOURCE] = sourcePhotonPartition;
589 photonPartition [OBJ_SPHERE] = spherePhotonPartition;
590 photonPartition [OBJ_CYLINDER] = cylPhotonPartition;
591 photonOrigin [OBJ_FACE] = photonOrigin [OBJ_RING] = flatPhotonOrigin;
592 photonOrigin [OBJ_SOURCE] = sourcePhotonOrigin;
593 photonOrigin [OBJ_SPHERE] = spherePhotonOrigin;
594 photonOrigin [OBJ_CYLINDER] = cylPhotonOrigin;
595 }
596
597
598
599 void initPhotonEmission (EmissionMap *emap, float numPdfSamples)
600 /* Initialize photon emission from partitioned light source emap -> src;
601 * this involves integrating the flux emitted from the current photon
602 * origin emap -> photonOrg and setting up a PDF to sample the emission
603 * distribution with numPdfSamples samples */
604 {
605 unsigned i, t, p;
606 double phi, cosTheta, sinTheta, du, dv, dOmega, thetaScale;
607 EmissionSample* sample;
608 const OBJREC* mod = findmaterial(emap -> src -> so);
609 static RAY r;
610 #if 0
611 static double lastCosNorm = FHUGE;
612 static SRCREC *lastSrc = NULL, *lastPort = NULL;
613 #endif
614
615 setcolor(emap -> partFlux, 0, 0, 0);
616
617 photonOrigin [emap -> src -> so -> otype] (emap);
618 cosTheta = DOT(emap -> ws, emap -> wh);
619
620 #if 0
621 if (emap -> src == lastSrc && emap -> port == lastPort &&
622 (emap -> src -> sflags & SDISTANT || mod -> omod == OVOID) &&
623 cosTheta == lastCosNorm)
624 /* Same source, port, and aperture-normal angle, and source is
625 either distant (and thus translationally invariant) or has
626 no modifier --> flux unchanged */
627 /* BUG: this optimisation ignores partial occlusion of ports and
628 can lead to erroneous "zero emission" bailouts.
629 It can also lead to bias with modifiers exhibiting high variance!
630 Disabled for now -- RS 12/13 */
631 return;
632
633 lastSrc = emap -> src;
634 lastPort = emap -> port;
635 lastCosNorm = cosTheta;
636 #endif
637
638 /* Need to recompute flux & PDF */
639 emap -> cdf = 0;
640 emap -> numSamples = 0;
641
642 if (cosTheta <= 0 &&
643 sqrt(1 - sqr(cosTheta)) <= emap -> cosThetaMax + FTINY)
644 /* Aperture below surface; no emission from current origin */
645 return;
646
647 if (mod -> omod == OVOID && !emap -> port &&
648 (cosTheta >= 1 - FTINY || (emap -> src -> sflags & SDISTANT &&
649 acos(cosTheta) + acos(emap -> cosThetaMax) <= 0.5 * PI))) {
650 /* Source is unmodified and has no port (which requires testing for
651 occlusion), and is either local with normal aligned aperture or
652 distant with aperture above surface; analytical flux & PDF */
653 setcolor(emap -> partFlux, mod -> oargs.farg [0],
654 mod -> oargs.farg [1], mod -> oargs.farg [2]);
655
656 /* Multiply radiance by Omega * dA to get flux */
657 scalecolor(emap -> partFlux,
658 PI * cosTheta * (1 - sqr(max(emap -> cosThetaMax, 0))) *
659 emap -> partArea);
660 }
661
662 else {
663 /* Source is either modified, has a port, is local with off-normal
664 aperture, or distant with aperture partly below surface; get flux
665 via numerical integration */
666 thetaScale = (1 - emap -> cosThetaMax);
667
668 /* Figga out numba of aperture samples for integration;
669 numTheta / numPhi ratio is 1 / PI */
670 du = sqrt(pdfSamples * 2 * thetaScale);
671 emap -> numTheta = max(du + 0.5, 1);
672 emap -> numPhi = max(PI * du + 0.5, 1);
673
674 dOmega = 2 * PI * thetaScale / (emap -> numTheta * emap -> numPhi);
675 thetaScale /= emap -> numTheta;
676
677 /* Allocate PDF, baby */
678 sample = emap -> samples = (EmissionSample*)
679 realloc(emap -> samples,
680 sizeof(EmissionSample) *
681 emap -> numTheta * emap -> numPhi);
682 if (!emap -> samples)
683 error(USER, "can't allocate emission PDF");
684
685 VCOPY(r.rorg, emap -> photonOrg);
686 VCOPY(r.rop, emap -> photonOrg);
687 r.rmax = 0;
688
689 for (t = 0; t < emap -> numTheta; t++) {
690 for (p = 0; p < emap -> numPhi; p++) {
691 /* This uniform mapping handles 0 <= thetaMax <= PI */
692 cosTheta = 1 - (t + pmapRandom(emitState)) * thetaScale;
693 sinTheta = sqrt(1 - sqr(cosTheta));
694 phi = 2 * PI * (p + pmapRandom(emitState)) / emap -> numPhi;
695 du = cos(phi) * sinTheta;
696 dv = sin(phi) * sinTheta;
697 rayorigin(&r, PRIMARY, NULL, NULL);
698
699 for (i = 0; i < 3; i++)
700 r.rdir [i] = du * emap -> uh [i] + dv * emap -> vh [i] +
701 cosTheta * emap -> wh [i];
702
703 /* Sample behind surface? */
704 VCOPY(r.ron, emap -> ws);
705 if ((r.rod = DOT(r.rdir, r.ron)) <= 0)
706 continue;
707
708 /* Get radiance emitted in this direction; to get flux we
709 multiply by cos(theta_surface), dOmega, and dA. Ray
710 is directed towards light source for raytexture(). */
711 if (!(emap -> src -> sflags & SDISTANT))
712 for (i = 0; i < 3; i++)
713 r.rdir [i] = -r.rdir [i];
714
715 /* Port occluded in this direction? */
716 if (emap -> port && localhit(&r, &thescene))
717 continue;
718
719 raytexture(&r, mod -> omod);
720 setcolor(r.rcol, mod -> oargs.farg [0], mod -> oargs.farg [1],
721 mod -> oargs.farg [2]);
722 multcolor(r.rcol, r.pcol);
723
724 /* Multiply by cos(theta_surface) */
725 scalecolor(r.rcol, r.rod);
726
727 /* Add PDF sample if nonzero; importance info for photon emission
728 * could go here... */
729 if (colorAvg(r.rcol)) {
730 copycolor(sample -> pdf, r.rcol);
731 sample -> cdf = emap -> cdf += colorAvg(sample -> pdf);
732 sample -> theta = t;
733 sample++ -> phi = p;
734 emap -> numSamples++;
735 addcolor(emap -> partFlux, r.rcol);
736 }
737 }
738 }
739
740 /* Multiply by dOmega * dA */
741 scalecolor(emap -> partFlux, dOmega * emap -> partArea);
742 }
743 }
744
745
746
747 #define vomitPhoton emitPhoton
748 #define bluarrrghPhoton vomitPhoton
749
750 void emitPhoton (const EmissionMap* emap, RAY* ray)
751 /* Emit photon from current partition emap -> partitionCnt based on
752 emission distribution. Returns new photon ray. */
753 {
754 unsigned long i, lo, hi;
755 const EmissionSample* sample = emap -> samples;
756 RREAL du, dv, cosTheta, cosThetaSqr, sinTheta, phi;
757 const OBJREC* mod = findmaterial(emap -> src -> so);
758
759 /* Choose a new origin within current partition for every
760 emitted photon to break up clustering artifacts */
761 photonOrigin [emap -> src -> so -> otype] ((EmissionMap*)emap);
762 /* If we have a local glow source with a maximum radius, then
763 restrict our photon to the specified distance, otherwise we set
764 the limit imposed by photonMaxDist (or no limit if 0) */
765 if (mod -> otype == MAT_GLOW && !(emap -> src -> sflags & SDISTANT)
766 && mod -> oargs.farg[3] > FTINY)
767 ray -> rmax = mod -> oargs.farg[3];
768 else
769 ray -> rmax = photonMaxDist;
770 rayorigin(ray, PRIMARY, NULL, NULL);
771
772 if (!emap -> numSamples) {
773 /* Source is unmodified and has no port, and either local with
774 normal aligned aperture, or distant with aperture above surface;
775 use cosine weighted distribution */
776 cosThetaSqr = 1 - pmapRandom(emitState) *
777 (1 - sqr(max(emap -> cosThetaMax, 0)));
778 cosTheta = sqrt(cosThetaSqr);
779 sinTheta = sqrt(1 - cosThetaSqr);
780 phi = 2 * PI * pmapRandom(emitState);
781 setcolor(ray -> rcol, mod -> oargs.farg [0], mod -> oargs.farg [1],
782 mod -> oargs.farg [2]);
783 }
784
785 else {
786 /* Source is either modified, has a port, is local with off-normal
787 aperture, or distant with aperture partly below surface; choose
788 direction from constructed cumulative distribution function with
789 Monte Carlo inversion using binary search. */
790 du = pmapRandom(emitState) * emap -> cdf;
791 lo = 1;
792 hi = emap -> numSamples;
793
794 while (hi > lo) {
795 i = (lo + hi) >> 1;
796 sample = emap -> samples + i - 1;
797
798 if (sample -> cdf >= du)
799 hi = i;
800 if (sample -> cdf < du)
801 lo = i + 1;
802 }
803
804 /* This is a uniform mapping, mon */
805 cosTheta = 1 - (sample -> theta + pmapRandom(emitState)) *
806 (1 - emap -> cosThetaMax) / emap -> numTheta;
807 sinTheta = sqrt(1 - sqr(cosTheta));
808 phi = 2 * PI * (sample -> phi + pmapRandom(emitState)) / emap -> numPhi;
809 copycolor(ray -> rcol, sample -> pdf);
810 }
811
812 /* Normalize photon flux so that average over RGB is 1 */
813 colorNorm(ray -> rcol);
814
815 VCOPY(ray -> rorg, emap -> photonOrg);
816 du = cos(phi) * sinTheta;
817 dv = sin(phi) * sinTheta;
818
819 for (i = 0; i < 3; i++)
820 ray -> rdir [i] = du * emap -> uh [i] + dv * emap -> vh [i] +
821 cosTheta * emap -> wh [i];
822
823 if (emap -> src -> sflags & SDISTANT)
824 /* Distant source; reverse ray direction to point into the scene. */
825 for (i = 0; i < 3; i++)
826 ray -> rdir [i] = -ray -> rdir [i];
827
828 if (emap -> port)
829 /* Photon emitted from port; move origin just behind port so it
830 will be scattered */
831 for (i = 0; i < 3; i++)
832 ray -> rorg [i] -= 2 * FTINY * ray -> rdir [i];
833
834 /* Assign emitting light source index */
835 ray -> rsrc = emap -> src - source;
836 }
837
838
839
840 void multDirectPmap (RAY *r)
841 /* Factor irradiance from direct photons into r -> rcol; interface to
842 * direct() */
843 {
844 COLOR photonIrrad;
845
846 /* Lookup direct photon irradiance */
847 (directPmap -> lookup)(directPmap, r, photonIrrad);
848
849 /* Multiply with coefficient in ray */
850 multcolor(r -> rcol, photonIrrad);
851
852 return;
853 }
854
855
856
857 void inscatterVolumePmap (RAY *r, COLOR inscatter)
858 /* Add inscattering from volume photon map; interface to srcscatter() */
859 {
860 /* Add ambient in-scattering via lookup callback */
861 (volumePmap -> lookup)(volumePmap, r, inscatter);
862 }