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

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.8 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.7 2013/09/25 17:42:45 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 greg 2.7 #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 greg 2.1 /* 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 greg 2.8 if (val <= 0) /* truncate to zero */
58     val = 0;
59     else if (!isDSF)
60 greg 2.1 val *= ovec[2]; /* convert from BSDF to DSF */
61    
62 greg 2.4 /* update BSDF histogram */
63     if (val < BSDF2BIG*ovec[2] && val > BSDF2SML*ovec[2])
64     ++bsdf_hist[histndx(val/ovec[2])];
65    
66 greg 2.1 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 greg 2.5 /* 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 greg 2.6 /* 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 greg 2.7 int neigh[NNEIGH][2];
259 greg 2.6 int n;
260     FVECT our_dir;
261    
262     ovec_from_pos(our_dir, i, j);
263 greg 2.7 n = get_neighbors(neigh, NNEIGH, i, j);
264 greg 2.6 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 greg 2.7 max_ratio = MAXFRAC * dsf_grid[neigh[n][0]][neigh[n][1]].vsum
270 greg 2.6 / 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 greg 2.1 /* 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 greg 2.2 RBFVAL *itera;
292 greg 2.1 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 greg 2.5 /* compute minimum BSDF */
302     comp_bsdf_min();
303 greg 2.1 /* allocate RBF array */
304     newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
305 greg 2.2 if (newnode == NULL)
306     goto memerr;
307 greg 2.1 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 greg 2.6 newnode->rbfa[nn].crad = adj_coded_radius(i, j);
322 greg 2.1 newnode->rbfa[nn].gx = i;
323     newnode->rbfa[nn].gy = j;
324     ++nn;
325     }
326     /* iterate to improve interpolation accuracy */
327 greg 2.2 itera = (RBFVAL *)malloc(sizeof(RBFVAL)*newnode->nrbf);
328     if (itera == NULL)
329     goto memerr;
330     memcpy(itera, newnode->rbfa, sizeof(RBFVAL)*newnode->nrbf);
331 greg 2.1 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 greg 2.2 itera[nn++].peak *= corr =
341 greg 2.1 dsf_grid[i][j].vsum /
342     eval_rbfrep(newnode, odir);
343 greg 2.2 dsum += 1. - corr;
344     dsum2 += (1.-corr)*(1.-corr);
345 greg 2.1 }
346 greg 2.2 memcpy(newnode->rbfa, itera, sizeof(RBFVAL)*newnode->nrbf);
347 greg 2.1 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 greg 2.2 free(itera);
357 greg 2.1 nn = 0; /* compute sum for normalization */
358     while (nn < newnode->nrbf)
359     newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
360 greg 2.3 #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 greg 2.1 insert_dsf(newnode);
366    
367     return(newnode);
368 greg 2.2 memerr:
369     fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
370     exit(1);
371 greg 2.1 }