ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapbias.c
Revision: 2.1
Committed: Tue Feb 24 19:39:26 2015 UTC (9 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     Bias compensation for photon density estimates
4    
5     For background see:
6     R. Schregle, "Bias Compensation for Photon Maps",
7     Computer Graphics Forum, v22:n4, pp. 729-742, Dec. 2003.
8    
9     Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
10     (c) Fraunhofer Institute for Solar Energy Systems,
11     Lucerne University of Applied Sciences & Arts
12     ==================================================================
13    
14     $Id: pmapbias.c,v 4.5 2015/01/29 13:12:35 taschreg Exp taschreg $
15     */
16    
17    
18    
19     #include "pmapbias.h"
20     #include "pmap.h"
21     #include "pmaprand.h"
22    
23    
24    
25     void squeuePartition (PhotonSQNode* squeue, unsigned lo,
26     unsigned mid, unsigned hi)
27     /* REVERSE Partition squeue such that all photons in
28     squeue-hi+1..squeue-mid are farther than the median at squeue-mid+1,
29     and those in squeue-mid+2..squeue-lo+1 are closer than the median.
30     This means that squeue points to the END of the queue, and the (1-based)
31     indices are offsets relative to it. This convoluted scheme is adopted
32     since the queue is initially a maxheap, so reverse sorting is expected
33     to be faster. */
34     {
35     unsigned l, h, p;
36     PhotonSQNode *lp, *hp, *pp;
37     float pivot, dist;
38     Photon* photon;
39    
40     while (hi > lo) {
41     /* Grab pivot node in middle as an educated guess, since our
42     queue is sorta sorted. */
43     l = lo;
44     h = hi;
45     p = mid;
46     lp = squeue - lo + 1;
47     hp = squeue - hi + 1;
48     pp = squeue - p + 1;
49     pivot = pp -> dist;
50    
51     /* l & h converge, swapping elements out of order with respect to
52     pivot node. */
53     while (l < h) {
54     while (lp -> dist <= pivot && l <= h && l < hi)
55     ++l, --lp;
56     while (hp -> dist >= pivot && h >= l && h > lo)
57     --h, ++hp;
58    
59     if (l < h) {
60     /* Swap */
61     photon = lp -> photon;
62     dist = lp -> dist;
63     lp -> photon = hp -> photon;
64     lp -> dist = hp -> dist;
65     hp -> photon = photon;
66     hp -> dist = dist;
67     }
68     }
69    
70     /* Swap convergence and pivot node */
71     if (p > h) {
72     /* Need this otherwise shit happens!
73     Since lp -> dist > hp -> dist, we swap either l or p depending
74     on whether we're above or below p */
75     h = l;
76     hp = lp;
77     }
78    
79     photon = hp -> photon;
80     dist = hp -> dist;
81     hp -> photon = pp -> photon;
82     hp -> dist = pivot;
83     pp -> photon = photon;
84     pp -> dist = dist;
85     if (h >= mid)
86     hi = h - 1;
87     if (h <= mid)
88     lo = h + 1;
89     }
90    
91     /* Once lo & hi have converged, we have found the median! */
92     }
93    
94    
95    
96     void biasComp (PhotonMap* pmap, COLOR irrad)
97     /* Photon density estimate with bias compensation -- czech dis shit out! */
98     {
99     unsigned i, numLo, numHi, numMid;
100     PhotonSQNode *sq;
101     PhotonBCNode *hist;
102     float r, totalWeight = 0;
103     PhotonSQNode *squeueEnd;
104     PhotonBCNode *histEnd;
105     COLOR fluxLo, fluxMid, irradVar, irradAvg, p, d;
106    
107     if (!pmap -> biasCompHist) {
108     /* Allocate bias compensation history */
109     numHi = pmap -> maxGather - pmap -> minGather;
110     for (i = pmap -> minGather + 1; numHi > 1; numHi >>= 1, ++i);
111     pmap -> biasCompHist = (PhotonBCNode*)malloc(i * sizeof(PhotonBCNode));
112     if (!pmap -> biasCompHist)
113     error(USER, "can't allocate bias compensation history");
114     }
115    
116     numLo = min(pmap -> minGather, pmap -> squeueEnd - 1);
117     numHi = min(pmap -> maxGather, pmap -> squeueEnd - 1);
118     sq = squeueEnd = pmap -> squeue + pmap -> squeueEnd - 1;
119     histEnd = pmap -> biasCompHist;
120     setcolor(fluxLo, 0, 0, 0);
121    
122     /* Partition to get numLo closest photons starting from END of queue */
123     squeuePartition(squeueEnd, 1, numLo + 1, numHi);
124    
125     /* Get irradiance estimates (ignoring 1 / PI) using 1..numLo photons
126     and chuck in history. Queue is traversed BACKWARDS. */
127     for (i = 1; i <= numLo; i++, sq--) {
128     /* Make sure furthest two photons are consecutive wrt distance */
129     squeuePartition(squeueEnd, i, i + 1, numLo + 1);
130     getPhotonFlux(sq -> photon, irrad);
131     addcolor(fluxLo, irrad);
132     /* Average radius between furthest two photons to improve accuracy */
133     r = 0.25 * (sq -> dist + (sq - 1) -> dist +
134     2 * sqrt(sq -> dist * (sq - 1) -> dist));
135     /* Add irradiance and weight to history. Weights should increase
136     monotonically based on number of photons used for the estimate. */
137     histEnd -> irrad [0] = fluxLo [0] / r;
138     histEnd -> irrad [1] = fluxLo [1] / r;
139     histEnd -> irrad [2] = fluxLo [2] / r;
140     totalWeight += histEnd++ -> weight = BIASCOMP_WGT((float)i);
141     }
142    
143     /* Compute expected value (average) and variance of irradiance based on
144     history for numLo photons. */
145     setcolor(irradAvg, 0, 0, 0);
146     setcolor(irradVar, 0, 0, 0);
147    
148     for (hist = pmap -> biasCompHist; hist < histEnd; ++hist)
149     for (i = 0; i <= 2; ++i) {
150     irradAvg [i] += r = hist -> weight * hist -> irrad [i];
151     irradVar [i] += r * hist -> irrad [i];
152     }
153    
154     for (i = 0; i <= 2; ++i) {
155     r = irradAvg [i] /= totalWeight;
156     irradVar [i] = irradVar [i] / totalWeight - r * r;
157     }
158    
159     /* Do binary search within interval [numLo, numHi]. numLo is towards
160     the END of the queue. */
161     while (numHi - numLo > 1) {
162     numMid = (numLo + numHi) >> 1;
163     /* Split interval to get numMid closest photons starting from the
164     END of the queue */
165     squeuePartition(squeueEnd, numLo, numMid, numHi);
166     /* Make sure furthest two photons are consecutive wrt distance */
167     squeuePartition(squeueEnd, numMid, numMid + 1, numHi);
168     copycolor(fluxMid, fluxLo);
169     sq = squeueEnd - numLo;
170    
171     /* Get irradiance for numMid photons (ignoring 1 / PI) */
172     for (i = numLo; i < numMid; i++, sq--) {
173     getPhotonFlux(sq -> photon, irrad);
174     addcolor(fluxMid, irrad);
175     }
176    
177     /* Average radius between furthest two photons to improve accuracy */
178     r = 0.25 * (sq -> dist + (sq + 1) -> dist +
179     2 * sqrt(sq -> dist * (sq + 1) -> dist));
180    
181     /* Get deviation from current average, and probability that it's due
182     to noise from gaussian distribution based on current variance. Since
183     we are doing this for each colour channel we can also detect
184     chromatic bias. */
185     for (i = 0; i <= 2; ++i) {
186     d [i] = irradAvg [i] - (irrad [i] = fluxMid [i] / r);
187     p [i] = exp(-0.5 * d [i] * d [i] / irradVar [i]);
188     }
189    
190     if (pmapRandom(pmap -> randState) < colorAvg(p)) {
191     /* Deviation is probably noise, so add mid irradiance to history */
192     copycolor(histEnd -> irrad, irrad);
193     totalWeight += histEnd++ -> weight = BIASCOMP_WGT((float)numMid);
194     setcolor(irradAvg, 0, 0, 0);
195     setcolor(irradVar, 0, 0, 0);
196    
197     /* Update average and variance */
198     for (hist = pmap -> biasCompHist; hist < histEnd; ++hist)
199     for (i = 0; i <= 2; i++) {
200     r = hist -> irrad [i];
201     irradAvg [i] += hist -> weight * r;
202     irradVar [i] += hist -> weight * r * r;
203     }
204    
205     for (i = 0; i <= 2; i++) {
206     r = irradAvg [i] /= totalWeight;
207     irradVar [i] = irradVar [i] / totalWeight - r * r;
208     }
209    
210     /* Need more photons -- recurse on [numMid, numHi] */
211     numLo = numMid;
212     copycolor(fluxLo, fluxMid);
213     }
214     else
215     /* Deviation is probably bias -- need fewer photons,
216     so recurse on [numLo, numMid] */
217     numHi = numMid;
218     }
219    
220     --histEnd;
221     for (i = 0; i <= 2; i++) {
222     /* Estimated relative error */
223     d [i] = histEnd -> irrad [i] / irradAvg [i] - 1;
224    
225     #ifdef BIASCOMP_BWIDTH
226     /* Return bandwidth instead of irradiance */
227     irrad [i] = numHi / PI;
228     #else
229     /* 1 / PI required as ambient normalisation factor */
230     irrad [i] = histEnd -> irrad [i] / (PI * PI);
231     #endif
232     }
233    
234     /* Update statistix */
235     r = colorAvg(d);
236     if (r < pmap -> minError)
237     pmap -> minError = r;
238     if (r > pmap -> maxError)
239     pmap -> maxError = r;
240     pmap -> rmsError += r * r;
241    
242     if (numHi < pmap -> minGathered)
243     pmap -> minGathered = numHi;
244     if (numHi > pmap -> maxGathered)
245     pmap -> maxGathered = numHi;
246    
247     pmap -> totalGathered += numHi;
248     ++pmap -> numDensity;
249     }
250    
251    
252    
253     void volumeBiasComp (PhotonMap* pmap, const RAY* ray, COLOR irrad)
254     /* Photon volume density estimate with bias compensation -- czech dis
255     shit out! */
256     {
257     unsigned i, numLo, numHi, numMid = 0;
258     PhotonSQNode *sq;
259     PhotonBCNode *hist;
260     const float gecc2 = ray -> gecc * ray -> gecc;
261     float r, totalWeight = 0;
262     PhotonSQNode *squeueEnd;
263     PhotonBCNode *histEnd;
264     COLOR fluxLo, fluxMid, irradVar, irradAvg, p, d;
265    
266     if (!pmap -> biasCompHist) {
267     /* Allocate bias compensation history */
268     numHi = pmap -> maxGather - pmap -> minGather;
269     for (i = pmap -> minGather + 1; numHi > 1; numHi >>= 1, ++i);
270     pmap -> biasCompHist = (PhotonBCNode*)malloc(i * sizeof(PhotonBCNode));
271     if (!pmap -> biasCompHist)
272     error(USER, "can't allocate bias compensation history");
273     }
274    
275     numLo = min(pmap -> minGather, pmap -> squeueEnd - 1);
276     numHi = min(pmap -> maxGather, pmap -> squeueEnd - 1);
277     sq = squeueEnd = pmap -> squeue + pmap -> squeueEnd - 1;
278     histEnd = pmap -> biasCompHist;
279     setcolor(fluxLo, 0, 0, 0);
280     /* Partition to get numLo closest photons starting from END of queue */
281     squeuePartition(squeueEnd, 1, numLo, numHi);
282    
283     /* Get irradiance estimates (ignoring constants) using 1..numLo photons
284     and chuck in history. Queue is traversed BACKWARDS. */
285     for (i = 1; i <= numLo; i++, sq--) {
286     /* Make sure furthest two photons are consecutive wrt distance */
287     squeuePartition(squeueEnd, i, i + 1, numHi);
288    
289     /* Compute phase function for inscattering from photon */
290     if (gecc2 <= FTINY)
291     r = 1;
292     else {
293     r = DOT(ray -> rdir, sq -> photon -> norm) / 127;
294     r = 1 + gecc2 - 2 * ray -> gecc * r;
295     r = (1 - gecc2) / (r * sqrt(r));
296     }
297    
298     getPhotonFlux(sq -> photon, irrad);
299     scalecolor(irrad, r);
300     addcolor(fluxLo, irrad);
301     /* Average radius between furthest two photons to improve accuracy */
302     r = 0.25 * (sq -> dist + (sq - 1) -> dist +
303     2 * sqrt(sq -> dist * (sq - 1) -> dist));
304     r *= sqrt(r);
305     /* Add irradiance and weight to history. Weights should increase
306     monotonically based on number of photons used for the estimate. */
307     histEnd -> irrad [0] = fluxLo [0] / r;
308     histEnd -> irrad [1] = fluxLo [1] / r;
309     histEnd -> irrad [2] = fluxLo [2] / r;
310     totalWeight += histEnd++ -> weight = BIASCOMP_WGT((float)i);
311     }
312    
313     /* Compute expected value (average) and variance of irradiance based on
314     history for numLo photons. */
315     setcolor(irradAvg, 0, 0, 0);
316     setcolor(irradVar, 0, 0, 0);
317    
318     for (hist = pmap -> biasCompHist; hist < histEnd; ++hist)
319     for (i = 0; i <= 2; ++i) {
320     irradAvg [i] += r = hist -> weight * hist -> irrad [i];
321     irradVar [i] += r * hist -> irrad [i];
322     }
323    
324     for (i = 0; i <= 2; ++i) {
325     r = irradAvg [i] /= totalWeight;
326     irradVar [i] = irradVar [i] / totalWeight - r * r;
327     }
328    
329     /* Do binary search within interval [numLo, numHi]. numLo is towards
330     the END of the queue. */
331     while (numHi - numLo > 1) {
332     numMid = (numLo + numHi) >> 1;
333     /* Split interval to get numMid closest photons starting from the
334     END of the queue */
335     squeuePartition(squeueEnd, numLo, numMid, numHi);
336     /* Make sure furthest two photons are consecutive wrt distance */
337     squeuePartition(squeueEnd, numMid, numMid + 1, numHi);
338     copycolor(fluxMid, fluxLo);
339     sq = squeueEnd - numLo;
340    
341     /* Get irradiance for numMid photons (ignoring constants) */
342     for (i = numLo; i < numMid; i++, sq--) {
343     /* Compute phase function for inscattering from photon */
344     if (gecc2 <= FTINY)
345     r = 1;
346     else {
347     r = DOT(ray -> rdir, sq -> photon -> norm) / 127;
348     r = 1 + gecc2 - 2 * ray -> gecc * r;
349     r = (1 - gecc2) / (r * sqrt(r));
350     }
351    
352     getPhotonFlux(sq -> photon, irrad);
353     scalecolor(irrad, r);
354     addcolor(fluxMid, irrad);
355     }
356    
357     /* Average radius between furthest two photons to improve accuracy */
358     r = 0.25 * (sq -> dist + (sq + 1) -> dist +
359     2 * sqrt(sq -> dist * (sq + 1) -> dist));
360     r *= sqrt(r);
361    
362     /* Get deviation from current average, and probability that it's due
363     to noise from gaussian distribution based on current variance. Since
364     we are doing this for each colour channel we can also detect
365     chromatic bias. */
366     for (i = 0; i <= 2; ++i) {
367     d [i] = irradAvg [i] - (irrad [i] = fluxMid [i] / r);
368     p [i] = exp(-0.5 * d [i] * d [i] / irradVar [i]);
369     }
370    
371     if (pmapRandom(pmap -> randState) < colorAvg(p)) {
372     /* Deviation is probably noise, so add mid irradiance to history */
373     copycolor(histEnd -> irrad, irrad);
374     totalWeight += histEnd++ -> weight = BIASCOMP_WGT((float)numMid);
375     setcolor(irradAvg, 0, 0, 0);
376     setcolor(irradVar, 0, 0, 0);
377    
378     /* Update average and variance */
379     for (hist = pmap -> biasCompHist; hist < histEnd; ++hist)
380     for (i = 0; i <= 2; i++) {
381     r = hist -> irrad [i];
382     irradAvg [i] += hist -> weight * r;
383     irradVar [i] += hist -> weight * r * r;
384     }
385     for (i = 0; i <= 2; i++) {
386     r = irradAvg [i] /= totalWeight;
387     irradVar [i] = irradVar [i] / totalWeight - r * r;
388     }
389    
390     /* Need more photons -- recurse on [numMid, numHi] */
391     numLo = numMid;
392     copycolor(fluxLo, fluxMid);
393     }
394     else
395     /* Deviation is probably bias -- need fewer photons,
396     so recurse on [numLo, numMid] */
397     numHi = numMid;
398     }
399    
400     --histEnd;
401     for (i = 0; i <= 2; i++) {
402     /* Estimated relative error */
403     d [i] = histEnd -> irrad [i] / irradAvg [i] - 1;
404     /* Divide by 4 / 3 * PI for search volume (r^3 already accounted
405     for) and phase function normalization factor 1 / (4 * PI) */
406     irrad [i] = histEnd -> irrad [i] * 3 / (16 * PI * PI);
407     }
408    
409     /* Update statistix */
410     r = colorAvg(d);
411     if (r < pmap -> minError)
412     pmap -> minError = r;
413     if (r > pmap -> maxError)
414     pmap -> maxError = r;
415     pmap -> rmsError += r * r;
416    
417     if (numMid < pmap -> minGathered)
418     pmap -> minGathered = numMid;
419     if (numMid > pmap -> maxGathered)
420     pmap -> maxGathered = numMid;
421    
422     pmap -> totalGathered += numMid;
423     ++pmap -> numDensity;
424     }
425