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

# User Rev Content
1 greg 2.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     }