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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id$";
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 $Id: pmap.c,v 2.8 2015/05/26 12:07:31 rschregle Exp $
15 */
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 static char PmapRevision [] = "$Revision: 2.8 $";
35
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 if (pm -> fileName && octname &&
72 !stat(pm -> fileName, &pmstat) && !stat(octname, &octstat) &&
73 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 savePhotonMap(pmaps [t], pmaps [t] -> fileName, argc, argv);
108 }
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 #ifdef PMAP_RUNAWAY_WARN
213 error(WARNING, "runaway photon!");
214 #endif
215 return;
216 }
217
218 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 #ifdef SIGCONT
274 signal(SIGCONT, pmapPreCompReport);
275 #endif
276 repProgress = 0;
277 memcpy(nuHeap, pmap -> heap, nuHeapSize * sizeof(Photon));
278
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 #ifdef SIGCONT
297 else signal(SIGCONT, pmapPreCompReport);
298 #endif
299 }
300
301 #ifdef SIGCONT
302 signal(SIGCONT, SIG_DFL);
303 #endif
304
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 for (t = 0; t < NUM_PMAP_TYPES && !pmaps [t]; t++);
331 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 initPhotonMap(pmaps [t], t);
349
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 #ifdef SIGCONT
415 signal(SIGCONT, pmapDistribReport);
416 #endif
417 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 if (pmaps [t] && !pmaps [t] -> heapEnd) {
441 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 if (pmaps [t])
454 numEmit = min(pmaps [t] -> distribTarget, numEmit);
455
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 if ((pm = pmaps [t])) {
476 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 if ((pm = pmaps [t]))
501 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
516 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 #ifdef SIGCONT
577 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 if (pmaps [t] && !pmaps [t] -> heapEnd) {
588 /* 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 #ifdef SIGCONT
607 signal(SIGCONT, SIG_DFL);
608 #endif
609 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 if (pmaps [t]) {
616 if (photonRepTime) {
617 sprintf(errmsg, "\nBuilding %s photon map...\n", pmapName [t]);
618 eputs(errmsg);
619 fflush(stderr);
620 }
621
622 balancePhotons(pmaps [t], &totalFlux);
623 }
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 }