ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.6
Committed: Wed Sep 25 05:03:10 2013 UTC (10 years, 7 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.5: +64 -2 lines
Log Message:
Put limit on lobe radius based on neighbor values

File Contents

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