ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.14
Committed: Fri Feb 2 19:47:55 2018 UTC (6 years, 3 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.13: +4 -3 lines
Log Message:
Added -lr, -ld options to mkpmap, enabled -api

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pmapsrc.c,v 2.13 2016/05/17 17:39:47 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
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 we set
761 the limit imposed by photonMaxDist (or no limit if 0) */
762 if (mod -> otype == MAT_GLOW && !(emap -> src -> sflags & SDISTANT)
763 && mod -> oargs.farg[3] > FTINY)
764 ray -> rmax = mod -> oargs.farg[3];
765 else
766 ray -> rmax = photonMaxDist;
767 rayorigin(ray, PRIMARY, NULL, NULL);
768
769 if (!emap -> numSamples) {
770 /* Source is unmodified and has no port, and either local with
771 normal aligned aperture, or distant with aperture above surface;
772 use cosine weighted distribution */
773 cosThetaSqr = 1 - pmapRandom(emitState) *
774 (1 - sqr(max(emap -> cosThetaMax, 0)));
775 cosTheta = sqrt(cosThetaSqr);
776 sinTheta = sqrt(1 - cosThetaSqr);
777 phi = 2 * PI * pmapRandom(emitState);
778 setcolor(ray -> rcol, mod -> oargs.farg [0], mod -> oargs.farg [1],
779 mod -> oargs.farg [2]);
780 }
781
782 else {
783 /* Source is either modified, has a port, is local with off-normal
784 aperture, or distant with aperture partly below surface; choose
785 direction from constructed cumulative distribution function with
786 Monte Carlo inversion using binary search. */
787 du = pmapRandom(emitState) * emap -> cdf;
788 lo = 1;
789 hi = emap -> numSamples;
790
791 while (hi > lo) {
792 i = (lo + hi) >> 1;
793 sample = emap -> samples + i - 1;
794
795 if (sample -> cdf >= du)
796 hi = i;
797 if (sample -> cdf < du)
798 lo = i + 1;
799 }
800
801 /* This is a uniform mapping, mon */
802 cosTheta = 1 - (sample -> theta + pmapRandom(emitState)) *
803 (1 - emap -> cosThetaMax) / emap -> numTheta;
804 sinTheta = sqrt(1 - sqr(cosTheta));
805 phi = 2 * PI * (sample -> phi + pmapRandom(emitState)) / emap -> numPhi;
806 copycolor(ray -> rcol, sample -> pdf);
807 }
808
809 /* Normalize photon flux so that average over RGB is 1 */
810 colorNorm(ray -> rcol);
811
812 VCOPY(ray -> rorg, emap -> photonOrg);
813 du = cos(phi) * sinTheta;
814 dv = sin(phi) * sinTheta;
815
816 for (i = 0; i < 3; i++)
817 ray -> rdir [i] = du * emap -> uh [i] + dv * emap -> vh [i] +
818 cosTheta * emap -> wh [i];
819
820 if (emap -> src -> sflags & SDISTANT)
821 /* Distant source; reverse ray direction to point into the scene. */
822 for (i = 0; i < 3; i++)
823 ray -> rdir [i] = -ray -> rdir [i];
824
825 if (emap -> port)
826 /* Photon emitted from port; move origin just behind port so it
827 will be scattered */
828 for (i = 0; i < 3; i++)
829 ray -> rorg [i] -= 2 * FTINY * ray -> rdir [i];
830
831 /* Assign emitting light source index */
832 ray -> rsrc = emap -> src - source;
833 }
834
835
836
837 void multDirectPmap (RAY *r)
838 /* Factor irradiance from direct photons into r -> rcol; interface to
839 * direct() */
840 {
841 COLOR photonIrrad;
842
843 /* Lookup direct photon irradiance */
844 (directPmap -> lookup)(directPmap, r, photonIrrad);
845
846 /* Multiply with coefficient in ray */
847 multcolor(r -> rcol, photonIrrad);
848
849 return;
850 }
851
852
853
854 void inscatterVolumePmap (RAY *r, COLOR inscatter)
855 /* Add inscattering from volume photon map; interface to srcscatter() */
856 {
857 /* Add ambient in-scattering via lookup callback */
858 (volumePmap -> lookup)(volumePmap, r, inscatter);
859 }