ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.12
Committed: Mon Oct 21 18:33:15 2013 UTC (10 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.11: +154 -352 lines
Log Message:
Major overhaul/redesign of radial basis function derivation

File Contents

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