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 (8 years 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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pmap.c,v 4.33.1.10 2016/05/11 12:50:54 taschreg Exp taschreg $";
3 #endif
4
5 /*
6 ======================================================================
7 Photon map main module
8
9 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
10 (c) Fraunhofer Institute for Solar Energy Systems,
11 (c) Lucerne University of Applied Sciences and Arts,
12 supported by the Swiss National Science Foundation (SNSF, #147053)
13 ======================================================================
14
15 $Id: pmap.c,v 4.33.1.10 2016/05/11 12:50:54 taschreg Exp taschreg $
16 */
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 #include <sys/mman.h>
31 #include <sys/wait.h>
32
33 #define PMAP_REV "$Revision: 4.33.1.10 $"
34
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 if (pm -> fileName && octname &&
74 !stat(pm -> fileName, &pmstat) && !stat(octname, &octstat) &&
75 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 if (pm -> maxGather > pm -> numPhotons) {
95 error(WARNING, "adjusting density estimate bandwidth");
96 pm -> minGather = pm -> maxGather = pm -> numPhotons;
97 }
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 savePhotonMap(pmaps [t], pmaps [t] -> fileName, argc, argv);
110 }
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 if (albedo > FTINY && ray -> rlvl > 0)
154 /* Add to volume photon map */
155 newPhoton(volumePmap, ray);
156
157 /* Absorbed? */
158 if (pmapRandom(rouletteState) > albedo)
159 return 0;
160
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 #ifdef PMAP_RUNAWAY_WARN
216 error(WARNING, "runaway photon!");
217 #endif
218 return;
219 }
220
221 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 /* 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
256 repComplete = numPreComp = finalGather * pmap -> numPhotons;
257
258 if (photonRepTime) {
259 sprintf(errmsg, "Precomputing irradiance for %ld global photons...\n",
260 numPreComp);
261 eputs(errmsg);
262 fflush(stderr);
263 }
264
265 /* 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 }
276
277 /* Record start time, baby */
278 repStartTime = time(NULL);
279 #ifdef SIGCONT
280 signal(SIGCONT, pmapPreCompReport);
281 #endif
282 repProgress = 0;
283
284 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
306 /* Update progress */
307 repProgress++;
308
309 if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
310 pmapPreCompReport();
311 #ifdef SIGCONT
312 else signal(SIGCONT, pmapPreCompReport);
313 #endif
314 }
315
316 /* 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
327 if (photonRepTime) {
328 eputs("Rebuilding precomputed photon map...\n");
329 fflush(stderr);
330 }
331
332 /* Rebuild underlying data structure, destroying heap */
333 buildPhotonMap(pmap, NULL, NULL, 1);
334 }
335
336
337
338 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
355 for (t = 0; t < NUM_PMAP_TYPES && !pmaps [t]; t++);
356
357 if (t >= NUM_PMAP_TYPES)
358 error(USER, "no photon maps defined in distribPhotons");
359
360 if (!nsources)
361 error(USER, "no light sources in distribPhotons");
362
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 error(INTERNAL, "can't allocate source partitions in distribPhotons");
371
372 /* Initialise all defined photon maps */
373 for (t = 0; t < NUM_PMAP_TYPES; t++)
374 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
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 /* 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 if (photonRepTime)
414 eputs("\n");
415
416 /* ===================================================================
417 * FLUX INTEGRATION - Get total photon flux from light sources
418 * =================================================================== */
419 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
420 unsigned portCnt = 0;
421 emap.src = source + srcIdx;
422
423 do { /* Need at least one iteration if no ports! */
424 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 /* 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
467 /* 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
475 for (t = 0; t < NUM_PMAP_TYPES; t++)
476 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 do {
486 double numEmit;
487
488 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 }
508
509 /* Num to emit is fraction of minimum target count */
510 numEmit = FHUGE;
511
512 for (t = 0; t < NUM_PMAP_TYPES; t++)
513 if (pmaps [t])
514 numEmit = min(pmaps [t] -> distribTarget, numEmit);
515
516 numEmit *= preDistrib;
517 }
518 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
557 /* 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
602 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
648 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 }
660
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 }
669 } 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 }
695 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 for (t = 0; t < NUM_PMAP_TYPES; t++)
727 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 }
737
738 if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
739 pmapDistribReport();
740 #ifdef SIGCONT
741 else signal(SIGCONT, pmapDistribReport);
742 #endif
743 }
744
745 /* ===================================================================
746 * POST-DISTRIBUTION - Set photon flux and build data struct for photon
747 * storage, etc.
748 * =================================================================== */
749 #ifdef SIGCONT
750 signal(SIGCONT, SIG_DFL);
751 #endif
752 free(emap.samples);
753
754 /* Set photon flux (repProgress is total num emitted) */
755 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
766 for (t = 0; t < NUM_PMAP_TYPES; t++)
767 if (pmaps [t]) {
768 if (photonRepTime) {
769 sprintf(errmsg, "\nBuilding %s photon map...\n", pmapName [t]);
770 eputs(errmsg);
771 fflush(stderr);
772 }
773
774 /* Build underlying data structure; heap is destroyed */
775 buildPhotonMap(pmaps [t], &totalFlux, NULL, numProc);
776 }
777
778 /* 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 unsigned i;
789 float r;
790 COLOR flux;
791 Photon *photon;
792 const PhotonSearchQueueNode *sqn;
793
794 setcolor(irrad, 0, 0, 0);
795
796 if (!pmap -> maxGather)
797 return;
798
799 /* Ignore sources */
800 if (ray -> ro && islight(objptr(ray -> ro -> omod) -> otype))
801 return;
802
803 findPhotons(pmap, ray);
804
805 /* Need at least 2 photons */
806 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
814 return;
815 }
816
817 if (pmap -> minGather == pmap -> maxGather) {
818 /* No bias compensation. Just do a plain vanilla estimate */
819 sqn = pmap -> squeue.node + 1;
820
821 /* Average radius between furthest two photons to improve accuracy */
822 r = max(sqn -> dist2, (sqn + 1) -> dist2);
823 r = 0.25 * (pmap -> maxDist2 + r + 2 * sqrt(pmap -> maxDist2 * r));
824
825 /* Skip the extra photon */
826 for (i = 1 ; i < pmap -> squeue.tail; i++, sqn++) {
827 photon = getNearestPhoton(&pmap -> squeue, sqn -> idx);
828 getPhotonFlux(photon, flux);
829 #ifdef PMAP_EPANECHNIKOV
830 /* Apply Epanechnikov kernel to photon flux based on photon dist */
831 scalecolor(flux, 2 * (1 - sqn -> dist2 / r));
832 #endif
833 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 Photon p;
853
854 setcolor(irrad, 0, 0, 0);
855
856 /* Ignore sources */
857 if (r -> ro && islight(objptr(r -> ro -> omod) -> otype))
858 return;
859
860 find1Photon(preCompPmap, r, &p);
861 getPhotonFlux(&p, irrad);
862 }
863
864
865
866 void volumePhotonDensity (PhotonMap *pmap, RAY *ray, COLOR irrad)
867 /* Photon volume density estimate. Returns irradiance at ray -> rop. */
868 {
869 unsigned i;
870 float r, gecc2, ph;
871 COLOR flux;
872 Photon *photon;
873 const PhotonSearchQueueNode *sqn;
874
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 if (pmap -> squeue.tail < 2)
884 return;
885
886 #if 0
887 /* Volume biascomp disabled (probably redundant) */
888 if (pmap -> minGather == pmap -> maxGather)
889 #endif
890 {
891 /* No bias compensation. Just do a plain vanilla estimate */
892 gecc2 = ray -> gecc * ray -> gecc;
893 sqn = pmap -> squeue.node + 1;
894
895 /* Average radius between furthest two photons to improve accuracy */
896 r = max(sqn -> dist2, (sqn + 1) -> dist2);
897 r = 0.25 * (pmap -> maxDist2 + r + 2 * sqrt(pmap -> maxDist2 * r));
898
899 /* Skip the extra photon */
900 for (i = 1; i < pmap -> squeue.tail; i++, sqn++) {
901 photon = getNearestPhoton(&pmap -> squeue, sqn -> idx);
902
903 /* Compute phase function for inscattering from photon */
904 if (gecc2 <= FTINY)
905 ph = 1;
906 else {
907 ph = DOT(ray -> rdir, photon -> norm) / 127;
908 ph = 1 + gecc2 - 2 * ray -> gecc * ph;
909 ph = (1 - gecc2) / (ph * sqrt(ph));
910 }
911
912 getPhotonFlux(photon, flux);
913 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 #if 0
923 else
924 /* Apply bias compensation to density estimate */
925 volumeBiasComp(pmap, ray, irrad);
926 #endif
927 }