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

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.9 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.8 2013/10/02 20:38:26 greg Exp $";
3 greg 2.1 #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 greg 2.9 #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 greg 2.1 #endif
23 greg 2.7 #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 greg 2.1 /* 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 greg 2.8 if (val <= 0) /* truncate to zero */
61     val = 0;
62     else if (!isDSF)
63 greg 2.1 val *= ovec[2]; /* convert from BSDF to DSF */
64    
65 greg 2.4 /* update BSDF histogram */
66     if (val < BSDF2BIG*ovec[2] && val > BSDF2SML*ovec[2])
67     ++bsdf_hist[histndx(val/ovec[2])];
68    
69 greg 2.1 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 greg 2.9 /* (distance to furthest empty bin for which non-empty test bin is closest) */
77 greg 2.1 static void
78     compute_radii(void)
79     {
80 greg 2.9 const int cradmin = ANG2R(.5*M_PI/GRIDRES);
81 greg 2.1 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 greg 2.9 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 greg 2.1 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 greg 2.9 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 greg 2.1 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 greg 2.9 if ((ii == i) & (jj == j))
183     continue; /* don't get self-absorbed */
184 greg 2.1 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 greg 2.5 /* 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 greg 2.6 /* 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 greg 2.9 const double minrad = MINRSCA * rad0;
267     double currad = MAXRSCA * rad0;
268 greg 2.7 int neigh[NNEIGH][2];
269 greg 2.6 int n;
270     FVECT our_dir;
271    
272     ovec_from_pos(our_dir, i, j);
273 greg 2.7 n = get_neighbors(neigh, NNEIGH, i, j);
274 greg 2.6 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 greg 2.7 max_ratio = MAXFRAC * dsf_grid[neigh[n][0]][neigh[n][1]].vsum
280 greg 2.6 / 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 greg 2.9 if (currad <= minrad) /* limit how small we'll go */
288     return(ANG2R(minrad));
289 greg 2.6 }
290     return(ANG2R(currad)); /* encode selected radius */
291     }
292    
293 greg 2.1 /* Count up filled nodes and build RBF representation from current grid */
294     RBFNODE *
295     make_rbfrep(void)
296     {
297 greg 2.9 long cradsum = 0, ocradsum = 0;
298 greg 2.1 int niter = 16;
299     double lastVar, thisVar = 100.;
300     int nn;
301     RBFNODE *newnode;
302 greg 2.2 RBFVAL *itera;
303 greg 2.1 int i, j;
304 greg 2.9
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 greg 2.1 /* 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 greg 2.5 /* compute minimum BSDF */
332     comp_bsdf_min();
333 greg 2.1 /* allocate RBF array */
334     newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
335 greg 2.2 if (newnode == NULL)
336     goto memerr;
337 greg 2.1 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 greg 2.9 ocradsum += dsf_grid[i][j].crad;
352     cradsum +=
353 greg 2.6 newnode->rbfa[nn].crad = adj_coded_radius(i, j);
354 greg 2.1 newnode->rbfa[nn].gx = i;
355     newnode->rbfa[nn].gy = j;
356     ++nn;
357     }
358 greg 2.9 #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 greg 2.1 /* iterate to improve interpolation accuracy */
365 greg 2.2 itera = (RBFVAL *)malloc(sizeof(RBFVAL)*newnode->nrbf);
366     if (itera == NULL)
367     goto memerr;
368     memcpy(itera, newnode->rbfa, sizeof(RBFVAL)*newnode->nrbf);
369 greg 2.1 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 greg 2.2 itera[nn++].peak *= corr =
379 greg 2.1 dsf_grid[i][j].vsum /
380     eval_rbfrep(newnode, odir);
381 greg 2.2 dsum += 1. - corr;
382     dsum2 += (1.-corr)*(1.-corr);
383 greg 2.1 }
384 greg 2.2 memcpy(newnode->rbfa, itera, sizeof(RBFVAL)*newnode->nrbf);
385 greg 2.1 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 greg 2.2 free(itera);
395 greg 2.1 nn = 0; /* compute sum for normalization */
396     while (nn < newnode->nrbf)
397     newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
398 greg 2.3 #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 greg 2.1 insert_dsf(newnode);
404    
405     return(newnode);
406 greg 2.2 memerr:
407     fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
408     exit(1);
409 greg 2.1 }