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, 3 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Initial check-in of photon map addition by Roland Schregle

File Contents

# Content
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