ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmap.c
Revision: 2.9
Committed: Tue Aug 18 18:45:55 2015 UTC (8 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.8: +5 -2 lines
Log Message:
Added missing RCSid forgotten during initial check-in

File Contents

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