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

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.6 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.5 2013/06/28 23:18:51 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     #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 greg 2.4 /* update BSDF histogram */
55     if (val < BSDF2BIG*ovec[2] && val > BSDF2SML*ovec[2])
56     ++bsdf_hist[histndx(val/ovec[2])];
57    
58 greg 2.1 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 greg 2.5 /* 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 greg 2.6 /* 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 greg 2.1 /* 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 greg 2.2 RBFVAL *itera;
285 greg 2.1 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 greg 2.5 /* compute minimum BSDF */
295     comp_bsdf_min();
296 greg 2.1 /* allocate RBF array */
297     newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
298 greg 2.2 if (newnode == NULL)
299     goto memerr;
300 greg 2.1 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 greg 2.6 newnode->rbfa[nn].crad = adj_coded_radius(i, j);
315 greg 2.1 newnode->rbfa[nn].gx = i;
316     newnode->rbfa[nn].gy = j;
317     ++nn;
318     }
319     /* iterate to improve interpolation accuracy */
320 greg 2.2 itera = (RBFVAL *)malloc(sizeof(RBFVAL)*newnode->nrbf);
321     if (itera == NULL)
322     goto memerr;
323     memcpy(itera, newnode->rbfa, sizeof(RBFVAL)*newnode->nrbf);
324 greg 2.1 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 greg 2.2 itera[nn++].peak *= corr =
334 greg 2.1 dsf_grid[i][j].vsum /
335     eval_rbfrep(newnode, odir);
336 greg 2.2 dsum += 1. - corr;
337     dsum2 += (1.-corr)*(1.-corr);
338 greg 2.1 }
339 greg 2.2 memcpy(newnode->rbfa, itera, sizeof(RBFVAL)*newnode->nrbf);
340 greg 2.1 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 greg 2.2 free(itera);
350 greg 2.1 nn = 0; /* compute sum for normalization */
351     while (nn < newnode->nrbf)
352     newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
353 greg 2.3 #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 greg 2.1 insert_dsf(newnode);
359    
360     return(newnode);
361 greg 2.2 memerr:
362     fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
363     exit(1);
364 greg 2.1 }