ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.13
Committed: Tue May 17 17:39:47 2016 UTC (7 years, 11 months ago) by rschregle
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R1
Changes since 2.12: +1 -1 lines
Log Message:
Initial import of ooC photon map

File Contents

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