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

# 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 ([email protected])
10 (c) Fraunhofer Institute for Solar Energy Systems
11 ==================================================================
12
13 $Id: pmapbias.c,v 2.1 2015/02/24 19:39:26 greg Exp $
14 */
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