ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapsrc.c
Revision: 2.17
Committed: Thu Nov 8 00:54:07 2018 UTC (6 years, 5 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.16: +2 -1 lines
Log Message:
Moved findmaterial() from source.c to initotypes.c

File Contents

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