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

File Contents

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