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

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pmapcontrib.c,v 4.30.1.12 2016/05/11 12:40:00 taschreg Exp taschreg $";
3 #endif
4
5 /*
6 ======================================================================
7 Photon map support for light source contributions
8
9 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
10 (c) Lucerne University of Applied Sciences and Arts,
11 supported by the Swiss National Science Foundation (SNSF, #147053)
12 ======================================================================
13
14 $Id: pmapcontrib.c,v 4.30.1.12 2016/05/11 12:40:00 taschreg Exp taschreg $
15 */
16
17
18 #include "pmapcontrib.h"
19 #include "pmapmat.h"
20 #include "pmapsrc.h"
21 #include "pmaprand.h"
22 #include "pmapio.h"
23 #include "pmapdiag.h"
24 #include "rcontrib.h"
25 #include "otypes.h"
26 #include <sys/mman.h>
27 #include <sys/wait.h>
28
29
30
31 static void setPmapContribParams (PhotonMap *pmap, LUTAB *srcContrib)
32 /* Set parameters for light source contributions */
33 {
34 /* Set light source modifier list and appropriate callback to extract
35 * their contributions from the photon map */
36 if (pmap) {
37 pmap -> srcContrib = srcContrib;
38 pmap -> lookup = photonContrib;
39 /* Ensure we get all requested photon contribs during lookups */
40 pmap -> gatherTolerance = 1.0;
41 }
42 }
43
44
45
46 static void checkPmapContribs (const PhotonMap *pmap, LUTAB *srcContrib)
47 /* Check modifiers for light source contributions */
48 {
49 const PhotonPrimary *primary = pmap -> primaries;
50 PhotonPrimaryIdx i, found = 0;
51 OBJREC *srcMod;
52
53 /* Make sure at least one of the modifiers is actually in the pmap,
54 * otherwise findPhotons() winds up in an infinite loop! */
55 for (i = pmap -> numPrimary; i; --i, ++primary) {
56 if (primary -> srcIdx < 0 || primary -> srcIdx >= nsources)
57 error(INTERNAL, "invalid light source index in photon map");
58
59 srcMod = findmaterial(source [primary -> srcIdx].so);
60 if ((MODCONT*)lu_find(srcContrib, srcMod -> oname) -> data)
61 ++found;
62 }
63
64 if (!found)
65 error(USER, "modifiers not in photon map");
66 }
67
68
69
70 void initPmapContrib (LUTAB *srcContrib, unsigned numSrcContrib)
71 {
72 unsigned t;
73
74 for (t = 0; t < NUM_PMAP_TYPES; t++)
75 if (photonMaps [t] && t != PMAP_TYPE_CONTRIB) {
76 sprintf(errmsg, "%s photon map does not support contributions",
77 pmapName [t]);
78 error(USER, errmsg);
79 }
80
81 /* Get params */
82 setPmapContribParams(contribPmap, srcContrib);
83
84 if (contribPhotonMapping) {
85 if (contribPmap -> maxGather < numSrcContrib) {
86 /* Adjust density estimate bandwidth if lower than modifier
87 * count, otherwise contributions are missing */
88 error(WARNING, "contrib density estimate bandwidth too low, "
89 "adjusting to modifier count");
90 contribPmap -> maxGather = numSrcContrib;
91 }
92
93 /* Sanity check */
94 checkPmapContribs(contribPmap, srcContrib);
95 }
96 }
97
98
99
100 static PhotonPrimaryIdx newPhotonPrimary (PhotonMap *pmap,
101 const RAY *primRay,
102 FILE *primHeap)
103 /* Add primary ray for emitted photon and save light source index, origin on
104 * source, and emitted direction; used by contrib photons. The current
105 * primary is stored in pmap -> lastPrimary. If the previous primary
106 * contributed photons (has srcIdx >= 0), it's appended to primHeap. If
107 * primRay == NULL, the current primary is still flushed, but no new primary
108 * is set. Returns updated primary counter pmap -> numPrimary. */
109 {
110 if (!pmap || !primHeap)
111 return 0;
112
113 /* Check if last primary ray has spawned photons (srcIdx >= 0, see
114 * newPhoton()), in which case we write it to the primary heap file
115 * before overwriting it */
116 if (pmap -> lastPrimary.srcIdx >= 0) {
117 if (!fwrite(&pmap -> lastPrimary, sizeof(PhotonPrimary), 1, primHeap))
118 error(SYSTEM, "failed writing photon primary in newPhotonPrimary");
119
120 pmap -> numPrimary++;
121 if (pmap -> numPrimary > PMAP_MAXPRIMARY)
122 error(INTERNAL, "photon primary overflow in newPhotonPrimary");
123 }
124
125 /* Mark unused with negative source index until path spawns a photon (see
126 * newPhoton()) */
127 pmap -> lastPrimary.srcIdx = -1;
128
129 if (primRay) {
130 FVECT dvec;
131
132 /* Reverse incident direction to point to light source */
133 dvec [0] = -primRay -> rdir [0];
134 dvec [1] = -primRay -> rdir [1];
135 dvec [2] = -primRay -> rdir [2];
136 pmap -> lastPrimary.dir = encodedir(dvec);
137 #ifdef PMAP_PRIMARYPOS
138 VCOPY(pmap -> lastPrimary.pos, primRay -> rop);
139 #endif
140 }
141
142 return pmap -> numPrimary;
143 }
144
145
146
147 #ifdef DEBUG_PMAP_CONTRIB
148 static int checkPrimaryHeap (FILE *file)
149 /* Check heap for ordered primaries */
150 {
151 Photon p, lastp;
152 int i, dup;
153
154 rewind(file);
155 memset(&lastp, 0, sizeof(lastp));
156
157 while (fread(&p, sizeof(p), 1, file)) {
158 dup = 1;
159
160 for (i = 0; i <= 2; i++) {
161 if (p.pos [i] < thescene.cuorg [i] ||
162 p.pos [i] > thescene.cuorg [i] + thescene.cusize) {
163
164 sprintf(errmsg, "corrupt photon in heap at [%f, %f, %f]\n",
165 p.pos [0], p.pos [1], p.pos [2]);
166 error(WARNING, errmsg);
167 }
168
169 dup &= p.pos [i] == lastp.pos [i];
170 }
171
172 if (dup) {
173 sprintf(errmsg,
174 "consecutive duplicate photon in heap at [%f, %f, %f]\n",
175 p.pos [0], p.pos [1], p.pos [2]);
176 error(WARNING, errmsg);
177 }
178 }
179
180 return 0;
181 }
182 #endif
183
184
185
186 static PhotonPrimaryIdx buildPrimaries (PhotonMap *pmap, FILE **primaryHeap,
187 PhotonPrimaryIdx *primaryOfs,
188 unsigned numHeaps)
189 /* Consolidate per-subprocess photon primary heaps into the primary array
190 * pmap -> primaries. Returns offset for primary index linearisation in
191 * numPrimary. The heap files in primaryHeap are closed on return. */
192 {
193 PhotonPrimaryIdx heapLen;
194 unsigned heap;
195
196 if (!pmap || !primaryHeap || !primaryOfs || !numHeaps)
197 return 0;
198
199 pmap -> numPrimary = 0;
200
201 for (heap = 0; heap < numHeaps; heap++) {
202 primaryOfs [heap] = pmap -> numPrimary;
203
204 if (fseek(primaryHeap [heap], 0, SEEK_END))
205 error(SYSTEM, "failed photon primary seek in buildPrimaries");
206 pmap -> numPrimary += heapLen = ftell(primaryHeap [heap]) /
207 sizeof(PhotonPrimary);
208
209 pmap -> primaries = realloc(pmap -> primaries,
210 pmap -> numPrimary *
211 sizeof(PhotonPrimary));
212 if (!pmap -> primaries)
213 error(SYSTEM, "failed photon primary alloc in buildPrimaries");
214
215 rewind(primaryHeap [heap]);
216 if (fread(pmap -> primaries + primaryOfs [heap], sizeof(PhotonPrimary),
217 heapLen, primaryHeap [heap]) != heapLen)
218 error(SYSTEM, "failed reading photon primaries in buildPrimaries");
219
220 fclose(primaryHeap [heap]);
221 }
222
223 return pmap -> numPrimary;
224 }
225
226
227
228 /* Defs for photon emission counter array passed by sub-processes to parent
229 * via shared memory */
230 typedef unsigned long PhotonContribCnt;
231
232 /* Indices for photon emission counter array: num photons stored and num
233 * emitted per source */
234 #define PHOTONCNT_NUMPHOT 0
235 #define PHOTONCNT_NUMEMIT(n) (1 + n)
236
237
238
239 void distribPhotonContrib (PhotonMap* pm, unsigned numProc)
240 {
241 EmissionMap emap;
242 char errmsg2 [128], shmFname [255];
243 unsigned srcIdx, proc;
244 int shmFile, stat, pid;
245 double *srcFlux, /* Emitted flux per light source */
246 srcDistribTarget; /* Target photon count per source */
247 PhotonContribCnt *photonCnt; /* Photon emission counter array */
248 const unsigned photonCntSize = sizeof(PhotonContribCnt) *
249 PHOTONCNT_NUMEMIT(nsources);
250 FILE *primaryHeap [numProc];
251 PhotonPrimaryIdx primaryOfs [numProc];
252
253 if (!pm)
254 error(USER, "no photon map defined in distribPhotonContrib");
255
256 if (!nsources)
257 error(USER, "no light sources in distribPhotonContrib");
258
259 if (nsources > MAXMODLIST)
260 error(USER, "too many light sources in distribPhotonContrib");
261
262 /* Allocate photon flux per light source; this differs for every
263 * source as all sources contribute the same number of distributed
264 * photons (srcDistribTarget), hence the number of photons emitted per
265 * source does not correlate with its emitted flux. The resulting flux
266 * per photon is therefore adjusted individually for each source. */
267 if (!(srcFlux = calloc(nsources, sizeof(double))))
268 error(SYSTEM, "can't allocate source flux in distribPhotonContrib");
269
270 /* ===================================================================
271 * INITIALISATION - Set up emission and scattering funcs
272 * =================================================================== */
273 emap.samples = NULL;
274 emap.src = NULL;
275 emap.maxPartitions = MAXSPART;
276 emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
277 if (!emap.partitions)
278 error(USER, "can't allocate source partitions in distribPhotonContrib");
279
280 /* Initialise contrib photon map */
281 initPhotonMap(pm, PMAP_TYPE_CONTRIB);
282 initPhotonHeap(pm);
283 initPhotonEmissionFuncs();
284 initPhotonScatterFuncs();
285
286 /* Per-subprocess / per-source target counts */
287 pm -> distribTarget /= numProc;
288 srcDistribTarget = nsources ? (double)pm -> distribTarget / nsources : 0;
289
290 /* Get photon ports if specified */
291 if (ambincl == 1)
292 getPhotonPorts();
293
294 /* Get photon sensor modifiers */
295 getPhotonSensors(photonSensorList);
296
297 /* Set up shared mem for photon counters (zeroed by ftruncate) */
298 #if 0
299 snprintf(shmFname, 255, PMAP_SHMFNAME, getpid());
300 shmFile = shm_open(shmFname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
301 #else
302 strcpy(shmFname, PMAP_SHMFNAME);
303 shmFile = mkstemp(shmFname);
304 #endif
305
306 if (shmFile < 0 || ftruncate(shmFile, photonCntSize) < 0)
307 error(SYSTEM, "failed shared mem init in distribPhotonContrib");
308
309 photonCnt = mmap(NULL, photonCntSize, PROT_READ | PROT_WRITE,
310 MAP_SHARED, shmFile, 0);
311
312 if (photonCnt == MAP_FAILED)
313 error(SYSTEM, "failed shared mem mapping in distribPhotonContrib");
314
315 /* =============================================================
316 * FLUX INTEGRATION - Get total flux emitted from light source
317 * ============================================================= */
318 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
319 unsigned portCnt = 0;
320
321 srcFlux [srcIdx] = 0;
322 emap.src = source + srcIdx;
323
324 if (photonRepTime)
325 eputs("\n");
326
327 do { /* Need at least one iteration if no ports! */
328 emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
329 : NULL;
330 photonPartition [emap.src -> so -> otype] (&emap);
331
332 if (photonRepTime) {
333 sprintf(errmsg, "Integrating flux from source %s (mod %s) ",
334 source [srcIdx].so -> oname,
335 objptr(source [srcIdx].so -> omod) -> oname);
336
337 if (emap.port) {
338 sprintf(errmsg2, "via port %s ",
339 photonPorts [portCnt].so -> oname);
340 strcat(errmsg, errmsg2);
341 }
342
343 sprintf(errmsg2, "(%lu partitions)...\n", emap.numPartitions);
344 strcat(errmsg, errmsg2);
345 eputs(errmsg);
346 fflush(stderr);
347 }
348
349 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
350 emap.partitionCnt++) {
351 initPhotonEmission(&emap, pdfSamples);
352 srcFlux [srcIdx] += colorAvg(emap.partFlux);
353 }
354
355 portCnt++;
356 } while (portCnt < numPhotonPorts);
357
358 if (srcFlux [srcIdx] < FTINY) {
359 sprintf(errmsg, "source %s has zero emission",
360 source [srcIdx].so -> oname);
361 error(WARNING, errmsg);
362 }
363 }
364
365 if (photonRepTime)
366 eputs("\n");
367
368 /* Init per-subprocess primary heap files */
369 for (proc = 0; proc < numProc; proc++)
370 if (!(primaryHeap [proc] = tmpfile()))
371 error(SYSTEM, "failed opening primary heap file in "
372 "distribPhotonContrib");
373
374 /* MAIN LOOP */
375 for (proc = 0; proc < numProc; proc++) {
376 if (!(pid = fork())) {
377 /* SUBPROCESS ENTERS HERE;
378 * all opened and memory mapped files are inherited */
379
380 /* Local photon counters for this subprocess */
381 unsigned long lastNumPhotons = 0, localNumEmitted = 0;
382 double photonFluxSum = 0; /* Running photon flux sum */
383
384 /* Seed RNGs from PID for decorellated photon distribution */
385 pmapSeed(randSeed + proc, partState);
386 pmapSeed(randSeed + proc, emitState);
387 pmapSeed(randSeed + proc, cntState);
388 pmapSeed(randSeed + proc, mediumState);
389 pmapSeed(randSeed + proc, scatterState);
390 pmapSeed(randSeed + proc, rouletteState);
391
392 /* =============================================================
393 * 2-PASS PHOTON DISTRIBUTION
394 * Pass 1 (pre): emit fraction of target photon count
395 * Pass 2 (main): based on outcome of pass 1, estimate remaining
396 * number of photons to emit to approximate target
397 * count
398 * ============================================================= */
399 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
400 unsigned portCnt, passCnt = 0, prePassCnt = 0;
401 float srcPreDistrib = preDistrib;
402 double srcNumEmit = 0; /* # to emit from source */
403 unsigned long srcNumDistrib = pm -> numPhotons; /* # stored */
404
405 if (srcFlux [srcIdx] < FTINY)
406 continue;
407
408 while (passCnt < 2) {
409 if (!passCnt) {
410 /* INIT PASS 1 */
411 if (++prePassCnt > maxPreDistrib) {
412 /* Warn if no photons contributed after sufficient
413 * iterations */
414 sprintf(errmsg, "proc %d, source %s: "
415 "too many prepasses, skipped",
416 proc, source [srcIdx].so -> oname);
417 error(WARNING, errmsg);
418 break;
419 }
420
421 /* Num to emit is fraction of target count */
422 srcNumEmit = srcPreDistrib * srcDistribTarget;
423 }
424 else {
425 /* INIT PASS 2 */
426 double srcPhotonFlux, avgPhotonFlux;
427
428 /* Based on the outcome of the predistribution we can now
429 * figure out how many more photons we have to emit from
430 * the current source to meet the target count,
431 * srcDistribTarget. This value is clamped to 0 in case
432 * the target has already been exceeded in pass 1.
433 * srcNumEmit and srcNumDistrib is the number of photons
434 * emitted and distributed (stored) from the current
435 * source in pass 1, respectively. */
436 srcNumDistrib = pm -> numPhotons - srcNumDistrib;
437 srcNumEmit *= srcNumDistrib
438 ? max(srcDistribTarget/srcNumDistrib, 1) - 1
439 : 0;
440
441 if (!srcNumEmit)
442 /* No photons left to distribute in main pass */
443 break;
444
445 srcPhotonFlux = srcFlux [srcIdx] / srcNumEmit;
446 avgPhotonFlux = photonFluxSum / (srcIdx + 1);
447
448 if (avgPhotonFlux > 0 &&
449 srcPhotonFlux / avgPhotonFlux < FTINY) {
450 /* Skip source if its photon flux is grossly below the
451 * running average, indicating negligible contribs at
452 * the expense of excessive distribution time */
453 sprintf(errmsg, "proc %d, source %s: "
454 "itsy bitsy photon flux, skipped",
455 proc, source [srcIdx].so -> oname);
456 error(WARNING, errmsg);
457 srcNumEmit = 0;
458 }
459
460 /* Update sum of photon flux per light source */
461 photonFluxSum += srcPhotonFlux;
462 }
463
464 portCnt = 0;
465 do { /* Need at least one iteration if no ports! */
466 emap.src = source + srcIdx;
467 emap.port = emap.src -> sflags & SDISTANT
468 ? photonPorts + portCnt : NULL;
469 photonPartition [emap.src -> so -> otype] (&emap);
470
471 if (photonRepTime && !proc) {
472 if (!passCnt)
473 sprintf(errmsg, "PREPASS %d on source %s (mod %s) ",
474 prePassCnt, source [srcIdx].so -> oname,
475 objptr(source[srcIdx].so->omod) -> oname);
476 else
477 sprintf(errmsg, "MAIN PASS on source %s (mod %s) ",
478 source [srcIdx].so -> oname,
479 objptr(source[srcIdx].so->omod) -> oname);
480
481 if (emap.port) {
482 sprintf(errmsg2, "via port %s ",
483 photonPorts [portCnt].so -> oname);
484 strcat(errmsg, errmsg2);
485 }
486
487 sprintf(errmsg2, "(%lu partitions)\n",
488 emap.numPartitions);
489 strcat(errmsg, errmsg2);
490 eputs(errmsg);
491 fflush(stderr);
492 }
493
494 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
495 emap.partitionCnt++) {
496 double partNumEmit;
497 unsigned long partEmitCnt;
498
499 /* Get photon origin within current source partishunn
500 * and build emission map */
501 photonOrigin [emap.src -> so -> otype] (&emap);
502 initPhotonEmission(&emap, pdfSamples);
503
504 /* Number of photons to emit from ziss partishunn;
505 * scale according to its normalised contribushunn to
506 * the emitted source flux */
507 partNumEmit = srcNumEmit * colorAvg(emap.partFlux) /
508 srcFlux [srcIdx];
509 partEmitCnt = (unsigned long)partNumEmit;
510
511 /* Probabilistically account for fractional photons */
512 if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
513 partEmitCnt++;
514
515 /* Update local and shared global emission counter */
516 localNumEmitted += partEmitCnt;
517 photonCnt [PHOTONCNT_NUMEMIT(srcIdx)] += partEmitCnt;
518
519 /* Integer counter avoids FP rounding errors */
520 while (partEmitCnt--) {
521 RAY photonRay;
522
523 /* Emit photon according to PDF (if any), allocate
524 * associated primary ray, and trace through scene
525 * until absorbed/leaked; emitPhoton() sets the
526 * emitting light source index in photonRay */
527 emitPhoton(&emap, &photonRay);
528 newPhotonPrimary(pm, &photonRay, primaryHeap[proc]);
529 /* Set subprocess index in photonRay for post-
530 * distrib primary index linearisation; this is
531 * propagated with the primary index in photonRay
532 * and set for photon hits by newPhoton() */
533 PMAP_SETRAYPROC(&photonRay, proc);
534 tracePhoton(&photonRay);
535 }
536
537 /* Update shared global photon count */
538 photonCnt [PHOTONCNT_NUMPHOT] += pm -> numPhotons -
539 lastNumPhotons;
540 lastNumPhotons = pm -> numPhotons;
541 }
542
543 portCnt++;
544 } while (portCnt < numPhotonPorts);
545
546 if (pm -> numPhotons == srcNumDistrib)
547 /* Double predistrib factor in case no photons were stored
548 * for this source and redo pass 1 */
549 srcPreDistrib *= 2;
550 else {
551 /* Now do pass 2 */
552 passCnt++;
553 /* if (photonRepTime)
554 eputs("\n"); */
555 }
556 }
557 }
558
559 /* Flush heap buffa one final time to prevent data corruption */
560 flushPhotonHeap(pm);
561 fclose(pm -> heap);
562
563 /* Flush final photon primary to primary heap file */
564 newPhotonPrimary(pm, NULL, primaryHeap [proc]);
565 fclose(primaryHeap [proc]);
566
567 #ifdef DEBUG_PMAP
568 sprintf(errmsg, "Proc %d exited with total %ld photons\n", proc,
569 pm -> numPhotons);
570 eputs(errmsg);
571 #endif
572
573 exit(0);
574 }
575 else if (pid < 0)
576 error(SYSTEM, "failed to fork subprocess in distribPhotonContrib");
577 }
578
579 /* PARENT PROCESS CONTINUES HERE */
580 /* Record start time and enable progress report signal handler */
581 repStartTime = time(NULL);
582 #ifdef SIGCONT
583 signal(SIGCONT, pmapDistribReport);
584 #endif
585 /*
586 if (photonRepTime)
587 eputs("\n"); */
588
589 /* Wait for subprocesses to complete while reporting progress */
590 proc = numProc;
591 while (proc) {
592 while (waitpid(-1, &stat, WNOHANG) > 0) {
593 /* Subprocess exited; check status */
594 if (!WIFEXITED(stat) || WEXITSTATUS(stat))
595 error(USER, "failed photon distribution");
596
597 --proc;
598 }
599
600 /* Nod off for a bit and update progress */
601 sleep(1);
602
603 /* Update progress report from shared subprocess counters */
604 repComplete = pm -> distribTarget * numProc;
605 repProgress = photonCnt [PHOTONCNT_NUMPHOT];
606 for (repEmitted = 0, srcIdx = 0; srcIdx < nsources; srcIdx++)
607 repEmitted += photonCnt [PHOTONCNT_NUMEMIT(srcIdx)];
608
609 /* Get global photon count from shmem updated by subprocs */
610 pm -> numPhotons = photonCnt [PHOTONCNT_NUMPHOT];
611
612 if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
613 pmapDistribReport();
614 #ifdef SIGCONT
615 else signal(SIGCONT, pmapDistribReport);
616 #endif
617 }
618
619 /* ================================================================
620 * POST-DISTRIBUTION - Set photon flux and build kd-tree, etc.
621 * ================================================================ */
622 #ifdef SIGCONT
623 signal(SIGCONT, SIG_DFL);
624 #endif
625 free(emap.samples);
626
627 if (!pm -> numPhotons)
628 error(USER, "empty photon map");
629
630 /* Load per-subprocess primary rays into pm -> primary array */
631 pm -> numPrimary = buildPrimaries(pm, primaryHeap, primaryOfs, numProc);
632 if (!pm -> numPrimary)
633 error(INTERNAL, "no primary rays in contribution photon map");
634
635 /* Set photon flux per source */
636 for (srcIdx = 0; srcIdx < nsources; srcIdx++)
637 srcFlux [srcIdx] /= photonCnt [PHOTONCNT_NUMEMIT(srcIdx)];
638
639 /* Photon counters no longer needed, unmap shared memory */
640 munmap(photonCnt, sizeof(*photonCnt));
641 close(shmFile);
642 #if 0
643 shm_unlink(shmFname);
644 #else
645 unlink(shmFname);
646 #endif
647
648 if (photonRepTime) {
649 eputs("\nBuilding contrib photon map...\n");
650 fflush(stderr);
651 }
652
653 /* Build underlying data structure; heap is destroyed */
654 buildPhotonMap(pm, srcFlux, primaryOfs, numProc);
655 }
656
657
658
659 void photonContrib (PhotonMap *pmap, RAY *ray, COLOR irrad)
660 /* Sum up light source contributions from photons in pmap->srcContrib */
661 {
662 unsigned i;
663 PhotonSearchQueueNode *sqn;
664 float r, invArea;
665 RREAL rayCoeff [3];
666 Photon *photon;
667 static char warn = 1;
668
669 setcolor(irrad, 0, 0, 0);
670
671 if (!pmap -> maxGather)
672 return;
673
674 /* Ignore sources */
675 if (ray -> ro && islight(objptr(ray -> ro -> omod) -> otype))
676 return;
677
678 /* Get cumulative path coefficient up to photon lookup point */
679 raycontrib(rayCoeff, ray, PRIMARY);
680
681 /* Lookup photons */
682 pmap -> squeue.tail = 0;
683 findPhotons(pmap, ray);
684
685 /* Need at least 2 photons */
686 if (pmap -> squeue.tail < 2) {
687 #ifdef PMAP_NONEFOUND
688 sprintf(errmsg, "no photons found on %s at (%.3f, %.3f, %.3f)",
689 ray -> ro ? ray -> ro -> oname : "<null>",
690 ray -> rop [0], ray -> rop [1], ray -> rop [2]);
691 error(WARNING, errmsg);
692 #endif
693
694 return;
695 }
696
697 /* Average (squared) radius between furthest two photons to improve
698 * accuracy and get inverse search area 1 / (PI * r^2), with extra
699 * normalisation factor 1 / PI for ambient calculation */
700 sqn = pmap -> squeue.node + 1;
701 r = max(sqn -> dist2, (sqn + 1) -> dist2);
702 r = 0.25 * (pmap -> maxDist2 + r + 2 * sqrt(pmap -> maxDist2 * r));
703 invArea = 1 / (PI * PI * r);
704
705 /* Skip the extra photon */
706 for (i = 1 ; i < pmap -> squeue.tail; i++, sqn++) {
707 COLOR flux;
708
709 /* Get photon's contribution to density estimate */
710 photon = getNearestPhoton(&pmap -> squeue, sqn -> idx);
711 getPhotonFlux(photon, flux);
712 scalecolor(flux, invArea);
713 #ifdef PMAP_EPANECHNIKOV
714 /* Apply Epanechnikov kernel to photon flux based on photon distance */
715 scalecolor(flux, 2 * (1 - sqn -> dist2 / r));
716 #endif
717 addcolor(irrad, flux);
718
719 if (pmap -> srcContrib) {
720 const PhotonPrimary *primary = pmap -> primaries +
721 photon -> primary;
722 const SRCREC *sp = &source [primary -> srcIdx];
723 OBJREC *srcMod = findmaterial(sp -> so);
724 MODCONT *srcContrib = (MODCONT*)lu_find(pmap -> srcContrib,
725 srcMod -> oname) -> data;
726 double srcBinReal;
727 int srcBin;
728 RAY srcRay;
729
730 if (!srcContrib)
731 continue;
732
733 /* Photon's emitting light source has modifier whose contributions
734 * are sought */
735 if (srcContrib -> binv -> type != NUM) {
736 /* Use intersection function to set shadow ray parameters if
737 * it's not simply a constant */
738 rayorigin(&srcRay, SHADOW, NULL, NULL);
739 srcRay.rsrc = primary -> srcIdx;
740 #ifdef PMAP_PRIMARYPOS
741 VCOPY(srcRay.rorg, primary -> pos);
742 #else
743 /* No primary hitpoints; set dummy ray origin and warn once */
744 srcRay.rorg [0] = srcRay.rorg [1] = srcRay.rorg [2] = 0;
745 if (warn) {
746 error(WARNING, "no photon primary hitpoints for bin evaluation;"
747 " using dummy (0,0,0) !");
748 warn = 0;
749 }
750 #endif
751 decodedir(srcRay.rdir, primary -> dir);
752
753 if (!(sp->sflags & SDISTANT
754 ? sourcehit(&srcRay)
755 : (*ofun[sp -> so -> otype].funp)(sp -> so, &srcRay)))
756 continue; /* XXX shouldn't happen! */
757
758 worldfunc(RCCONTEXT, &srcRay);
759 set_eparams((char *)srcContrib -> params);
760 }
761
762 if ((srcBinReal = evalue(srcContrib -> binv)) < -.5)
763 continue; /* silently ignore negative bins */
764
765 if ((srcBin = srcBinReal + .5) >= srcContrib -> nbins) {
766 error(WARNING, "bad bin number (ignored)");
767 continue;
768 }
769
770 if (!contrib) {
771 /* Ray coefficient mode; normalise by light source radiance
772 * after applying distrib pattern */
773 int j;
774
775 raytexture(ray, srcMod -> omod);
776 setcolor(ray -> rcol, srcMod -> oargs.farg [0],
777 srcMod -> oargs.farg [1], srcMod -> oargs.farg [2]);
778 multcolor(ray -> rcol, ray -> pcol);
779 for (j = 0; j < 3; j++)
780 flux [j] = ray -> rcol [j] ? flux [j] / ray -> rcol [j] : 0;
781 }
782
783 multcolor(flux, rayCoeff);
784 addcolor(srcContrib -> cbin [srcBin], flux);
785 }
786 }
787
788 return;
789 }