ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapbias.c
Revision: 2.2
Committed: Fri May 8 13:20:23 2015 UTC (9 years, 1 month ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.1: +3 -4 lines
Log Message:
Double-counting bugfix for glow sources (thanks DGM!), revised copyright

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