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

File Contents

# User Rev Content
1 greg 2.9 #ifndef lint
2 rschregle 2.11 static const char RCSid[] = "$Id: pmap.c,v 4.33.1.10 2016/05/11 12:50:54 taschreg Exp taschreg $";
3 greg 2.9 #endif
4 rschregle 2.11
5 greg 2.1 /*
6 rschregle 2.11 ======================================================================
7 greg 2.1 Photon map main module
8    
9     Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
10     (c) Fraunhofer Institute for Solar Energy Systems,
11 rschregle 2.4 (c) Lucerne University of Applied Sciences and Arts,
12 rschregle 2.11 supported by the Swiss National Science Foundation (SNSF, #147053)
13     ======================================================================
14 greg 2.1
15 rschregle 2.11 $Id: pmap.c,v 4.33.1.10 2016/05/11 12:50:54 taschreg Exp taschreg $
16 greg 2.1 */
17    
18    
19    
20     #include "pmap.h"
21     #include "pmapmat.h"
22     #include "pmapsrc.h"
23     #include "pmaprand.h"
24     #include "pmapio.h"
25     #include "pmapbias.h"
26     #include "pmapdiag.h"
27     #include "otypes.h"
28     #include <time.h>
29     #include <sys/stat.h>
30 rschregle 2.11 #include <sys/mman.h>
31     #include <sys/wait.h>
32 greg 2.1
33 rschregle 2.11 #define PMAP_REV "$Revision: 4.33.1.10 $"
34 greg 2.1
35    
36     extern char *octname;
37    
38    
39    
40     /* Photon map lookup functions per type */
41     void (*pmapLookup [NUM_PMAP_TYPES])(PhotonMap*, RAY*, COLOR) = {
42     photonDensity, photonPreCompDensity, photonDensity, volumePhotonDensity,
43     photonDensity, NULL
44     };
45    
46    
47    
48     void colorNorm (COLOR c)
49     /* Normalise colour channels to average of 1 */
50     {
51     const float avg = colorAvg(c);
52    
53     if (!avg)
54     return;
55    
56     c [0] /= avg;
57     c [1] /= avg;
58     c [2] /= avg;
59     }
60    
61    
62    
63     void loadPmaps (PhotonMap **pmaps, const PhotonMapParams *parm)
64     {
65     unsigned t;
66     struct stat octstat, pmstat;
67     PhotonMap *pm;
68     PhotonMapType type;
69    
70     for (t = 0; t < NUM_PMAP_TYPES; t++)
71     if (setPmapParam(&pm, parm + t)) {
72     /* Check if photon map newer than octree */
73 rschregle 2.4 if (pm -> fileName && octname &&
74     !stat(pm -> fileName, &pmstat) && !stat(octname, &octstat) &&
75 greg 2.1 octstat.st_mtime > pmstat.st_mtime) {
76     sprintf(errmsg, "photon map in file %s may be stale",
77     pm -> fileName);
78     error(USER, errmsg);
79     }
80    
81     /* Load photon map from file and get its type */
82     if ((type = loadPhotonMap(pm, pm -> fileName)) == PMAP_TYPE_NONE)
83     error(USER, "failed loading photon map");
84    
85     /* Assign to appropriate photon map type (deleting previously
86     * loaded photon map of same type if necessary) */
87     if (pmaps [type]) {
88     deletePhotons(pmaps [type]);
89     free(pmaps [type]);
90     }
91     pmaps [type] = pm;
92    
93     /* Check for invalid density estimate bandwidth */
94 rschregle 2.11 if (pm -> maxGather > pm -> numPhotons) {
95 greg 2.1 error(WARNING, "adjusting density estimate bandwidth");
96 rschregle 2.11 pm -> minGather = pm -> maxGather = pm -> numPhotons;
97 greg 2.1 }
98     }
99     }
100    
101    
102    
103     void savePmaps (const PhotonMap **pmaps, int argc, char **argv)
104     {
105     unsigned t;
106    
107     for (t = 0; t < NUM_PMAP_TYPES; t++) {
108     if (pmaps [t])
109 greg 2.7 savePhotonMap(pmaps [t], pmaps [t] -> fileName, argc, argv);
110 greg 2.1 }
111     }
112    
113    
114    
115     void cleanUpPmaps (PhotonMap **pmaps)
116     {
117     unsigned t;
118    
119     for (t = 0; t < NUM_PMAP_TYPES; t++) {
120     if (pmaps [t]) {
121     deletePhotons(pmaps [t]);
122     free(pmaps [t]);
123     }
124     }
125     }
126    
127    
128    
129     static int photonParticipate (RAY *ray)
130     /* Trace photon through participating medium. Returns 1 if passed through,
131     or 0 if absorbed and $*%&ed. Analogon to rayparticipate(). */
132     {
133     int i;
134     RREAL cosTheta, cosPhi, du, dv;
135     const float cext = colorAvg(ray -> cext),
136     albedo = colorAvg(ray -> albedo);
137     FVECT u, v;
138     COLOR cvext;
139    
140     /* Mean free distance until interaction with medium */
141     ray -> rmax = -log(pmapRandom(mediumState)) / cext;
142    
143     while (!localhit(ray, &thescene)) {
144     setcolor(cvext, exp(-ray -> rmax * ray -> cext [0]),
145     exp(-ray -> rmax * ray -> cext [1]),
146     exp(-ray -> rmax * ray -> cext [2]));
147    
148     /* Modify ray color and normalise */
149     multcolor(ray -> rcol, cvext);
150     colorNorm(ray -> rcol);
151     VCOPY(ray -> rorg, ray -> rop);
152    
153 rschregle 2.11 if (albedo > FTINY && ray -> rlvl > 0)
154 greg 2.1 /* Add to volume photon map */
155 rschregle 2.11 newPhoton(volumePmap, ray);
156 greg 2.1
157     /* Absorbed? */
158 rschregle 2.11 if (pmapRandom(rouletteState) > albedo)
159     return 0;
160 greg 2.1
161     /* Colour bleeding without attenuation (?) */
162     multcolor(ray -> rcol, ray -> albedo);
163     scalecolor(ray -> rcol, 1 / albedo);
164    
165     /* Scatter photon */
166     cosTheta = ray -> gecc <= FTINY ? 2 * pmapRandom(scatterState) - 1
167     : 1 / (2 * ray -> gecc) *
168     (1 + ray -> gecc * ray -> gecc -
169     (1 - ray -> gecc * ray -> gecc) /
170     (1 - ray -> gecc + 2 * ray -> gecc *
171     pmapRandom(scatterState)));
172    
173     cosPhi = cos(2 * PI * pmapRandom(scatterState));
174     du = dv = sqrt(1 - cosTheta * cosTheta); /* sin(theta) */
175     du *= cosPhi;
176     dv *= sqrt(1 - cosPhi * cosPhi); /* sin(phi) */
177    
178     /* Get axes u & v perpendicular to photon direction */
179     i = 0;
180     do {
181     v [0] = v [1] = v [2] = 0;
182     v [i++] = 1;
183     fcross(u, v, ray -> rdir);
184     } while (normalize(u) < FTINY);
185     fcross(v, ray -> rdir, u);
186    
187     for (i = 0; i < 3; i++)
188     ray -> rdir [i] = du * u [i] + dv * v [i] +
189     cosTheta * ray -> rdir [i];
190     ray -> rlvl++;
191     ray -> rmax = -log(pmapRandom(mediumState)) / cext;
192     }
193    
194     setcolor(cvext, exp(-ray -> rot * ray -> cext [0]),
195     exp(-ray -> rot * ray -> cext [1]),
196     exp(-ray -> rot * ray -> cext [2]));
197    
198     /* Modify ray color and normalise */
199     multcolor(ray -> rcol, cvext);
200     colorNorm(ray -> rcol);
201    
202     /* Passed through medium */
203     return 1;
204     }
205    
206    
207    
208     void tracePhoton (RAY *ray)
209     /* Follow photon as it bounces around... */
210     {
211     long mod;
212     OBJREC* mat;
213    
214     if (ray -> rlvl > photonMaxBounce) {
215 rschregle 2.5 #ifdef PMAP_RUNAWAY_WARN
216 greg 2.1 error(WARNING, "runaway photon!");
217 rschregle 2.5 #endif
218 greg 2.1 return;
219     }
220 rschregle 2.5
221 greg 2.1 if (colorAvg(ray -> cext) > FTINY && !photonParticipate(ray))
222     return;
223    
224     if (localhit(ray, &thescene)) {
225     mod = ray -> ro -> omod;
226    
227     if ((ray -> clipset && inset(ray -> clipset, mod)) || mod == OVOID) {
228     /* Transfer ray if modifier is VOID or clipped within antimatta */
229     RAY tray;
230     photonRay(ray, &tray, PMAP_XFER, NULL);
231     tracePhoton(&tray);
232     }
233     else {
234     /* Scatter for modifier material */
235     mat = objptr(mod);
236     photonScatter [mat -> otype] (mat, ray);
237     }
238     }
239     }
240    
241    
242    
243     static void preComputeGlobal (PhotonMap *pmap)
244 rschregle 2.11 /* Precompute irradiance from global photons for final gathering for
245     a random subset of finalGather * pmap -> numPhotons photons, and builds
246     the photon map, discarding the original photons. */
247     /* !!! NOTE: PRECOMPUTATION WITH OOC CURRENTLY WITHOUT CACHE !!! */
248     {
249     unsigned long i, numPreComp;
250     unsigned j;
251     PhotonIdx pIdx;
252     Photon photon;
253     RAY ray;
254     PhotonMap nuPmap;
255 greg 2.1
256 rschregle 2.11 repComplete = numPreComp = finalGather * pmap -> numPhotons;
257 greg 2.1
258     if (photonRepTime) {
259 rschregle 2.11 sprintf(errmsg, "Precomputing irradiance for %ld global photons...\n",
260     numPreComp);
261 greg 2.1 eputs(errmsg);
262     fflush(stderr);
263     }
264    
265 rschregle 2.11 /* Copy photon map for precomputed photons */
266     memcpy(&nuPmap, pmap, sizeof(PhotonMap));
267    
268     /* Zero counters, init new heap and extents */
269     nuPmap.numPhotons = 0;
270     initPhotonHeap(&nuPmap);
271    
272     for (j = 0; j < 3; j++) {
273     nuPmap.minPos [j] = FHUGE;
274     nuPmap.maxPos [j] = -FHUGE;
275 greg 2.1 }
276 rschregle 2.11
277 greg 2.1 /* Record start time, baby */
278     repStartTime = time(NULL);
279 rschregle 2.11 #ifdef SIGCONT
280     signal(SIGCONT, pmapPreCompReport);
281     #endif
282 greg 2.1 repProgress = 0;
283    
284 rschregle 2.11 photonRay(NULL, &ray, PRIMARY, NULL);
285     ray.ro = NULL;
286    
287     for (i = 0; i < numPreComp; i++) {
288     /* Get random photon from stratified distribution in source heap to
289     * avoid duplicates and clutering */
290     pIdx = firstPhoton(pmap) +
291     (unsigned long)((i + pmapRandom(pmap -> randState)) /
292     finalGather);
293     getPhoton(pmap, pIdx, &photon);
294    
295     /* Init dummy photon ray with intersection at photon position */
296     VCOPY(ray.rop, photon.pos);
297     for (j = 0; j < 3; j++)
298     ray.ron [j] = photon.norm [j] / 127.0;
299    
300     /* Get density estimate at photon position */
301     photonDensity(pmap, &ray, ray.rcol);
302    
303     /* Append photon to new heap from ray */
304     newPhoton(&nuPmap, &ray);
305 greg 2.1
306 rschregle 2.11 /* Update progress */
307 greg 2.1 repProgress++;
308    
309     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
310     pmapPreCompReport();
311 rschregle 2.11 #ifdef SIGCONT
312     else signal(SIGCONT, pmapPreCompReport);
313     #endif
314 greg 2.1 }
315    
316 rschregle 2.11 /* Flush heap */
317     flushPhotonHeap(&nuPmap);
318    
319     #ifdef SIGCONT
320     signal(SIGCONT, SIG_DFL);
321     #endif
322    
323     /* Trash original pmap, replace with precomputed one */
324     deletePhotons(pmap);
325     memcpy(pmap, &nuPmap, sizeof(PhotonMap));
326 greg 2.1
327     if (photonRepTime) {
328 rschregle 2.11 eputs("Rebuilding precomputed photon map...\n");
329 greg 2.1 fflush(stderr);
330     }
331 rschregle 2.11
332     /* Rebuild underlying data structure, destroying heap */
333     buildPhotonMap(pmap, NULL, NULL, 1);
334 greg 2.1 }
335    
336    
337    
338 rschregle 2.11 typedef struct {
339     unsigned long numPhotons [NUM_PMAP_TYPES],
340     numEmitted, numComplete;
341     } PhotonCnt;
342    
343    
344    
345     void distribPhotons (PhotonMap **pmaps, unsigned numProc)
346     {
347     EmissionMap emap;
348     char errmsg2 [128], shmFname [255];
349     unsigned t, srcIdx, proc;
350     double totalFlux = 0;
351     int shmFile, stat, pid;
352     PhotonMap *pm;
353     PhotonCnt *photonCnt;
354 greg 2.1
355 rschregle 2.8 for (t = 0; t < NUM_PMAP_TYPES && !pmaps [t]; t++);
356 rschregle 2.11
357 greg 2.1 if (t >= NUM_PMAP_TYPES)
358 rschregle 2.11 error(USER, "no photon maps defined in distribPhotons");
359 greg 2.1
360     if (!nsources)
361 rschregle 2.11 error(USER, "no light sources in distribPhotons");
362 greg 2.1
363     /* ===================================================================
364     * INITIALISATION - Set up emission and scattering funcs
365     * =================================================================== */
366     emap.samples = NULL;
367     emap.maxPartitions = MAXSPART;
368     emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
369     if (!emap.partitions)
370 rschregle 2.11 error(INTERNAL, "can't allocate source partitions in distribPhotons");
371 greg 2.1
372     /* Initialise all defined photon maps */
373     for (t = 0; t < NUM_PMAP_TYPES; t++)
374 rschregle 2.11 if (pmaps [t]) {
375     initPhotonMap(pmaps [t], t);
376     /* Open photon heapfile */
377     initPhotonHeap(pmaps [t]);
378     /* Per-subprocess target count */
379     pmaps [t] -> distribTarget /= numProc;
380     }
381 greg 2.1
382     initPhotonEmissionFuncs();
383     initPhotonScatterFuncs();
384    
385     /* Get photon ports if specified */
386     if (ambincl == 1)
387     getPhotonPorts();
388    
389     /* Get photon sensor modifiers */
390     getPhotonSensors(photonSensorList);
391    
392 rschregle 2.11 /* Set up shared mem for photon counters (zeroed by ftruncate) */
393     #if 0
394     snprintf(shmFname, 255, PMAP_SHMFNAME, getpid());
395     shmFile = shm_open(shmFname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
396     #else
397     strcpy(shmFname, PMAP_SHMFNAME);
398     shmFile = mkstemp(shmFname);
399     #endif
400    
401     if (shmFile < 0)
402     error(SYSTEM, "failed opening shared memory file in distribPhotons");
403    
404     if (ftruncate(shmFile, sizeof(*photonCnt)) < 0)
405     error(SYSTEM, "failed setting shared memory size in distribPhotons");
406    
407     photonCnt = mmap(NULL, sizeof(*photonCnt), PROT_READ | PROT_WRITE,
408     MAP_SHARED, shmFile, 0);
409    
410     if (photonCnt == MAP_FAILED)
411     error(SYSTEM, "failed mapping shared memory in distribPhotons");
412    
413 greg 2.1 if (photonRepTime)
414     eputs("\n");
415    
416     /* ===================================================================
417     * FLUX INTEGRATION - Get total photon flux from light sources
418     * =================================================================== */
419 rschregle 2.11 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
420 greg 2.1 unsigned portCnt = 0;
421     emap.src = source + srcIdx;
422    
423 rschregle 2.11 do { /* Need at least one iteration if no ports! */
424 greg 2.1 emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
425     : NULL;
426     photonPartition [emap.src -> so -> otype] (&emap);
427    
428     if (photonRepTime) {
429     sprintf(errmsg, "Integrating flux from source %s ",
430     source [srcIdx].so -> oname);
431    
432     if (emap.port) {
433     sprintf(errmsg2, "via port %s ",
434     photonPorts [portCnt].so -> oname);
435     strcat(errmsg, errmsg2);
436     }
437    
438     sprintf(errmsg2, "(%lu partitions)...\n", emap.numPartitions);
439     strcat(errmsg, errmsg2);
440     eputs(errmsg);
441     fflush(stderr);
442     }
443    
444     for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
445     emap.partitionCnt++) {
446     initPhotonEmission(&emap, pdfSamples);
447     totalFlux += colorAvg(emap.partFlux);
448     }
449    
450     portCnt++;
451     } while (portCnt < numPhotonPorts);
452     }
453    
454     if (totalFlux < FTINY)
455     error(USER, "zero flux from light sources");
456    
457 rschregle 2.11 /* MAIN LOOP */
458     for (proc = 0; proc < numProc; proc++) {
459     if (!(pid = fork())) {
460     /* SUBPROCESS ENTERS HERE.
461     All opened and memory mapped files are inherited */
462     unsigned passCnt = 0, prePassCnt = 0;
463     unsigned long lastNumPhotons [NUM_PMAP_TYPES];
464     unsigned long localNumEmitted = 0; /* Num photons emitted by this
465     subprocess alone */
466 greg 2.1
467 rschregle 2.11 /* Seed RNGs from PID for decorellated photon distribution */
468     pmapSeed(randSeed + proc, partState);
469     pmapSeed(randSeed + proc, emitState);
470     pmapSeed(randSeed + proc, cntState);
471     pmapSeed(randSeed + proc, mediumState);
472     pmapSeed(randSeed + proc, scatterState);
473     pmapSeed(randSeed + proc, rouletteState);
474 greg 2.1
475     for (t = 0; t < NUM_PMAP_TYPES; t++)
476 rschregle 2.11 lastNumPhotons [t] = 0;
477    
478     /* =============================================================
479     * 2-PASS PHOTON DISTRIBUTION
480     * Pass 1 (pre): emit fraction of target photon count
481     * Pass 2 (main): based on outcome of pass 1, estimate remaining
482     * number of photons to emit to approximate target
483     * count
484     * ============================================================= */
485 greg 2.1 do {
486 rschregle 2.11 double numEmit;
487 greg 2.1
488 rschregle 2.11 if (!passCnt) {
489     /* INIT PASS 1 */
490     /* Skip if no photons contributed after sufficient
491     * iterations; make it clear to user which photon maps are
492     * missing so (s)he can check geometry and materials */
493     if (++prePassCnt > maxPreDistrib) {
494     sprintf(errmsg,
495     "proc %d, source %s: too many prepasses",
496     proc, source [srcIdx].so -> oname);
497    
498     for (t = 0; t < NUM_PMAP_TYPES; t++)
499     if (pmaps [t] && !pmaps [t] -> numPhotons) {
500     sprintf(errmsg2, ", no %s photons stored",
501     pmapName [t]);
502     strcat(errmsg, errmsg2);
503     }
504    
505     error(USER, errmsg);
506     break;
507 greg 2.1 }
508 rschregle 2.11
509     /* Num to emit is fraction of minimum target count */
510     numEmit = FHUGE;
511 greg 2.1
512 rschregle 2.11 for (t = 0; t < NUM_PMAP_TYPES; t++)
513     if (pmaps [t])
514     numEmit = min(pmaps [t] -> distribTarget, numEmit);
515    
516     numEmit *= preDistrib;
517 greg 2.1 }
518 rschregle 2.11 else {
519     /* INIT PASS 2 */
520     /* Based on the outcome of the predistribution we can now
521     * estimate how many more photons we have to emit for each
522     * photon map to meet its respective target count. This
523     * value is clamped to 0 in case the target has already been
524     * exceeded in the pass 1. */
525     double maxDistribRatio = 0;
526    
527     /* Set the distribution ratio for each map; this indicates
528     * how many photons of each respective type are stored per
529     * emitted photon, and is used as probability for storing a
530     * photon by newPhoton(). Since this biases the photon
531     * density, newPhoton() promotes the flux of stored photons
532     * to compensate. */
533     for (t = 0; t < NUM_PMAP_TYPES; t++)
534     if ((pm = pmaps [t])) {
535     pm -> distribRatio = (double)pm -> distribTarget /
536     pm -> numPhotons - 1;
537    
538     /* Check if photon map "overflowed", i.e. exceeded its
539     * target count in the prepass; correcting the photon
540     * flux via the distribution ratio is no longer
541     * possible, as no more photons of this type will be
542     * stored, so notify the user rather than deliver
543     * incorrect results. In future we should handle this
544     * more intelligently by using the photonFlux in each
545     * photon map to individually correct the flux after
546     * distribution. */
547     if (pm -> distribRatio <= FTINY) {
548     sprintf(errmsg, "%s photon map overflow in "
549     "prepass, reduce -apD", pmapName [t]);
550     error(INTERNAL, errmsg);
551     }
552    
553     maxDistribRatio = max(pm -> distribRatio,
554     maxDistribRatio);
555     }
556 greg 2.1
557 rschregle 2.11 /* Normalise distribution ratios and calculate number of
558     * photons to emit in main pass */
559     for (t = 0; t < NUM_PMAP_TYPES; t++)
560     if ((pm = pmaps [t]))
561     pm -> distribRatio /= maxDistribRatio;
562    
563     if ((numEmit = localNumEmitted * maxDistribRatio) < FTINY)
564     /* No photons left to distribute in main pass */
565     break;
566     }
567    
568     /* Update shared completion counter for prog.report by parent */
569     photonCnt -> numComplete += numEmit;
570    
571     /* PHOTON DISTRIBUTION LOOP */
572     for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
573     unsigned portCnt = 0;
574     emap.src = source + srcIdx;
575    
576     do { /* Need at least one iteration if no ports! */
577     emap.port = emap.src -> sflags & SDISTANT
578     ? photonPorts + portCnt : NULL;
579     photonPartition [emap.src -> so -> otype] (&emap);
580    
581     if (photonRepTime && !proc) {
582     if (!passCnt)
583     sprintf(errmsg, "PREPASS %d on source %s ",
584     prePassCnt, source [srcIdx].so -> oname);
585     else
586     sprintf(errmsg, "MAIN PASS on source %s ",
587     source [srcIdx].so -> oname);
588    
589     if (emap.port) {
590     sprintf(errmsg2, "via port %s ",
591     photonPorts [portCnt].so -> oname);
592     strcat(errmsg, errmsg2);
593     }
594    
595     sprintf(errmsg2, "(%lu partitions)\n",
596     emap.numPartitions);
597     strcat(errmsg, errmsg2);
598     eputs(errmsg);
599     fflush(stderr);
600     }
601 greg 2.1
602 rschregle 2.11 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
603     emap.partitionCnt++) {
604     double partNumEmit;
605     unsigned long partEmitCnt;
606    
607     /* Get photon origin within current source partishunn
608     * and build emission map */
609     photonOrigin [emap.src -> so -> otype] (&emap);
610     initPhotonEmission(&emap, pdfSamples);
611    
612     /* Number of photons to emit from ziss partishunn --
613     * proportional to flux; photon ray weight and scalar
614     * flux are uniform (the latter only varying in RGB).
615     * */
616     partNumEmit = numEmit * colorAvg(emap.partFlux) /
617     totalFlux;
618     partEmitCnt = (unsigned long)partNumEmit;
619    
620     /* Probabilistically account for fractional photons */
621     if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
622     partEmitCnt++;
623    
624     /* Update local and shared (global) emission counter */
625     photonCnt -> numEmitted += partEmitCnt;
626     localNumEmitted += partEmitCnt;
627    
628     /* Integer counter avoids FP rounding errors during
629     * iteration */
630     while (partEmitCnt--) {
631     RAY photonRay;
632    
633     /* Emit photon based on PDF and trace through scene
634     * until absorbed/leaked */
635     emitPhoton(&emap, &photonRay);
636     tracePhoton(&photonRay);
637     }
638    
639     /* Update shared global photon count for each pmap */
640     for (t = 0; t < NUM_PMAP_TYPES; t++)
641     if (pmaps [t]) {
642     photonCnt -> numPhotons [t] +=
643     pmaps [t] -> numPhotons - lastNumPhotons [t];
644     lastNumPhotons [t] = pmaps [t] -> numPhotons;
645     }
646     }
647 greg 2.1
648 rschregle 2.11 portCnt++;
649     } while (portCnt < numPhotonPorts);
650     }
651    
652     for (t = 0; t < NUM_PMAP_TYPES; t++)
653     if (pmaps [t] && !pmaps [t] -> numPhotons) {
654     /* Double preDistrib in case a photon map is empty and
655     * redo pass 1 --> possibility of infinite loop for
656     * pathological scenes (e.g. absorbing materials) */
657     preDistrib *= 2;
658     break;
659 greg 2.1 }
660 rschregle 2.11
661     if (t >= NUM_PMAP_TYPES) {
662     /* No empty photon maps found; now do pass 2 */
663     passCnt++;
664     #if 0
665     if (photonRepTime)
666     eputs("\n");
667     #endif
668 greg 2.1 }
669 rschregle 2.11 } while (passCnt < 2);
670    
671     /* Unmap shared photon counters */
672     #if 0
673     munmap(photonCnt, sizeof(*photonCnt));
674     close(shmFile);
675     #endif
676    
677     /* Flush heap buffa for every pmap one final time; this is required
678     * to prevent data corruption! */
679     for (t = 0; t < NUM_PMAP_TYPES; t++)
680     if (pmaps [t]) {
681     #if 0
682     eputs("Final flush\n");
683     #endif
684     flushPhotonHeap(pmaps [t]);
685     fclose(pmaps [t] -> heap);
686     #ifdef DEBUG_PMAP
687     sprintf(errmsg, "Proc %d: total %ld photons\n", getpid(),
688     pmaps [t] -> numPhotons);
689     eputs(errmsg);
690     #endif
691     }
692    
693     exit(0);
694 greg 2.1 }
695 rschregle 2.11 else if (pid < 0)
696     error(SYSTEM, "failed to fork subprocess in distribPhotons");
697     }
698    
699     /* PARENT PROCESS CONTINUES HERE */
700     /* Record start time and enable progress report signal handler */
701     repStartTime = time(NULL);
702     #ifdef SIGCONT
703     signal(SIGCONT, pmapDistribReport);
704     #endif
705    
706     if (photonRepTime)
707     eputs("\n");
708    
709     /* Wait for subprocesses to complete while reporting progress */
710     proc = numProc;
711     while (proc) {
712     while (waitpid(-1, &stat, WNOHANG) > 0) {
713     /* Subprocess exited; check status */
714     if (!WIFEXITED(stat) || WEXITSTATUS(stat))
715     error(USER, "failed photon distribution");
716    
717     --proc;
718     }
719    
720     /* Nod off for a bit and update progress */
721     sleep(1);
722     /* Update progress report from shared subprocess counters */
723     repEmitted = repProgress = photonCnt -> numEmitted;
724     repComplete = photonCnt -> numComplete;
725    
726 greg 2.1 for (t = 0; t < NUM_PMAP_TYPES; t++)
727 rschregle 2.11 if ((pm = pmaps [t])) {
728     #if 0
729     /* Get photon count from heapfile size for progress update */
730     fseek(pm -> heap, 0, SEEK_END);
731     pm -> numPhotons = ftell(pm -> heap) / sizeof(Photon); */
732     #else
733     /* Get global photon count from shmem updated by subprocs */
734     pm -> numPhotons = photonCnt -> numPhotons [t];
735     #endif
736 greg 2.1 }
737 rschregle 2.11
738     if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
739     pmapDistribReport();
740     #ifdef SIGCONT
741     else signal(SIGCONT, pmapDistribReport);
742     #endif
743     }
744 greg 2.1
745     /* ===================================================================
746 rschregle 2.11 * POST-DISTRIBUTION - Set photon flux and build data struct for photon
747     * storage, etc.
748 greg 2.1 * =================================================================== */
749 rschregle 2.11 #ifdef SIGCONT
750     signal(SIGCONT, SIG_DFL);
751     #endif
752 greg 2.1 free(emap.samples);
753    
754     /* Set photon flux (repProgress is total num emitted) */
755 rschregle 2.11 totalFlux /= photonCnt -> numEmitted;
756    
757     /* Photon counters no longer needed, unmap shared memory */
758     munmap(photonCnt, sizeof(*photonCnt));
759     close(shmFile);
760     #if 0
761     shm_unlink(shmFname);
762     #else
763     unlink(shmFname);
764     #endif
765 greg 2.1
766     for (t = 0; t < NUM_PMAP_TYPES; t++)
767 rschregle 2.8 if (pmaps [t]) {
768 greg 2.1 if (photonRepTime) {
769     sprintf(errmsg, "\nBuilding %s photon map...\n", pmapName [t]);
770     eputs(errmsg);
771     fflush(stderr);
772     }
773 rschregle 2.11
774     /* Build underlying data structure; heap is destroyed */
775     buildPhotonMap(pmaps [t], &totalFlux, NULL, numProc);
776 greg 2.1 }
777 rschregle 2.11
778 greg 2.1 /* Precompute photon irradiance if necessary */
779     if (preCompPmap)
780     preComputeGlobal(preCompPmap);
781     }
782    
783    
784    
785     void photonDensity (PhotonMap *pmap, RAY *ray, COLOR irrad)
786     /* Photon density estimate. Returns irradiance at ray -> rop. */
787     {
788 rschregle 2.11 unsigned i;
789     float r;
790     COLOR flux;
791     Photon *photon;
792     const PhotonSearchQueueNode *sqn;
793 greg 2.1
794     setcolor(irrad, 0, 0, 0);
795    
796     if (!pmap -> maxGather)
797     return;
798    
799     /* Ignore sources */
800 rschregle 2.11 if (ray -> ro && islight(objptr(ray -> ro -> omod) -> otype))
801     return;
802 greg 2.1
803     findPhotons(pmap, ray);
804    
805     /* Need at least 2 photons */
806 rschregle 2.11 if (pmap -> squeue.tail < 2) {
807     #ifdef PMAP_NONEFOUND
808     sprintf(errmsg, "no photons found on %s at (%.3f, %.3f, %.3f)",
809     ray -> ro ? ray -> ro -> oname : "<null>",
810     ray -> rop [0], ray -> rop [1], ray -> rop [2]);
811     error(WARNING, errmsg);
812     #endif
813 greg 2.1
814     return;
815     }
816 rschregle 2.11
817 greg 2.1 if (pmap -> minGather == pmap -> maxGather) {
818     /* No bias compensation. Just do a plain vanilla estimate */
819 rschregle 2.11 sqn = pmap -> squeue.node + 1;
820 greg 2.1
821     /* Average radius between furthest two photons to improve accuracy */
822 rschregle 2.11 r = max(sqn -> dist2, (sqn + 1) -> dist2);
823     r = 0.25 * (pmap -> maxDist2 + r + 2 * sqrt(pmap -> maxDist2 * r));
824 greg 2.1
825     /* Skip the extra photon */
826 rschregle 2.11 for (i = 1 ; i < pmap -> squeue.tail; i++, sqn++) {
827     photon = getNearestPhoton(&pmap -> squeue, sqn -> idx);
828     getPhotonFlux(photon, flux);
829 greg 2.1 #ifdef PMAP_EPANECHNIKOV
830 rschregle 2.11 /* Apply Epanechnikov kernel to photon flux based on photon dist */
831     scalecolor(flux, 2 * (1 - sqn -> dist2 / r));
832     #endif
833 greg 2.1 addcolor(irrad, flux);
834     }
835    
836     /* Divide by search area PI * r^2, 1 / PI required as ambient
837     normalisation factor */
838     scalecolor(irrad, 1 / (PI * PI * r));
839    
840     return;
841     }
842     else
843     /* Apply bias compensation to density estimate */
844     biasComp(pmap, irrad);
845     }
846    
847    
848    
849     void photonPreCompDensity (PhotonMap *pmap, RAY *r, COLOR irrad)
850     /* Returns precomputed photon density estimate at ray -> rop. */
851     {
852 rschregle 2.11 Photon p;
853 greg 2.1
854     setcolor(irrad, 0, 0, 0);
855    
856     /* Ignore sources */
857     if (r -> ro && islight(objptr(r -> ro -> omod) -> otype))
858     return;
859    
860 rschregle 2.11 find1Photon(preCompPmap, r, &p);
861     getPhotonFlux(&p, irrad);
862 greg 2.1 }
863    
864    
865    
866     void volumePhotonDensity (PhotonMap *pmap, RAY *ray, COLOR irrad)
867     /* Photon volume density estimate. Returns irradiance at ray -> rop. */
868     {
869 rschregle 2.11 unsigned i;
870     float r, gecc2, ph;
871     COLOR flux;
872     Photon *photon;
873     const PhotonSearchQueueNode *sqn;
874 greg 2.1
875     setcolor(irrad, 0, 0, 0);
876    
877     if (!pmap -> maxGather)
878     return;
879    
880     findPhotons(pmap, ray);
881    
882     /* Need at least 2 photons */
883 rschregle 2.11 if (pmap -> squeue.tail < 2)
884 greg 2.1 return;
885 rschregle 2.11
886     #if 0
887     /* Volume biascomp disabled (probably redundant) */
888     if (pmap -> minGather == pmap -> maxGather)
889     #endif
890     {
891 greg 2.1 /* No bias compensation. Just do a plain vanilla estimate */
892     gecc2 = ray -> gecc * ray -> gecc;
893 rschregle 2.11 sqn = pmap -> squeue.node + 1;
894 greg 2.1
895     /* Average radius between furthest two photons to improve accuracy */
896 rschregle 2.11 r = max(sqn -> dist2, (sqn + 1) -> dist2);
897     r = 0.25 * (pmap -> maxDist2 + r + 2 * sqrt(pmap -> maxDist2 * r));
898 greg 2.1
899     /* Skip the extra photon */
900 rschregle 2.11 for (i = 1; i < pmap -> squeue.tail; i++, sqn++) {
901     photon = getNearestPhoton(&pmap -> squeue, sqn -> idx);
902    
903 greg 2.1 /* Compute phase function for inscattering from photon */
904     if (gecc2 <= FTINY)
905     ph = 1;
906     else {
907 rschregle 2.11 ph = DOT(ray -> rdir, photon -> norm) / 127;
908 greg 2.1 ph = 1 + gecc2 - 2 * ray -> gecc * ph;
909     ph = (1 - gecc2) / (ph * sqrt(ph));
910     }
911    
912 rschregle 2.11 getPhotonFlux(photon, flux);
913 greg 2.1 scalecolor(flux, ph);
914     addcolor(irrad, flux);
915     }
916    
917     /* Divide by search volume 4 / 3 * PI * r^3 and phase function
918     normalization factor 1 / (4 * PI) */
919     scalecolor(irrad, 3 / (16 * PI * PI * r * sqrt(r)));
920     return;
921     }
922 rschregle 2.11 #if 0
923 greg 2.1 else
924     /* Apply bias compensation to density estimate */
925     volumeBiasComp(pmap, ray, irrad);
926 rschregle 2.11 #endif
927 greg 2.1 }