ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.19
Committed: Sun Mar 2 01:56:03 2014 UTC (10 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.18: +2 -2 lines
Log Message:
Went back to earlier absolute threshold

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.18 2014/02/18 16:42:16 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.03 /* 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 unsigned long cnt, target;
95 int i;
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][0] = rMtx[0][2];
151 rMtx[2][1] = rMtx[1][2];
152 nvs = rMtx[2][2];
153 if (SDinvXform(rMtx, rMtx) != SDEnone)
154 return(1); /* colinear values */
155 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 }
165 if (sqerr <= nvs*SMOOTH_MSE) /* below absolute MSE threshold? */
166 return(1);
167 /* OR below relative MSE threshold? */
168 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 }
190 /* 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 }
198
199 /* Recursive function to build radial basis function representation */
200 static int
201 build_rbfrep(RBFVAL **arp, int *np, int x0, int x1, int y0, int y1)
202 {
203 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 if ((nleaves == 4) & (x1-x0 <= MAX_RAD) &&
228 smooth_region(x0, x1, y0, y1))
229 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 }
237 /* 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 }
249
250 /* Count up filled nodes and build RBF representation from current grid */
251 RBFNODE *
252 make_rbfrep()
253 {
254 RBFNODE *newnode;
255 RBFVAL *rbfarr;
256 int nn;
257 /* compute minimum BSDF */
258 comp_bsdf_min();
259 /* 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 if (newnode == NULL)
267 goto memerr;
268 /* copy computed lobes into RBF node */
269 memmove(newnode->rbfa, newnode, sizeof(RBFVAL)*nn);
270 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 newnode->vtotal = .0;
278 newnode->nrbf = nn;
279 /* compute sum for normalization */
280 while (nn-- > 0)
281 newnode->vtotal += rbf_volume(&newnode->rbfa[nn]);
282 #ifdef DEBUG
283 fprintf(stderr, "Built RBF with %d lobes\n", newnode->nrbf);
284 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 insert_dsf(newnode);
289
290 return(newnode);
291 memerr:
292 fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
293 exit(1);
294 }