ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmap.c
Revision: 2.1
Committed: Tue Feb 24 19:39:26 2015 UTC (10 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Initial check-in of photon map addition by Roland Schregle

File Contents

# Content
1 /*
2 ==================================================================
3 Photon map main module
4
5 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
6 (c) Fraunhofer Institute for Solar Energy Systems,
7 Lucerne University of Applied Sciences & Arts
8 ==================================================================
9
10 $Id: pmap.c,v 4.31 2015/02/12 09:56:41 taschreg Exp taschreg $
11 */
12
13
14
15 #include "pmap.h"
16 #include "pmapmat.h"
17 #include "pmapsrc.h"
18 #include "pmaprand.h"
19 #include "pmapio.h"
20 #include "pmapbias.h"
21 #include "pmapdiag.h"
22 #include "otypes.h"
23 #include <signal.h>
24 #include <time.h>
25 #include <sys/stat.h>
26
27
28
29 extern char *octname;
30
31 static char PmapRevision [] = "$Revision: 4.31 $";
32
33
34
35 /* Photon map lookup functions per type */
36 void (*pmapLookup [NUM_PMAP_TYPES])(PhotonMap*, RAY*, COLOR) = {
37 photonDensity, photonPreCompDensity, photonDensity, volumePhotonDensity,
38 photonDensity, NULL
39 };
40
41
42
43 void colorNorm (COLOR c)
44 /* Normalise colour channels to average of 1 */
45 {
46 const float avg = colorAvg(c);
47
48 if (!avg)
49 return;
50
51 c [0] /= avg;
52 c [1] /= avg;
53 c [2] /= avg;
54 }
55
56
57
58 void loadPmaps (PhotonMap **pmaps, const PhotonMapParams *parm)
59 {
60 unsigned t;
61 struct stat octstat, pmstat;
62 PhotonMap *pm;
63 PhotonMapType type;
64
65 for (t = 0; t < NUM_PMAP_TYPES; t++)
66 if (setPmapParam(&pm, parm + t)) {
67 /* Check if photon map newer than octree */
68 if (!stat(pm -> fileName, &pmstat) && !stat(octname, &octstat) &&
69 octstat.st_mtime > pmstat.st_mtime) {
70 sprintf(errmsg, "photon map in file %s may be stale",
71 pm -> fileName);
72 error(USER, errmsg);
73 }
74
75 /* Load photon map from file and get its type */
76 if ((type = loadPhotonMap(pm, pm -> fileName)) == PMAP_TYPE_NONE)
77 error(USER, "failed loading photon map");
78
79 /* Assign to appropriate photon map type (deleting previously
80 * loaded photon map of same type if necessary) */
81 if (pmaps [type]) {
82 deletePhotons(pmaps [type]);
83 free(pmaps [type]);
84 }
85 pmaps [type] = pm;
86
87 /* Check for invalid density estimate bandwidth */
88 if (pm -> maxGather > pm -> heapSize) {
89 error(WARNING, "adjusting density estimate bandwidth");
90 pm -> minGather = pm -> maxGather = pm -> heapSize;
91 }
92 }
93 }
94
95
96
97 void savePmaps (const PhotonMap **pmaps, int argc, char **argv)
98 {
99 unsigned t;
100
101 for (t = 0; t < NUM_PMAP_TYPES; t++) {
102 if (pmaps [t])
103 savePhotonMap(pmaps [t], pmaps [t] -> fileName, t, argc, argv);
104 }
105 }
106
107
108
109 void cleanUpPmaps (PhotonMap **pmaps)
110 {
111 unsigned t;
112
113 for (t = 0; t < NUM_PMAP_TYPES; t++) {
114 if (pmaps [t]) {
115 deletePhotons(pmaps [t]);
116 free(pmaps [t]);
117 }
118 }
119 }
120
121
122
123 static int photonParticipate (RAY *ray)
124 /* Trace photon through participating medium. Returns 1 if passed through,
125 or 0 if absorbed and $*%&ed. Analogon to rayparticipate(). */
126 {
127 int i;
128 RREAL cosTheta, cosPhi, du, dv;
129 const float cext = colorAvg(ray -> cext),
130 albedo = colorAvg(ray -> albedo);
131 FVECT u, v;
132 COLOR cvext;
133
134 /* Mean free distance until interaction with medium */
135 ray -> rmax = -log(pmapRandom(mediumState)) / cext;
136
137 while (!localhit(ray, &thescene)) {
138 setcolor(cvext, exp(-ray -> rmax * ray -> cext [0]),
139 exp(-ray -> rmax * ray -> cext [1]),
140 exp(-ray -> rmax * ray -> cext [2]));
141
142 /* Modify ray color and normalise */
143 multcolor(ray -> rcol, cvext);
144 colorNorm(ray -> rcol);
145 VCOPY(ray -> rorg, ray -> rop);
146
147 if (albedo > FTINY)
148 /* Add to volume photon map */
149 if (ray -> rlvl > 0) addPhoton(volumePmap, ray);
150
151 /* Absorbed? */
152 if (pmapRandom(rouletteState) > albedo) return 0;
153
154 /* Colour bleeding without attenuation (?) */
155 multcolor(ray -> rcol, ray -> albedo);
156 scalecolor(ray -> rcol, 1 / albedo);
157
158 /* Scatter photon */
159 cosTheta = ray -> gecc <= FTINY ? 2 * pmapRandom(scatterState) - 1
160 : 1 / (2 * ray -> gecc) *
161 (1 + ray -> gecc * ray -> gecc -
162 (1 - ray -> gecc * ray -> gecc) /
163 (1 - ray -> gecc + 2 * ray -> gecc *
164 pmapRandom(scatterState)));
165
166 cosPhi = cos(2 * PI * pmapRandom(scatterState));
167 du = dv = sqrt(1 - cosTheta * cosTheta); /* sin(theta) */
168 du *= cosPhi;
169 dv *= sqrt(1 - cosPhi * cosPhi); /* sin(phi) */
170
171 /* Get axes u & v perpendicular to photon direction */
172 i = 0;
173 do {
174 v [0] = v [1] = v [2] = 0;
175 v [i++] = 1;
176 fcross(u, v, ray -> rdir);
177 } while (normalize(u) < FTINY);
178 fcross(v, ray -> rdir, u);
179
180 for (i = 0; i < 3; i++)
181 ray -> rdir [i] = du * u [i] + dv * v [i] +
182 cosTheta * ray -> rdir [i];
183 ray -> rlvl++;
184 ray -> rmax = -log(pmapRandom(mediumState)) / cext;
185 }
186
187 setcolor(cvext, exp(-ray -> rot * ray -> cext [0]),
188 exp(-ray -> rot * ray -> cext [1]),
189 exp(-ray -> rot * ray -> cext [2]));
190
191 /* Modify ray color and normalise */
192 multcolor(ray -> rcol, cvext);
193 colorNorm(ray -> rcol);
194
195 /* Passed through medium */
196 return 1;
197 }
198
199
200
201 void tracePhoton (RAY *ray)
202 /* Follow photon as it bounces around... */
203 {
204 long mod;
205 OBJREC* mat;
206
207 if (ray -> rlvl > photonMaxBounce) {
208 error(WARNING, "runaway photon!");
209 return;
210 }
211
212 if (colorAvg(ray -> cext) > FTINY && !photonParticipate(ray))
213 return;
214
215 if (localhit(ray, &thescene)) {
216 mod = ray -> ro -> omod;
217
218 if ((ray -> clipset && inset(ray -> clipset, mod)) || mod == OVOID) {
219 /* Transfer ray if modifier is VOID or clipped within antimatta */
220 RAY tray;
221 photonRay(ray, &tray, PMAP_XFER, NULL);
222 tracePhoton(&tray);
223 }
224 else {
225 /* Scatter for modifier material */
226 mat = objptr(mod);
227 photonScatter [mat -> otype] (mat, ray);
228 }
229 }
230 }
231
232
233
234 static void preComputeGlobal (PhotonMap *pmap)
235 /* Precompute irradiance from global photons for final gathering using
236 the first finalGather * pmap -> heapSize photons in the heap. Returns
237 new heap with precomputed photons. */
238 {
239 unsigned long i, nuHeapSize;
240 unsigned j;
241 Photon *nuHeap, *p;
242 COLOR irrad;
243 RAY ray;
244 float nuMinPos [3], nuMaxPos [3];
245
246 repComplete = nuHeapSize = finalGather * pmap -> heapSize;
247
248 if (photonRepTime) {
249 sprintf(errmsg,
250 "Precomputing irradiance for %ld global photons...\n",
251 nuHeapSize);
252 eputs(errmsg);
253 fflush(stderr);
254 }
255
256 p = nuHeap = (Photon*)malloc(nuHeapSize * sizeof(Photon));
257 if (!nuHeap)
258 error(USER, "can't allocate photon heap");
259
260 for (j = 0; j <= 2; j++) {
261 nuMinPos [j] = FHUGE;
262 nuMaxPos [j] = -FHUGE;
263 }
264
265 /* Record start time, baby */
266 repStartTime = time(NULL);
267 signal(SIGCONT, pmapPreCompReport);
268 repProgress = 0;
269 bcopy(pmap -> heap, nuHeap, nuHeapSize * sizeof(Photon));
270
271 for (i = 0, p = nuHeap; i < nuHeapSize; i++, p++) {
272 ray.ro = NULL;
273 VCOPY(ray.rop, p -> pos);
274
275 /* Update min and max positions & set ray normal */
276 for (j = 0; j < 3; j++) {
277 if (p -> pos [j] < nuMinPos [j]) nuMinPos [j] = p -> pos [j];
278 if (p -> pos [j] > nuMaxPos [j]) nuMaxPos [j] = p -> pos [j];
279 ray.ron [j] = p -> norm [j] / 127.0;
280 }
281
282 photonDensity(pmap, &ray, irrad);
283 setPhotonFlux(p, irrad);
284 repProgress++;
285
286 if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
287 pmapPreCompReport();
288 #ifndef BSD
289 else signal(SIGCONT, pmapPreCompReport);
290 #endif
291 }
292
293 signal(SIGCONT, SIG_DFL);
294
295 /* Replace & rebuild heap */
296 free(pmap -> heap);
297 pmap -> heap = nuHeap;
298 pmap -> heapSize = pmap -> heapEnd = nuHeapSize;
299 VCOPY(pmap -> minPos, nuMinPos);
300 VCOPY(pmap -> maxPos, nuMaxPos);
301
302 if (photonRepTime) {
303 eputs("Rebuilding global photon heap...\n");
304 fflush(stderr);
305 }
306
307 balancePhotons(pmap, NULL);
308 }
309
310
311
312 void distribPhotons (PhotonMap **pmaps)
313 {
314 EmissionMap emap;
315 char errmsg2 [128];
316 unsigned t, srcIdx, passCnt = 0, prePassCnt = 0;
317 double totalFlux = 0;
318 PhotonMap *pm;
319
320 for (t = 0; t < NUM_PMAP_TYPES && !photonMaps [t]; t++);
321 if (t >= NUM_PMAP_TYPES)
322 error(USER, "no photon maps defined");
323
324 if (!nsources)
325 error(USER, "no light sources");
326
327 /* ===================================================================
328 * INITIALISATION - Set up emission and scattering funcs
329 * =================================================================== */
330 emap.samples = NULL;
331 emap.maxPartitions = MAXSPART;
332 emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
333 if (!emap.partitions)
334 error(INTERNAL, "can't allocate source partitions");
335
336 /* Initialise all defined photon maps */
337 for (t = 0; t < NUM_PMAP_TYPES; t++)
338 initPhotonMap(photonMaps [t], t);
339
340 initPhotonEmissionFuncs();
341 initPhotonScatterFuncs();
342
343 /* Get photon ports if specified */
344 if (ambincl == 1)
345 getPhotonPorts();
346
347 /* Get photon sensor modifiers */
348 getPhotonSensors(photonSensorList);
349
350 /* Seed RNGs for photon distribution */
351 pmapSeed(randSeed, partState);
352 pmapSeed(randSeed, emitState);
353 pmapSeed(randSeed, cntState);
354 pmapSeed(randSeed, mediumState);
355 pmapSeed(randSeed, scatterState);
356 pmapSeed(randSeed, rouletteState);
357
358 if (photonRepTime)
359 eputs("\n");
360
361 /* ===================================================================
362 * FLUX INTEGRATION - Get total photon flux from light sources
363 * =================================================================== */
364 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
365 unsigned portCnt = 0;
366 emap.src = source + srcIdx;
367
368 do {
369 emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
370 : NULL;
371 photonPartition [emap.src -> so -> otype] (&emap);
372
373 if (photonRepTime) {
374 sprintf(errmsg, "Integrating flux from source %s ",
375 source [srcIdx].so -> oname);
376
377 if (emap.port) {
378 sprintf(errmsg2, "via port %s ",
379 photonPorts [portCnt].so -> oname);
380 strcat(errmsg, errmsg2);
381 }
382
383 sprintf(errmsg2, "(%lu partitions)...\n", emap.numPartitions);
384 strcat(errmsg, errmsg2);
385 eputs(errmsg);
386 fflush(stderr);
387 }
388
389 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
390 emap.partitionCnt++) {
391 initPhotonEmission(&emap, pdfSamples);
392 totalFlux += colorAvg(emap.partFlux);
393 }
394
395 portCnt++;
396 } while (portCnt < numPhotonPorts);
397 }
398
399 if (totalFlux < FTINY)
400 error(USER, "zero flux from light sources");
401
402 /* Record start time and enable progress report signal handler */
403 repStartTime = time(NULL);
404 signal(SIGCONT, pmapDistribReport);
405 repProgress = prePassCnt = 0;
406
407 if (photonRepTime)
408 eputs("\n");
409
410 /* ===================================================================
411 * 2-PASS PHOTON DISTRIBUTION
412 * Pass 1 (pre): emit fraction of target photon count
413 * Pass 2 (main): based on outcome of pass 1, estimate remaining number
414 * of photons to emit to approximate target count
415 * =================================================================== */
416 do {
417 double numEmit;
418
419 if (!passCnt) {
420 /* INIT PASS 1 */
421 /* Skip if no photons contributed after sufficient iterations; make
422 * it clear to user which photon maps are missing so (s)he can
423 * check the scene geometry and materials */
424 if (++prePassCnt > maxPreDistrib) {
425 sprintf(errmsg, "too many prepasses");
426
427 for (t = 0; t < NUM_PMAP_TYPES; t++)
428 if (photonMaps [t] && !photonMaps [t] -> heapEnd) {
429 sprintf(errmsg2, ", no %s photons stored", pmapName [t]);
430 strcat(errmsg, errmsg2);
431 }
432
433 error(USER, errmsg);
434 break;
435 }
436
437 /* Num to emit is fraction of minimum target count */
438 numEmit = FHUGE;
439
440 for (t = 0; t < NUM_PMAP_TYPES; t++)
441 if (photonMaps [t])
442 numEmit = min(photonMaps [t] -> distribTarget, numEmit);
443
444 numEmit *= preDistrib;
445 }
446
447 else {
448 /* INIT PASS 2 */
449 /* Based on the outcome of the predistribution we can now estimate
450 * how many more photons we have to emit for each photon map to
451 * meet its respective target count. This value is clamped to 0 in
452 * case the target has already been exceeded in the pass 1. Note
453 * repProgress is the number of photons emitted thus far, while
454 * heapEnd is the number of photons stored in each photon map. */
455 double maxDistribRatio = 0;
456
457 /* Set the distribution ratio for each map; this indicates how many
458 * photons of each respective type are stored per emitted photon,
459 * and is used as probability for storing a photon by addPhoton().
460 * Since this biases the photon density, addPhoton() promotes the
461 * flux of stored photons to compensate. */
462 for (t = 0; t < NUM_PMAP_TYPES; t++)
463 if ((pm = photonMaps [t])) {
464 pm -> distribRatio = (double)pm -> distribTarget /
465 pm -> heapEnd - 1;
466
467 /* Check if photon map "overflowed", i.e. exceeded its target
468 * count in the prepass; correcting the photon flux via the
469 * distribution ratio is no longer possible, as no more
470 * photons of this type will be stored, so notify the user
471 * rather than deliver incorrect results.
472 * In future we should handle this more intelligently by
473 * using the photonFlux in each photon map to individually
474 * correct the flux after distribution. */
475 if (pm -> distribRatio <= FTINY) {
476 sprintf(errmsg,
477 "%s photon map overflow in prepass, reduce -apD",
478 pmapName [t]);
479 error(INTERNAL, errmsg);
480 }
481
482 maxDistribRatio = max(pm -> distribRatio, maxDistribRatio);
483 }
484
485 /* Normalise distribution ratios and calculate number of photons to
486 * emit in main pass */
487 for (t = 0; t < NUM_PMAP_TYPES; t++)
488 if ((pm = photonMaps [t]))
489 pm -> distribRatio /= maxDistribRatio;
490
491 if ((numEmit = repProgress * maxDistribRatio) < FTINY)
492 /* No photons left to distribute in main pass */
493 break;
494 }
495
496 /* Set completion count for progress report */
497 repComplete = numEmit + repProgress;
498
499 /* PHOTON DISTRIBUTION LOOP */
500 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
501 unsigned portCnt = 0;
502 emap.src = source + srcIdx;
503
504 do {
505 emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
506 : NULL;
507 photonPartition [emap.src -> so -> otype] (&emap);
508
509 if (photonRepTime) {
510 if (!passCnt)
511 sprintf(errmsg, "PREPASS %d on source %s ",
512 prePassCnt, source [srcIdx].so -> oname);
513 else
514 sprintf(errmsg, "MAIN PASS on source %s ",
515 source [srcIdx].so -> oname);
516
517 if (emap.port) {
518 sprintf(errmsg2, "via port %s ",
519 photonPorts [portCnt].so -> oname);
520 strcat(errmsg, errmsg2);
521 }
522
523 sprintf(errmsg2, "(%lu partitions)...\n", emap.numPartitions);
524 strcat(errmsg, errmsg2);
525 eputs(errmsg);
526 fflush(stderr);
527 }
528
529 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
530 emap.partitionCnt++) {
531 double partNumEmit;
532 unsigned long partEmitCnt;
533
534 /* Get photon origin within current source partishunn and
535 * build emission map */
536 photonOrigin [emap.src -> so -> otype] (&emap);
537 initPhotonEmission(&emap, pdfSamples);
538
539 /* Number of photons to emit from ziss partishunn --
540 * proportional to flux; photon ray weight and scalar flux
541 * are uniform (the latter only varying in RGB). */
542 partNumEmit = numEmit * colorAvg(emap.partFlux) / totalFlux;
543 partEmitCnt = (unsigned long)partNumEmit;
544
545 /* Probabilistically account for fractional photons */
546 if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
547 partEmitCnt++;
548
549 /* Integer counter avoids FP rounding errors */
550 while (partEmitCnt--) {
551 RAY photonRay;
552
553 /* Emit photon based on PDF and trace through scene until
554 * absorbed/leaked */
555 emitPhoton(&emap, &photonRay);
556 tracePhoton(&photonRay);
557
558 /* Record progress */
559 repProgress++;
560
561 if (photonRepTime > 0 &&
562 time(NULL) >= repLastTime + photonRepTime)
563 pmapDistribReport();
564 #ifndef BSD
565 else signal(SIGCONT, pmapDistribReport);
566 #endif
567 }
568 }
569
570 portCnt++;
571 } while (portCnt < numPhotonPorts);
572 }
573
574 for (t = 0; t < NUM_PMAP_TYPES; t++)
575 if (photonMaps [t] && !photonMaps [t] -> heapEnd) {
576 /* Double preDistrib in case a photon map is empty and redo
577 * pass 1 --> possibility of infinite loop for pathological
578 * scenes (e.g. absorbing materials) */
579 preDistrib *= 2;
580 break;
581 }
582
583 if (t >= NUM_PMAP_TYPES) {
584 /* No empty photon maps found; now do pass 2 */
585 passCnt++;
586 if (photonRepTime)
587 eputs("\n");
588 }
589 } while (passCnt < 2);
590
591 /* ===================================================================
592 * POST-DISTRIBUTION - Set photon flux and build kd-tree, etc.
593 * =================================================================== */
594 signal(SIGCONT, SIG_DFL);
595 free(emap.samples);
596
597 /* Set photon flux (repProgress is total num emitted) */
598 totalFlux /= repProgress;
599
600 for (t = 0; t < NUM_PMAP_TYPES; t++)
601 if (photonMaps [t]) {
602 if (photonRepTime) {
603 sprintf(errmsg, "\nBuilding %s photon map...\n", pmapName [t]);
604 eputs(errmsg);
605 fflush(stderr);
606 }
607
608 balancePhotons(photonMaps [t], &totalFlux);
609 }
610
611 /* Precompute photon irradiance if necessary */
612 if (preCompPmap)
613 preComputeGlobal(preCompPmap);
614 }
615
616
617
618 void photonDensity (PhotonMap *pmap, RAY *ray, COLOR irrad)
619 /* Photon density estimate. Returns irradiance at ray -> rop. */
620 {
621 unsigned i;
622 PhotonSQNode *sq;
623 float r;
624 COLOR flux;
625
626 setcolor(irrad, 0, 0, 0);
627
628 if (!pmap -> maxGather)
629 return;
630
631 /* Ignore sources */
632 if (ray -> ro)
633 if (islight(objptr(ray -> ro -> omod) -> otype))
634 return;
635
636 pmap -> squeueEnd = 0;
637 findPhotons(pmap, ray);
638
639 /* Need at least 2 photons */
640 if (pmap -> squeueEnd < 2) {
641 #ifdef PMAP_NONEFOUND
642 sprintf(errmsg, "no photons found on %s at (%.3f, %.3f, %.3f)",
643 ray -> ro ? ray -> ro -> oname : "<null>",
644 ray -> rop [0], ray -> rop [1], ray -> rop [2]);
645 error(WARNING, errmsg);
646 #endif
647
648 return;
649 }
650
651 if (pmap -> minGather == pmap -> maxGather) {
652 /* No bias compensation. Just do a plain vanilla estimate */
653 sq = pmap -> squeue + 1;
654
655 /* Average radius between furthest two photons to improve accuracy */
656 r = max(sq -> dist, (sq + 1) -> dist);
657 r = 0.25 * (pmap -> maxDist + r + 2 * sqrt(pmap -> maxDist * r));
658
659 /* Skip the extra photon */
660 for (i = 1 ; i < pmap -> squeueEnd; i++, sq++) {
661 getPhotonFlux(sq -> photon, flux);
662 #ifdef PMAP_EPANECHNIKOV
663 /* Apply Epanechnikov kernel to photon flux (dists are squared) */
664 scalecolor(flux, 2 * (1 - sq -> dist / r));
665 #endif
666 addcolor(irrad, flux);
667 }
668
669 /* Divide by search area PI * r^2, 1 / PI required as ambient
670 normalisation factor */
671 scalecolor(irrad, 1 / (PI * PI * r));
672
673 return;
674 }
675 else
676 /* Apply bias compensation to density estimate */
677 biasComp(pmap, irrad);
678 }
679
680
681
682 void photonPreCompDensity (PhotonMap *pmap, RAY *r, COLOR irrad)
683 /* Returns precomputed photon density estimate at ray -> rop. */
684 {
685 Photon *p;
686
687 setcolor(irrad, 0, 0, 0);
688
689 /* Ignore sources */
690 if (r -> ro && islight(objptr(r -> ro -> omod) -> otype))
691 return;
692
693 if ((p = find1Photon(preCompPmap, r)))
694 getPhotonFlux(p, irrad);
695 }
696
697
698
699 void volumePhotonDensity (PhotonMap *pmap, RAY *ray, COLOR irrad)
700 /* Photon volume density estimate. Returns irradiance at ray -> rop. */
701 {
702 unsigned i;
703 PhotonSQNode *sq;
704 float gecc2, r, ph;
705 COLOR flux;
706
707 setcolor(irrad, 0, 0, 0);
708
709 if (!pmap -> maxGather)
710 return;
711
712 pmap -> squeueEnd = 0;
713 findPhotons(pmap, ray);
714
715 /* Need at least 2 photons */
716 if (pmap -> squeueEnd < 2)
717 return;
718
719 if (pmap -> minGather == pmap -> maxGather) {
720 /* No bias compensation. Just do a plain vanilla estimate */
721 gecc2 = ray -> gecc * ray -> gecc;
722 sq = pmap -> squeue + 1;
723
724 /* Average radius between furthest two photons to improve accuracy */
725 r = max(sq -> dist, (sq + 1) -> dist);
726 r = 0.25 * (pmap -> maxDist + r + 2 * sqrt(pmap -> maxDist * r));
727
728 /* Skip the extra photon */
729 for (i = 1 ; i < pmap -> squeueEnd; i++, sq++) {
730 /* Compute phase function for inscattering from photon */
731 if (gecc2 <= FTINY)
732 ph = 1;
733 else {
734 ph = DOT(ray -> rdir, sq -> photon -> norm) / 127;
735 ph = 1 + gecc2 - 2 * ray -> gecc * ph;
736 ph = (1 - gecc2) / (ph * sqrt(ph));
737 }
738
739 getPhotonFlux(sq -> photon, flux);
740 scalecolor(flux, ph);
741 addcolor(irrad, flux);
742 }
743
744 /* Divide by search volume 4 / 3 * PI * r^3 and phase function
745 normalization factor 1 / (4 * PI) */
746 scalecolor(irrad, 3 / (16 * PI * PI * r * sqrt(r)));
747
748 return;
749 }
750
751 else
752 /* Apply bias compensation to density estimate */
753 volumeBiasComp(pmap, ray, irrad);
754 }