ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.18
Committed: Tue Feb 18 16:42:16 2014 UTC (10 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.17: +3 -3 lines
Log Message:
Reduced error thresholds to improve lobe sampling

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.18 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.17 2014/02/17 21:56:22 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Radial basis function representation for BSDF data.
6     *
7     * G. Ward
8     */
9    
10 greg 2.13 /****************************************************************
11     1) Collect samples into a grid using the Shirley-Chiu
12     angular mapping from a hemisphere to a square.
13    
14     2) Compute an adaptive quadtree by subdividing the grid so that
15     each leaf node has at least one sample up to as many
16     samples as fit nicely on a plane to within a certain
17     MSE tolerance.
18    
19     3) Place one Gaussian lobe at each leaf node in the quadtree,
20     sizing it to have a radius equal to the leaf size and
21     a volume equal to the energy in that node.
22     *****************************************************************/
23    
24 greg 2.1 #define _USE_MATH_DEFINES
25     #include <stdio.h>
26     #include <stdlib.h>
27     #include <string.h>
28     #include <math.h>
29     #include "bsdfrep.h"
30    
31 greg 2.12 #ifndef RSCA
32     #define RSCA 2.2 /* radius scaling factor (empirical) */
33 greg 2.9 #endif
34 greg 2.12 #ifndef SMOOTH_MSE
35 greg 2.18 #define SMOOTH_MSE 2e-5 /* acceptable mean squared error */
36 greg 2.1 #endif
37 greg 2.12 #ifndef SMOOTH_MSER
38 greg 2.18 #define SMOOTH_MSER 0.03 /* acceptable relative MSE */
39 greg 2.7 #endif
40 greg 2.12 #define MAX_RAD (GRIDRES/8) /* maximum lobe radius */
41    
42     #define RBFALLOCB 10 /* RBF allocation block size */
43    
44 greg 2.1 /* our loaded grid for this incident angle */
45     GRIDVAL dsf_grid[GRIDRES][GRIDRES];
46    
47     /* Start new DSF input grid */
48     void
49     new_bsdf_data(double new_theta, double new_phi)
50     {
51     if (!new_input_direction(new_theta, new_phi))
52     exit(1);
53     memset(dsf_grid, 0, sizeof(dsf_grid));
54     }
55    
56     /* Add BSDF data point */
57     void
58     add_bsdf_data(double theta_out, double phi_out, double val, int isDSF)
59     {
60     FVECT ovec;
61     int pos[2];
62    
63     if (!output_orient) /* check output orientation */
64     output_orient = 1 - 2*(theta_out > 90.);
65     else if (output_orient > 0 ^ theta_out < 90.) {
66     fputs("Cannot handle output angles on both sides of surface\n",
67     stderr);
68     exit(1);
69     }
70     ovec[2] = sin((M_PI/180.)*theta_out);
71     ovec[0] = cos((M_PI/180.)*phi_out) * ovec[2];
72     ovec[1] = sin((M_PI/180.)*phi_out) * ovec[2];
73     ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
74    
75 greg 2.8 if (val <= 0) /* truncate to zero */
76     val = 0;
77     else if (!isDSF)
78 greg 2.1 val *= ovec[2]; /* convert from BSDF to DSF */
79    
80 greg 2.4 /* update BSDF histogram */
81     if (val < BSDF2BIG*ovec[2] && val > BSDF2SML*ovec[2])
82     ++bsdf_hist[histndx(val/ovec[2])];
83    
84 greg 2.1 pos_from_vec(pos, ovec);
85    
86     dsf_grid[pos[0]][pos[1]].vsum += val;
87     dsf_grid[pos[0]][pos[1]].nval++;
88     }
89    
90 greg 2.11 /* Compute minimum BSDF from histogram (does not clear) */
91 greg 2.5 static void
92     comp_bsdf_min()
93     {
94 greg 2.17 unsigned long cnt, target;
95     int i;
96 greg 2.5
97     cnt = 0;
98     for (i = HISTLEN; i--; )
99     cnt += bsdf_hist[i];
100     if (!cnt) { /* shouldn't happen */
101     bsdf_min = 0;
102     return;
103     }
104     target = cnt/100; /* ignore bottom 1% */
105     cnt = 0;
106     for (i = 0; cnt <= target; i++)
107     cnt += bsdf_hist[i];
108     bsdf_min = histval(i-1);
109     }
110    
111 greg 2.12 /* Determine if the given region is empty of grid samples */
112 greg 2.6 static int
113 greg 2.12 empty_region(int x0, int x1, int y0, int y1)
114 greg 2.6 {
115 greg 2.12 int x, y;
116    
117     for (x = x0; x < x1; x++)
118     for (y = y0; y < y1; y++)
119     if (dsf_grid[x][y].nval)
120     return(0);
121     return(1);
122     }
123    
124     /* Determine if the given region is smooth enough to be a single lobe */
125     static int
126     smooth_region(int x0, int x1, int y0, int y1)
127     {
128     RREAL rMtx[3][3];
129     FVECT xvec;
130     double A, B, C, nvs, sqerr;
131     int x, y, n;
132     /* compute planar regression */
133     memset(rMtx, 0, sizeof(rMtx));
134     memset(xvec, 0, sizeof(xvec));
135     for (x = x0; x < x1; x++)
136     for (y = y0; y < y1; y++)
137     if ((n = dsf_grid[x][y].nval) > 0) {
138     double z = dsf_grid[x][y].vsum;
139 greg 2.13 rMtx[0][0] += x*x*(double)n;
140     rMtx[0][1] += x*y*(double)n;
141     rMtx[0][2] += x*(double)n;
142     rMtx[1][1] += y*y*(double)n;
143     rMtx[1][2] += y*(double)n;
144     rMtx[2][2] += (double)n;
145 greg 2.12 xvec[0] += x*z;
146     xvec[1] += y*z;
147     xvec[2] += z;
148     }
149     rMtx[1][0] = rMtx[0][1];
150 greg 2.15 rMtx[2][0] = rMtx[0][2];
151 greg 2.12 rMtx[2][1] = rMtx[1][2];
152     nvs = rMtx[2][2];
153     if (SDinvXform(rMtx, rMtx) != SDEnone)
154 greg 2.16 return(1); /* colinear values */
155 greg 2.12 A = DOT(rMtx[0], xvec);
156     B = DOT(rMtx[1], xvec);
157     C = DOT(rMtx[2], xvec);
158     sqerr = 0.0; /* compute mean squared error */
159     for (x = x0; x < x1; x++)
160     for (y = y0; y < y1; y++)
161     if ((n = dsf_grid[x][y].nval) > 0) {
162     double d = A*x + B*y + C - dsf_grid[x][y].vsum/n;
163     sqerr += n*d*d;
164 greg 2.6 }
165 greg 2.12 if (sqerr <= nvs*SMOOTH_MSE) /* below absolute MSE threshold? */
166     return(1);
167 greg 2.13 /* OR below relative MSE threshold? */
168 greg 2.12 return(sqerr*nvs <= xvec[2]*xvec[2]*SMOOTH_MSER);
169     }
170    
171     /* Create new lobe based on integrated samples in region */
172     static void
173     create_lobe(RBFVAL *rvp, int x0, int x1, int y0, int y1)
174     {
175     double vtot = 0.0;
176     int nv = 0;
177     double rad;
178     int x, y;
179     /* compute average for region */
180     for (x = x0; x < x1; x++)
181     for (y = y0; y < y1; y++) {
182     vtot += dsf_grid[x][y].vsum;
183     nv += dsf_grid[x][y].nval;
184     }
185     if (!nv) {
186     fprintf(stderr, "%s: internal - missing samples in create_lobe\n",
187     progname);
188     exit(1);
189 greg 2.6 }
190 greg 2.12 /* peak value based on integral */
191     vtot *= (x1-x0)*(y1-y0)*(2.*M_PI/GRIDRES/GRIDRES)/(double)nv;
192     rad = (RSCA/(double)GRIDRES)*(x1-x0);
193     rvp->peak = vtot / ((2.*M_PI) * rad*rad);
194     rvp->crad = ANG2R(rad);
195     rvp->gx = (x0+x1)>>1;
196     rvp->gy = (y0+y1)>>1;
197 greg 2.6 }
198    
199 greg 2.12 /* Recursive function to build radial basis function representation */
200 greg 2.6 static int
201 greg 2.12 build_rbfrep(RBFVAL **arp, int *np, int x0, int x1, int y0, int y1)
202 greg 2.6 {
203 greg 2.12 int xmid = (x0+x1)>>1;
204     int ymid = (y0+y1)>>1;
205     int branched[4];
206     int nadded, nleaves;
207     /* need to make this a leaf? */
208     if (empty_region(x0, xmid, y0, ymid) ||
209     empty_region(xmid, x1, y0, ymid) ||
210     empty_region(x0, xmid, ymid, y1) ||
211     empty_region(xmid, x1, ymid, y1))
212     return(0);
213     /* add children (branches+leaves) */
214     if ((branched[0] = build_rbfrep(arp, np, x0, xmid, y0, ymid)) < 0)
215     return(-1);
216     if ((branched[1] = build_rbfrep(arp, np, xmid, x1, y0, ymid)) < 0)
217     return(-1);
218     if ((branched[2] = build_rbfrep(arp, np, x0, xmid, ymid, y1)) < 0)
219     return(-1);
220     if ((branched[3] = build_rbfrep(arp, np, xmid, x1, ymid, y1)) < 0)
221     return(-1);
222     nadded = branched[0] + branched[1] + branched[2] + branched[3];
223     nleaves = !branched[0] + !branched[1] + !branched[2] + !branched[3];
224     if (!nleaves) /* nothing but branches? */
225     return(nadded);
226     /* combine 4 leaves into 1? */
227 greg 2.14 if ((nleaves == 4) & (x1-x0 <= MAX_RAD) &&
228     smooth_region(x0, x1, y0, y1))
229 greg 2.12 return(0);
230     /* need more array space? */
231     if ((*np+nleaves-1)>>RBFALLOCB != (*np-1)>>RBFALLOCB) {
232     *arp = (RBFVAL *)realloc(*arp,
233     sizeof(RBFVAL)*(*np+nleaves-1+(1<<RBFALLOCB)));
234     if (*arp == NULL)
235     return(-1);
236 greg 2.6 }
237 greg 2.12 /* create lobes for leaves */
238     if (!branched[0])
239     create_lobe(*arp+(*np)++, x0, xmid, y0, ymid);
240     if (!branched[1])
241     create_lobe(*arp+(*np)++, xmid, x1, y0, ymid);
242     if (!branched[2])
243     create_lobe(*arp+(*np)++, x0, xmid, ymid, y1);
244     if (!branched[3])
245     create_lobe(*arp+(*np)++, xmid, x1, ymid, y1);
246     nadded += nleaves;
247     return(nadded);
248 greg 2.6 }
249    
250 greg 2.1 /* Count up filled nodes and build RBF representation from current grid */
251     RBFNODE *
252 greg 2.12 make_rbfrep()
253 greg 2.1 {
254 greg 2.12 RBFNODE *newnode;
255     RBFVAL *rbfarr;
256 greg 2.1 int nn;
257 greg 2.5 /* compute minimum BSDF */
258     comp_bsdf_min();
259 greg 2.12 /* create RBF node list */
260     rbfarr = NULL; nn = 0;
261     if (build_rbfrep(&rbfarr, &nn, 0, GRIDRES, 0, GRIDRES) <= 0)
262     goto memerr;
263     /* (re)allocate RBF array */
264     newnode = (RBFNODE *)realloc(rbfarr,
265     sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
266 greg 2.2 if (newnode == NULL)
267     goto memerr;
268 greg 2.12 /* copy computed lobes into RBF node */
269     memmove(newnode->rbfa, newnode, sizeof(RBFVAL)*nn);
270 greg 2.1 newnode->ord = -1;
271     newnode->next = NULL;
272     newnode->ejl = NULL;
273     newnode->invec[2] = sin((M_PI/180.)*theta_in_deg);
274     newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2];
275     newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2];
276     newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
277 greg 2.12 newnode->vtotal = .0;
278 greg 2.1 newnode->nrbf = nn;
279 greg 2.12 /* compute sum for normalization */
280     while (nn-- > 0)
281     newnode->vtotal += rbf_volume(&newnode->rbfa[nn]);
282 greg 2.3 #ifdef DEBUG
283 greg 2.12 fprintf(stderr, "Built RBF with %d lobes\n", newnode->nrbf);
284 greg 2.3 fprintf(stderr, "Integrated DSF at (%.1f,%.1f) deg. is %.2f\n",
285     get_theta180(newnode->invec), get_phi360(newnode->invec),
286     newnode->vtotal);
287     #endif
288 greg 2.1 insert_dsf(newnode);
289    
290     return(newnode);
291 greg 2.2 memerr:
292     fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
293     exit(1);
294 greg 2.1 }