ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.13
Committed: Mon Oct 21 21:48:42 2013 UTC (10 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.12: +22 -8 lines
Log Message:
Fixed potential bug in smoothness criterion

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.12 2013/10/21 18:33:15 greg Exp $";
3 #endif
4 /*
5 * Radial basis function representation for BSDF data.
6 *
7 * G. Ward
8 */
9
10 /****************************************************************
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 #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 #ifndef RSCA
32 #define RSCA 2.2 /* radius scaling factor (empirical) */
33 #endif
34 #ifndef SMOOTH_MSE
35 #define SMOOTH_MSE 5e-5 /* acceptable mean squared error */
36 #endif
37 #ifndef SMOOTH_MSER
38 #define SMOOTH_MSER 0.07 /* acceptable relative MSE */
39 #endif
40 #define MAX_RAD (GRIDRES/8) /* maximum lobe radius */
41
42 #define RBFALLOCB 10 /* RBF allocation block size */
43
44 /* 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 if (val <= 0) /* truncate to zero */
76 val = 0;
77 else if (!isDSF)
78 val *= ovec[2]; /* convert from BSDF to DSF */
79
80 /* update BSDF histogram */
81 if (val < BSDF2BIG*ovec[2] && val > BSDF2SML*ovec[2])
82 ++bsdf_hist[histndx(val/ovec[2])];
83
84 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 /* Compute minimum BSDF from histogram (does not clear) */
91 static void
92 comp_bsdf_min()
93 {
94 int cnt;
95 int i, target;
96
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 /* Determine if the given region is empty of grid samples */
112 static int
113 empty_region(int x0, int x1, int y0, int y1)
114 {
115 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 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 xvec[0] += x*z;
146 xvec[1] += y*z;
147 xvec[2] += z;
148 }
149 rMtx[1][0] = rMtx[0][1];
150 rMtx[2][1] = rMtx[1][2];
151 nvs = rMtx[2][2];
152 if (SDinvXform(rMtx, rMtx) != SDEnone)
153 return(0);
154 A = DOT(rMtx[0], xvec);
155 B = DOT(rMtx[1], xvec);
156 C = DOT(rMtx[2], xvec);
157 sqerr = 0.0; /* compute mean squared error */
158 for (x = x0; x < x1; x++)
159 for (y = y0; y < y1; y++)
160 if ((n = dsf_grid[x][y].nval) > 0) {
161 double d = A*x + B*y + C - dsf_grid[x][y].vsum/n;
162 sqerr += n*d*d;
163 }
164 if (sqerr <= nvs*SMOOTH_MSE) /* below absolute MSE threshold? */
165 return(1);
166 /* OR below relative MSE threshold? */
167 return(sqerr*nvs <= xvec[2]*xvec[2]*SMOOTH_MSER);
168 }
169
170 /* Create new lobe based on integrated samples in region */
171 static void
172 create_lobe(RBFVAL *rvp, int x0, int x1, int y0, int y1)
173 {
174 double vtot = 0.0;
175 int nv = 0;
176 double rad;
177 int x, y;
178 /* compute average for region */
179 for (x = x0; x < x1; x++)
180 for (y = y0; y < y1; y++) {
181 vtot += dsf_grid[x][y].vsum;
182 nv += dsf_grid[x][y].nval;
183 }
184 if (!nv) {
185 fprintf(stderr, "%s: internal - missing samples in create_lobe\n",
186 progname);
187 exit(1);
188 }
189 /* peak value based on integral */
190 vtot *= (x1-x0)*(y1-y0)*(2.*M_PI/GRIDRES/GRIDRES)/(double)nv;
191 rad = (RSCA/(double)GRIDRES)*(x1-x0);
192 rvp->peak = vtot / ((2.*M_PI) * rad*rad);
193 rvp->crad = ANG2R(rad);
194 rvp->gx = (x0+x1)>>1;
195 rvp->gy = (y0+y1)>>1;
196 }
197
198 /* Recursive function to build radial basis function representation */
199 static int
200 build_rbfrep(RBFVAL **arp, int *np, int x0, int x1, int y0, int y1)
201 {
202 int xmid = (x0+x1)>>1;
203 int ymid = (y0+y1)>>1;
204 int branched[4];
205 int nadded, nleaves;
206 /* need to make this a leaf? */
207 if (empty_region(x0, xmid, y0, ymid) ||
208 empty_region(xmid, x1, y0, ymid) ||
209 empty_region(x0, xmid, ymid, y1) ||
210 empty_region(xmid, x1, ymid, y1))
211 return(0);
212 /* add children (branches+leaves) */
213 if ((branched[0] = build_rbfrep(arp, np, x0, xmid, y0, ymid)) < 0)
214 return(-1);
215 if ((branched[1] = build_rbfrep(arp, np, xmid, x1, y0, ymid)) < 0)
216 return(-1);
217 if ((branched[2] = build_rbfrep(arp, np, x0, xmid, ymid, y1)) < 0)
218 return(-1);
219 if ((branched[3] = build_rbfrep(arp, np, xmid, x1, ymid, y1)) < 0)
220 return(-1);
221 nadded = branched[0] + branched[1] + branched[2] + branched[3];
222 nleaves = !branched[0] + !branched[1] + !branched[2] + !branched[3];
223 if (!nleaves) /* nothing but branches? */
224 return(nadded);
225 /* combine 4 leaves into 1? */
226 if (nleaves == 4 && x1-x0 < MAX_RAD && smooth_region(x0, x1, y0, y1))
227 return(0);
228 /* need more array space? */
229 if ((*np+nleaves-1)>>RBFALLOCB != (*np-1)>>RBFALLOCB) {
230 *arp = (RBFVAL *)realloc(*arp,
231 sizeof(RBFVAL)*(*np+nleaves-1+(1<<RBFALLOCB)));
232 if (*arp == NULL)
233 return(-1);
234 }
235 /* create lobes for leaves */
236 if (!branched[0])
237 create_lobe(*arp+(*np)++, x0, xmid, y0, ymid);
238 if (!branched[1])
239 create_lobe(*arp+(*np)++, xmid, x1, y0, ymid);
240 if (!branched[2])
241 create_lobe(*arp+(*np)++, x0, xmid, ymid, y1);
242 if (!branched[3])
243 create_lobe(*arp+(*np)++, xmid, x1, ymid, y1);
244 nadded += nleaves;
245 return(nadded);
246 }
247
248 /* Count up filled nodes and build RBF representation from current grid */
249 RBFNODE *
250 make_rbfrep()
251 {
252 RBFNODE *newnode;
253 RBFVAL *rbfarr;
254 int nn;
255 /* compute minimum BSDF */
256 comp_bsdf_min();
257 /* create RBF node list */
258 rbfarr = NULL; nn = 0;
259 if (build_rbfrep(&rbfarr, &nn, 0, GRIDRES, 0, GRIDRES) <= 0)
260 goto memerr;
261 /* (re)allocate RBF array */
262 newnode = (RBFNODE *)realloc(rbfarr,
263 sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
264 if (newnode == NULL)
265 goto memerr;
266 /* copy computed lobes into RBF node */
267 memmove(newnode->rbfa, newnode, sizeof(RBFVAL)*nn);
268 newnode->ord = -1;
269 newnode->next = NULL;
270 newnode->ejl = NULL;
271 newnode->invec[2] = sin((M_PI/180.)*theta_in_deg);
272 newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2];
273 newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2];
274 newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
275 newnode->vtotal = .0;
276 newnode->nrbf = nn;
277 /* compute sum for normalization */
278 while (nn-- > 0)
279 newnode->vtotal += rbf_volume(&newnode->rbfa[nn]);
280 #ifdef DEBUG
281 fprintf(stderr, "Built RBF with %d lobes\n", newnode->nrbf);
282 fprintf(stderr, "Integrated DSF at (%.1f,%.1f) deg. is %.2f\n",
283 get_theta180(newnode->invec), get_phi360(newnode->invec),
284 newnode->vtotal);
285 #endif
286 insert_dsf(newnode);
287
288 return(newnode);
289 memerr:
290 fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
291 exit(1);
292 }