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

# User Rev Content
1 greg 2.7 #ifndef lint
2 rschregle 2.14 static const char RCSid[] = "$Id: pmapsrc.c,v 2.13 2016/05/17 17:39:47 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    
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 greg 2.2 memset(emap -> partitions + (emap -> maxPartitions >> 2), 0,
53     emap -> maxPartitions >> 2);
54 greg 2.1 }
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 greg 2.2 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
107 greg 2.1 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 greg 2.2 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
139 greg 2.1 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 greg 2.2 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
157 greg 2.1 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 greg 2.2
187     memset(emap -> partitions + (emap -> maxPartitions >> 2), 0,
188 greg 2.1 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 greg 2.2 memset(emap -> partitions, 0, emap -> maxPartitions >> 1);
230 greg 2.1 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 rschregle 2.11 OBJREC *obj, *mat;
529 greg 2.1
530     /* Check for missing port modifiers */
531     if (!ambset [0])
532 rschregle 2.11 error(USER, "no photon ports");
533 greg 2.1
534     for (i = 0; i < nobjects; i++) {
535     obj = objptr(i);
536 rschregle 2.11 mat = findmaterial(obj);
537 greg 2.1
538 rschregle 2.10 /* 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 rschregle 2.12 if (issurface(obj -> otype) && mat && !islight(mat -> otype) &&
542 rschregle 2.11 inset(ambset, objndx(mat))) {
543 greg 2.1 /* 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 rschregle 2.11
561     if (!numPhotonPorts)
562     error(USER, "no valid photon ports found");
563 greg 2.1 }
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 greg 2.4 const OBJREC* mod = findmaterial(emap -> src -> so);
606 greg 2.1 static RAY r;
607     #if 0
608     static double lastCosNorm = FHUGE;
609     static SRCREC *lastSrc = NULL, *lastPort = NULL;
610     #endif
611    
612 greg 2.4 setcolor(emap -> partFlux, 0, 0, 0);
613    
614 greg 2.1 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 greg 2.5 r.rmax = 0;
685 greg 2.1
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 greg 2.4 const OBJREC* mod = findmaterial(emap -> src -> so);
755 greg 2.1
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 greg 2.5 /* If we have a local glow source with a maximum radius, then
760 rschregle 2.14 restrict our photon to the specified distance, otherwise we set
761     the limit imposed by photonMaxDist (or no limit if 0) */
762 greg 2.6 if (mod -> otype == MAT_GLOW && !(emap -> src -> sflags & SDISTANT)
763 greg 2.5 && mod -> oargs.farg[3] > FTINY)
764     ray -> rmax = mod -> oargs.farg[3];
765     else
766 rschregle 2.14 ray -> rmax = photonMaxDist;
767 greg 2.1 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     }