ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.8
Committed: Wed Oct 2 20:38:26 2013 UTC (10 years, 7 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.7: +4 -2 lines
Log Message:
Added test to prevent tally of negative BSDF values

File Contents

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