ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmap.c
Revision: 2.10
Committed: Tue Sep 1 16:27:52 2015 UTC (8 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R0
Changes since 2.9: +2 -3 lines
Log Message:
Removed redundant $Id: in file

File Contents

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