ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.24
Committed: Fri Mar 21 01:04:42 2014 UTC (10 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.23: +5 -5 lines
Log Message:
Changed behavior so bad incident directions are skipped rather than dying

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.24 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.23 2014/03/21 00:42:46 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.19 #define SMOOTH_MSE 5e-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.20 /* our loaded grid or comparison DSFs */
45 greg 2.1 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.21 if (!isDSF)
76 greg 2.1 val *= ovec[2]; /* convert from BSDF to DSF */
77    
78 greg 2.4 /* update BSDF histogram */
79     if (val < BSDF2BIG*ovec[2] && val > BSDF2SML*ovec[2])
80     ++bsdf_hist[histndx(val/ovec[2])];
81    
82 greg 2.1 pos_from_vec(pos, ovec);
83    
84 greg 2.20 dsf_grid[pos[0]][pos[1]].sum.v += val;
85     dsf_grid[pos[0]][pos[1]].sum.n++;
86 greg 2.1 }
87    
88 greg 2.11 /* Compute minimum BSDF from histogram (does not clear) */
89 greg 2.5 static void
90     comp_bsdf_min()
91     {
92 greg 2.17 unsigned long cnt, target;
93     int i;
94 greg 2.5
95     cnt = 0;
96     for (i = HISTLEN; i--; )
97     cnt += bsdf_hist[i];
98     if (!cnt) { /* shouldn't happen */
99     bsdf_min = 0;
100     return;
101     }
102     target = cnt/100; /* ignore bottom 1% */
103     cnt = 0;
104     for (i = 0; cnt <= target; i++)
105     cnt += bsdf_hist[i];
106     bsdf_min = histval(i-1);
107     }
108    
109 greg 2.12 /* Determine if the given region is empty of grid samples */
110 greg 2.6 static int
111 greg 2.12 empty_region(int x0, int x1, int y0, int y1)
112 greg 2.6 {
113 greg 2.12 int x, y;
114    
115     for (x = x0; x < x1; x++)
116     for (y = y0; y < y1; y++)
117 greg 2.20 if (dsf_grid[x][y].sum.n)
118 greg 2.12 return(0);
119     return(1);
120     }
121    
122     /* Determine if the given region is smooth enough to be a single lobe */
123     static int
124     smooth_region(int x0, int x1, int y0, int y1)
125     {
126     RREAL rMtx[3][3];
127     FVECT xvec;
128     double A, B, C, nvs, sqerr;
129     int x, y, n;
130     /* compute planar regression */
131     memset(rMtx, 0, sizeof(rMtx));
132     memset(xvec, 0, sizeof(xvec));
133     for (x = x0; x < x1; x++)
134     for (y = y0; y < y1; y++)
135 greg 2.20 if ((n = dsf_grid[x][y].sum.n) > 0) {
136     double z = dsf_grid[x][y].sum.v;
137 greg 2.13 rMtx[0][0] += x*x*(double)n;
138     rMtx[0][1] += x*y*(double)n;
139     rMtx[0][2] += x*(double)n;
140     rMtx[1][1] += y*y*(double)n;
141     rMtx[1][2] += y*(double)n;
142     rMtx[2][2] += (double)n;
143 greg 2.12 xvec[0] += x*z;
144     xvec[1] += y*z;
145     xvec[2] += z;
146     }
147     rMtx[1][0] = rMtx[0][1];
148 greg 2.15 rMtx[2][0] = rMtx[0][2];
149 greg 2.12 rMtx[2][1] = rMtx[1][2];
150     nvs = rMtx[2][2];
151     if (SDinvXform(rMtx, rMtx) != SDEnone)
152 greg 2.16 return(1); /* colinear values */
153 greg 2.12 A = DOT(rMtx[0], xvec);
154     B = DOT(rMtx[1], xvec);
155     C = DOT(rMtx[2], xvec);
156     sqerr = 0.0; /* compute mean squared error */
157     for (x = x0; x < x1; x++)
158     for (y = y0; y < y1; y++)
159 greg 2.20 if ((n = dsf_grid[x][y].sum.n) > 0) {
160     double d = A*x + B*y + C - dsf_grid[x][y].sum.v/n;
161 greg 2.12 sqerr += n*d*d;
162 greg 2.6 }
163 greg 2.12 if (sqerr <= nvs*SMOOTH_MSE) /* below absolute MSE threshold? */
164     return(1);
165 greg 2.13 /* OR below relative MSE threshold? */
166 greg 2.12 return(sqerr*nvs <= xvec[2]*xvec[2]*SMOOTH_MSER);
167     }
168    
169     /* Create new lobe based on integrated samples in region */
170 greg 2.21 static int
171 greg 2.12 create_lobe(RBFVAL *rvp, int x0, int x1, int y0, int y1)
172     {
173     double vtot = 0.0;
174     int nv = 0;
175     double rad;
176     int x, y;
177     /* compute average for region */
178     for (x = x0; x < x1; x++)
179     for (y = y0; y < y1; y++) {
180 greg 2.20 vtot += dsf_grid[x][y].sum.v;
181     nv += dsf_grid[x][y].sum.n;
182 greg 2.12 }
183     if (!nv) {
184     fprintf(stderr, "%s: internal - missing samples in create_lobe\n",
185     progname);
186     exit(1);
187 greg 2.6 }
188 greg 2.21 if (vtot <= 0) /* only create positive lobes */
189     return(0);
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.21 return(1);
198 greg 2.6 }
199    
200 greg 2.12 /* Recursive function to build radial basis function representation */
201 greg 2.6 static int
202 greg 2.12 build_rbfrep(RBFVAL **arp, int *np, int x0, int x1, int y0, int y1)
203 greg 2.6 {
204 greg 2.12 int xmid = (x0+x1)>>1;
205     int ymid = (y0+y1)>>1;
206     int branched[4];
207     int nadded, nleaves;
208     /* need to make this a leaf? */
209     if (empty_region(x0, xmid, y0, ymid) ||
210     empty_region(xmid, x1, y0, ymid) ||
211     empty_region(x0, xmid, ymid, y1) ||
212     empty_region(xmid, x1, ymid, y1))
213     return(0);
214     /* add children (branches+leaves) */
215     if ((branched[0] = build_rbfrep(arp, np, x0, xmid, y0, ymid)) < 0)
216     return(-1);
217     if ((branched[1] = build_rbfrep(arp, np, xmid, x1, y0, ymid)) < 0)
218     return(-1);
219     if ((branched[2] = build_rbfrep(arp, np, x0, xmid, ymid, y1)) < 0)
220     return(-1);
221     if ((branched[3] = build_rbfrep(arp, np, xmid, x1, ymid, y1)) < 0)
222     return(-1);
223     nadded = branched[0] + branched[1] + branched[2] + branched[3];
224     nleaves = !branched[0] + !branched[1] + !branched[2] + !branched[3];
225     if (!nleaves) /* nothing but branches? */
226     return(nadded);
227     /* combine 4 leaves into 1? */
228 greg 2.14 if ((nleaves == 4) & (x1-x0 <= MAX_RAD) &&
229     smooth_region(x0, x1, y0, y1))
230 greg 2.12 return(0);
231     /* need more array space? */
232     if ((*np+nleaves-1)>>RBFALLOCB != (*np-1)>>RBFALLOCB) {
233     *arp = (RBFVAL *)realloc(*arp,
234     sizeof(RBFVAL)*(*np+nleaves-1+(1<<RBFALLOCB)));
235     if (*arp == NULL)
236     return(-1);
237 greg 2.6 }
238 greg 2.12 /* create lobes for leaves */
239 greg 2.21 if (!branched[0] && create_lobe(*arp+*np, x0, xmid, y0, ymid)) {
240     ++(*np); ++nadded;
241     }
242     if (!branched[1] && create_lobe(*arp+*np, xmid, x1, y0, ymid)) {
243     ++(*np); ++nadded;
244     }
245     if (!branched[2] && create_lobe(*arp+*np, x0, xmid, ymid, y1)) {
246     ++(*np); ++nadded;
247     }
248     if (!branched[3] && create_lobe(*arp+*np, xmid, x1, ymid, y1)) {
249     ++(*np); ++nadded;
250     }
251 greg 2.12 return(nadded);
252 greg 2.6 }
253    
254 greg 2.1 /* Count up filled nodes and build RBF representation from current grid */
255     RBFNODE *
256 greg 2.12 make_rbfrep()
257 greg 2.1 {
258 greg 2.12 RBFNODE *newnode;
259     RBFVAL *rbfarr;
260 greg 2.1 int nn;
261 greg 2.5 /* compute minimum BSDF */
262     comp_bsdf_min();
263 greg 2.12 /* create RBF node list */
264     rbfarr = NULL; nn = 0;
265 greg 2.22 if (build_rbfrep(&rbfarr, &nn, 0, GRIDRES, 0, GRIDRES) <= 0) {
266     if (nn)
267     goto memerr;
268 greg 2.24 fprintf(stderr,
269     "%s: warning - skipping bad incidence (%.1f,%.1f)\n",
270     progname, theta_in_deg, phi_in_deg);
271     return(NULL);
272 greg 2.22 }
273 greg 2.12 /* (re)allocate RBF array */
274     newnode = (RBFNODE *)realloc(rbfarr,
275     sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
276 greg 2.2 if (newnode == NULL)
277     goto memerr;
278 greg 2.12 /* copy computed lobes into RBF node */
279     memmove(newnode->rbfa, newnode, sizeof(RBFVAL)*nn);
280 greg 2.1 newnode->ord = -1;
281     newnode->next = NULL;
282     newnode->ejl = NULL;
283     newnode->invec[2] = sin((M_PI/180.)*theta_in_deg);
284     newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2];
285     newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2];
286     newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
287 greg 2.12 newnode->vtotal = .0;
288 greg 2.1 newnode->nrbf = nn;
289 greg 2.12 /* compute sum for normalization */
290     while (nn-- > 0)
291     newnode->vtotal += rbf_volume(&newnode->rbfa[nn]);
292 greg 2.3 #ifdef DEBUG
293 greg 2.12 fprintf(stderr, "Built RBF with %d lobes\n", newnode->nrbf);
294 greg 2.3 fprintf(stderr, "Integrated DSF at (%.1f,%.1f) deg. is %.2f\n",
295     get_theta180(newnode->invec), get_phi360(newnode->invec),
296     newnode->vtotal);
297     #endif
298 greg 2.1 insert_dsf(newnode);
299     return(newnode);
300 greg 2.2 memerr:
301     fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
302     exit(1);
303 greg 2.1 }