ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.9
Committed: Thu Oct 17 19:09:11 2013 UTC (10 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.8: +50 -12 lines
Log Message:
Fixed bug for closely-space sample points that caused poor convergence

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.8 2013/10/02 20:38:26 greg Exp $";
3 #endif
4 /*
5 * Radial basis function representation for BSDF data.
6 *
7 * G. Ward
8 */
9
10 #define _USE_MATH_DEFINES
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <math.h>
15 #include "bsdfrep.h"
16
17 #ifndef MINRSCA
18 #define MINRSCA 0.15 /* minimum radius scaling factor */
19 #endif
20 #ifndef MAXRSCA
21 #define MAXRSCA 2.7 /* maximum radius scaling factor */
22 #endif
23 #ifndef MAXFRAC
24 #define MAXFRAC 0.5 /* maximum contribution to neighbor */
25 #endif
26 #ifndef NNEIGH
27 #define NNEIGH 10 /* number of neighbors to consider */
28 #endif
29 /* our loaded grid for this incident angle */
30 GRIDVAL dsf_grid[GRIDRES][GRIDRES];
31
32 /* Start new DSF input grid */
33 void
34 new_bsdf_data(double new_theta, double new_phi)
35 {
36 if (!new_input_direction(new_theta, new_phi))
37 exit(1);
38 memset(dsf_grid, 0, sizeof(dsf_grid));
39 }
40
41 /* Add BSDF data point */
42 void
43 add_bsdf_data(double theta_out, double phi_out, double val, int isDSF)
44 {
45 FVECT ovec;
46 int pos[2];
47
48 if (!output_orient) /* check output orientation */
49 output_orient = 1 - 2*(theta_out > 90.);
50 else if (output_orient > 0 ^ theta_out < 90.) {
51 fputs("Cannot handle output angles on both sides of surface\n",
52 stderr);
53 exit(1);
54 }
55 ovec[2] = sin((M_PI/180.)*theta_out);
56 ovec[0] = cos((M_PI/180.)*phi_out) * ovec[2];
57 ovec[1] = sin((M_PI/180.)*phi_out) * ovec[2];
58 ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
59
60 if (val <= 0) /* truncate to zero */
61 val = 0;
62 else if (!isDSF)
63 val *= ovec[2]; /* convert from BSDF to DSF */
64
65 /* update BSDF histogram */
66 if (val < BSDF2BIG*ovec[2] && val > BSDF2SML*ovec[2])
67 ++bsdf_hist[histndx(val/ovec[2])];
68
69 pos_from_vec(pos, ovec);
70
71 dsf_grid[pos[0]][pos[1]].vsum += val;
72 dsf_grid[pos[0]][pos[1]].nval++;
73 }
74
75 /* Compute radii for non-empty bins */
76 /* (distance to furthest empty bin for which non-empty test bin is closest) */
77 static void
78 compute_radii(void)
79 {
80 const int cradmin = ANG2R(.5*M_PI/GRIDRES);
81 unsigned int fill_grid[GRIDRES][GRIDRES];
82 unsigned short fill_cnt[GRIDRES][GRIDRES];
83 FVECT ovec0, ovec1;
84 double ang2, lastang2;
85 int r, i, j, jn, ii, jj, inear, jnear;
86
87 for (i = 0; i < GRIDRES; i++) /* initialize minimum radii */
88 for (j = 0; j < GRIDRES; j++)
89 if (dsf_grid[i][j].nval)
90 dsf_grid[i][j].crad = cradmin;
91
92 r = GRIDRES/2; /* proceed in zig-zag */
93 for (i = 0; i < GRIDRES; i++)
94 for (jn = 0; jn < GRIDRES; jn++) {
95 j = (i&1) ? jn : GRIDRES-1-jn;
96 if (dsf_grid[i][j].nval) /* find empty grid pos. */
97 continue;
98 ovec_from_pos(ovec0, i, j);
99 inear = jnear = -1; /* find nearest non-empty */
100 lastang2 = M_PI*M_PI;
101 for (ii = i-r; ii <= i+r; ii++) {
102 if (ii < 0) continue;
103 if (ii >= GRIDRES) break;
104 for (jj = j-r; jj <= j+r; jj++) {
105 if (jj < 0) continue;
106 if (jj >= GRIDRES) break;
107 if (!dsf_grid[ii][jj].nval)
108 continue;
109 ovec_from_pos(ovec1, ii, jj);
110 ang2 = 2. - 2.*DOT(ovec0,ovec1);
111 if (ang2 >= lastang2)
112 continue;
113 lastang2 = ang2;
114 inear = ii; jnear = jj;
115 }
116 }
117 if (inear < 0) {
118 fprintf(stderr,
119 "%s: Could not find non-empty neighbor!\n",
120 progname);
121 exit(1);
122 }
123 ang2 = sqrt(lastang2);
124 r = ANG2R(ang2); /* record if > previous */
125 if (r > dsf_grid[inear][jnear].crad)
126 dsf_grid[inear][jnear].crad = r;
127 /* next search radius */
128 r = ang2*(2.*GRIDRES/M_PI) + 3;
129 }
130 /* blur radii over hemisphere */
131 memset(fill_grid, 0, sizeof(fill_grid));
132 memset(fill_cnt, 0, sizeof(fill_cnt));
133 for (i = 0; i < GRIDRES; i++)
134 for (j = 0; j < GRIDRES; j++) {
135 if (!dsf_grid[i][j].nval)
136 continue; /* not part of this */
137 r = R2ANG(dsf_grid[i][j].crad)*(2.*MAXRSCA*GRIDRES/M_PI);
138 for (ii = i-r; ii <= i+r; ii++) {
139 if (ii < 0) continue;
140 if (ii >= GRIDRES) break;
141 for (jj = j-r; jj <= j+r; jj++) {
142 if (jj < 0) continue;
143 if (jj >= GRIDRES) break;
144 if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r)
145 continue;
146 fill_grid[ii][jj] += dsf_grid[i][j].crad;
147 fill_cnt[ii][jj]++;
148 }
149 }
150 }
151 /* copy back blurred radii */
152 for (i = 0; i < GRIDRES; i++)
153 for (j = 0; j < GRIDRES; j++)
154 if (fill_cnt[i][j])
155 dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j];
156 }
157
158 /* Cull points for more uniform distribution, leave all nval 0 or 1 */
159 static void
160 cull_values(void)
161 {
162 FVECT ovec0, ovec1;
163 double maxang, maxang2;
164 int i, j, ii, jj, r;
165 /* simple greedy algorithm */
166 for (i = 0; i < GRIDRES; i++)
167 for (j = 0; j < GRIDRES; j++) {
168 if (!dsf_grid[i][j].nval)
169 continue;
170 if (!dsf_grid[i][j].crad)
171 continue; /* shouldn't happen */
172 ovec_from_pos(ovec0, i, j);
173 maxang = 2.*R2ANG(dsf_grid[i][j].crad);
174 if (maxang > ovec0[2]) /* clamp near horizon */
175 maxang = ovec0[2];
176 r = maxang*(2.*GRIDRES/M_PI) + 1;
177 maxang2 = maxang*maxang;
178 for (ii = i-r; ii <= i+r; ii++) {
179 if (ii < 0) continue;
180 if (ii >= GRIDRES) break;
181 for (jj = j-r; jj <= j+r; jj++) {
182 if ((ii == i) & (jj == j))
183 continue; /* don't get self-absorbed */
184 if (jj < 0) continue;
185 if (jj >= GRIDRES) break;
186 if (!dsf_grid[ii][jj].nval)
187 continue;
188 ovec_from_pos(ovec1, ii, jj);
189 if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
190 continue;
191 /* absorb sum */
192 dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum;
193 dsf_grid[i][j].nval += dsf_grid[ii][jj].nval;
194 /* keep value, though */
195 dsf_grid[ii][jj].vsum /= (float)dsf_grid[ii][jj].nval;
196 dsf_grid[ii][jj].nval = 0;
197 }
198 }
199 }
200 /* final averaging pass */
201 for (i = 0; i < GRIDRES; i++)
202 for (j = 0; j < GRIDRES; j++)
203 if (dsf_grid[i][j].nval > 1) {
204 dsf_grid[i][j].vsum /= (float)dsf_grid[i][j].nval;
205 dsf_grid[i][j].nval = 1;
206 }
207 }
208
209 /* Compute minimum BSDF from histogram and clear it */
210 static void
211 comp_bsdf_min()
212 {
213 int cnt;
214 int i, target;
215
216 cnt = 0;
217 for (i = HISTLEN; i--; )
218 cnt += bsdf_hist[i];
219 if (!cnt) { /* shouldn't happen */
220 bsdf_min = 0;
221 return;
222 }
223 target = cnt/100; /* ignore bottom 1% */
224 cnt = 0;
225 for (i = 0; cnt <= target; i++)
226 cnt += bsdf_hist[i];
227 bsdf_min = histval(i-1);
228 memset(bsdf_hist, 0, sizeof(bsdf_hist));
229 }
230
231 /* Find n nearest sub-sampled neighbors to the given grid position */
232 static int
233 get_neighbors(int neigh[][2], int n, const int i, const int j)
234 {
235 int k = 0;
236 int r;
237 /* search concentric squares */
238 for (r = 1; r < GRIDRES; r++) {
239 int ii, jj;
240 for (ii = i-r; ii <= i+r; ii++) {
241 int jstep = 1;
242 if (ii < 0) continue;
243 if (ii >= GRIDRES) break;
244 if ((i-r < ii) & (ii < i+r))
245 jstep = r<<1;
246 for (jj = j-r; jj <= j+r; jj += jstep) {
247 if (jj < 0) continue;
248 if (jj >= GRIDRES) break;
249 if (dsf_grid[ii][jj].nval) {
250 neigh[k][0] = ii;
251 neigh[k][1] = jj;
252 if (++k >= n)
253 return(n);
254 }
255 }
256 }
257 }
258 return(k);
259 }
260
261 /* Adjust coded radius for the given grid position based on neighborhood */
262 static int
263 adj_coded_radius(const int i, const int j)
264 {
265 const double rad0 = R2ANG(dsf_grid[i][j].crad);
266 const double minrad = MINRSCA * rad0;
267 double currad = MAXRSCA * rad0;
268 int neigh[NNEIGH][2];
269 int n;
270 FVECT our_dir;
271
272 ovec_from_pos(our_dir, i, j);
273 n = get_neighbors(neigh, NNEIGH, i, j);
274 while (n--) {
275 FVECT their_dir;
276 double max_ratio, rad_ok2;
277 /* check our value at neighbor */
278 ovec_from_pos(their_dir, neigh[n][0], neigh[n][1]);
279 max_ratio = MAXFRAC * dsf_grid[neigh[n][0]][neigh[n][1]].vsum
280 / dsf_grid[i][j].vsum;
281 if (max_ratio >= 1)
282 continue;
283 rad_ok2 = (DOT(their_dir,our_dir) - 1.)/log(max_ratio);
284 if (rad_ok2 >= currad*currad)
285 continue; /* value fraction OK */
286 currad = sqrt(rad_ok2); /* else reduce lobe radius */
287 if (currad <= minrad) /* limit how small we'll go */
288 return(ANG2R(minrad));
289 }
290 return(ANG2R(currad)); /* encode selected radius */
291 }
292
293 /* Count up filled nodes and build RBF representation from current grid */
294 RBFNODE *
295 make_rbfrep(void)
296 {
297 long cradsum = 0, ocradsum = 0;
298 int niter = 16;
299 double lastVar, thisVar = 100.;
300 int nn;
301 RBFNODE *newnode;
302 RBFVAL *itera;
303 int i, j;
304
305 #ifdef DEBUG
306 {
307 int maxcnt = 0, nempty = 0;
308 long cntsum = 0;
309 for (i = 0; i < GRIDRES; i++)
310 for (j = 0; j < GRIDRES; j++)
311 if (!dsf_grid[i][j].nval) {
312 ++nempty;
313 } else {
314 if (dsf_grid[i][j].nval > maxcnt)
315 maxcnt = dsf_grid[i][j].nval;
316 cntsum += dsf_grid[i][j].nval;
317 }
318 fprintf(stderr, "Average, maximum bin count: %d, %d (%.1f%% empty)\n",
319 (int)(cntsum/((GRIDRES*GRIDRES)-nempty)), maxcnt,
320 100./(GRIDRES*GRIDRES)*nempty);
321 }
322 #endif
323 /* compute RBF radii */
324 compute_radii();
325 /* coagulate lobes */
326 cull_values();
327 nn = 0; /* count selected bins */
328 for (i = 0; i < GRIDRES; i++)
329 for (j = 0; j < GRIDRES; j++)
330 nn += dsf_grid[i][j].nval;
331 /* compute minimum BSDF */
332 comp_bsdf_min();
333 /* allocate RBF array */
334 newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
335 if (newnode == NULL)
336 goto memerr;
337 newnode->ord = -1;
338 newnode->next = NULL;
339 newnode->ejl = NULL;
340 newnode->invec[2] = sin((M_PI/180.)*theta_in_deg);
341 newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2];
342 newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2];
343 newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
344 newnode->vtotal = 0;
345 newnode->nrbf = nn;
346 nn = 0; /* fill RBF array */
347 for (i = 0; i < GRIDRES; i++)
348 for (j = 0; j < GRIDRES; j++)
349 if (dsf_grid[i][j].nval) {
350 newnode->rbfa[nn].peak = dsf_grid[i][j].vsum;
351 ocradsum += dsf_grid[i][j].crad;
352 cradsum +=
353 newnode->rbfa[nn].crad = adj_coded_radius(i, j);
354 newnode->rbfa[nn].gx = i;
355 newnode->rbfa[nn].gy = j;
356 ++nn;
357 }
358 #ifdef DEBUG
359 fprintf(stderr,
360 "Average radius reduced from %.2f to %.2f degrees for %d lobes\n",
361 180./M_PI*MAXRSCA*R2ANG(ocradsum/newnode->nrbf),
362 180./M_PI*R2ANG(cradsum/newnode->nrbf), newnode->nrbf);
363 #endif
364 /* iterate to improve interpolation accuracy */
365 itera = (RBFVAL *)malloc(sizeof(RBFVAL)*newnode->nrbf);
366 if (itera == NULL)
367 goto memerr;
368 memcpy(itera, newnode->rbfa, sizeof(RBFVAL)*newnode->nrbf);
369 do {
370 double dsum = 0, dsum2 = 0;
371 nn = 0;
372 for (i = 0; i < GRIDRES; i++)
373 for (j = 0; j < GRIDRES; j++)
374 if (dsf_grid[i][j].nval) {
375 FVECT odir;
376 double corr;
377 ovec_from_pos(odir, i, j);
378 itera[nn++].peak *= corr =
379 dsf_grid[i][j].vsum /
380 eval_rbfrep(newnode, odir);
381 dsum += 1. - corr;
382 dsum2 += (1.-corr)*(1.-corr);
383 }
384 memcpy(newnode->rbfa, itera, sizeof(RBFVAL)*newnode->nrbf);
385 lastVar = thisVar;
386 thisVar = dsum2/(double)nn;
387 #ifdef DEBUG
388 fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n",
389 100.*dsum/(double)nn,
390 100.*sqrt(thisVar));
391 #endif
392 } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar);
393
394 free(itera);
395 nn = 0; /* compute sum for normalization */
396 while (nn < newnode->nrbf)
397 newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
398 #ifdef DEBUG
399 fprintf(stderr, "Integrated DSF at (%.1f,%.1f) deg. is %.2f\n",
400 get_theta180(newnode->invec), get_phi360(newnode->invec),
401 newnode->vtotal);
402 #endif
403 insert_dsf(newnode);
404
405 return(newnode);
406 memerr:
407 fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
408 exit(1);
409 }