ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.10
Committed: Fri Oct 18 02:49:30 2013 UTC (10 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.9: +49 -9 lines
Log Message:
Fixed bug for transmission and improved culling in uniform regions

File Contents

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