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, 8 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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pmap.c,v 2.9 2015/08/18 18:45:55 greg Exp $";
3 #endif
4 /*
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 (c) Lucerne University of Applied Sciences and Arts,
11 supported by the Swiss National Science Foundation (SNSF, #147053)
12 ==================================================================
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 static char PmapRevision [] = "$Revision: 2.9 $";
34
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 if (pm -> fileName && octname &&
71 !stat(pm -> fileName, &pmstat) && !stat(octname, &octstat) &&
72 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 savePhotonMap(pmaps [t], pmaps [t] -> fileName, argc, argv);
107 }
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 #ifdef PMAP_RUNAWAY_WARN
212 error(WARNING, "runaway photon!");
213 #endif
214 return;
215 }
216
217 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 #ifdef SIGCONT
273 signal(SIGCONT, pmapPreCompReport);
274 #endif
275 repProgress = 0;
276 memcpy(nuHeap, pmap -> heap, nuHeapSize * sizeof(Photon));
277
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 #ifdef SIGCONT
296 else signal(SIGCONT, pmapPreCompReport);
297 #endif
298 }
299
300 #ifdef SIGCONT
301 signal(SIGCONT, SIG_DFL);
302 #endif
303
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 for (t = 0; t < NUM_PMAP_TYPES && !pmaps [t]; t++);
330 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 initPhotonMap(pmaps [t], t);
348
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 #ifdef SIGCONT
414 signal(SIGCONT, pmapDistribReport);
415 #endif
416 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 if (pmaps [t] && !pmaps [t] -> heapEnd) {
440 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 if (pmaps [t])
453 numEmit = min(pmaps [t] -> distribTarget, numEmit);
454
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 if ((pm = pmaps [t])) {
475 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 if ((pm = pmaps [t]))
500 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
515 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 #ifdef SIGCONT
576 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 if (pmaps [t] && !pmaps [t] -> heapEnd) {
587 /* 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 #ifdef SIGCONT
606 signal(SIGCONT, SIG_DFL);
607 #endif
608 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 if (pmaps [t]) {
615 if (photonRepTime) {
616 sprintf(errmsg, "\nBuilding %s photon map...\n", pmapName [t]);
617 eputs(errmsg);
618 fflush(stderr);
619 }
620
621 balancePhotons(pmaps [t], &totalFlux);
622 }
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 }