ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.26
Committed: Fri Aug 22 05:38:44 2014 UTC (9 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad4R2P2, rad5R0, rad4R2P1
Changes since 2.25: +2 -2 lines
Log Message:
Set minimum cosine to 0.02 to avoid blowing-up values near grazing

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.25 2014/03/30 00:19:09 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.0 /* 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 /* loaded grid or comparison DSFs */
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 (!isDSF)
76 val *= COSF(ovec[2]); /* convert from BSDF to DSF */
77
78 /* update BSDF histogram */
79 if (val < BSDF2BIG*ovec[2] && val > BSDF2SML*ovec[2])
80 ++bsdf_hist[histndx(val/ovec[2])];
81
82 pos_from_vec(pos, ovec);
83
84 dsf_grid[pos[0]][pos[1]].sum.v += val;
85 dsf_grid[pos[0]][pos[1]].sum.n++;
86 }
87
88 /* Compute minimum BSDF from histogram (does not clear) */
89 static void
90 comp_bsdf_min()
91 {
92 unsigned long cnt, target;
93 int i;
94
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 /* Determine if the given region is empty of grid samples */
110 static int
111 empty_region(int x0, int x1, int y0, int y1)
112 {
113 int x, y;
114
115 for (x = x0; x < x1; x++)
116 for (y = y0; y < y1; y++)
117 if (dsf_grid[x][y].sum.n)
118 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 if ((n = dsf_grid[x][y].sum.n) > 0) {
136 double z = dsf_grid[x][y].sum.v;
137 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 xvec[0] += x*z;
144 xvec[1] += y*z;
145 xvec[2] += z;
146 }
147 rMtx[1][0] = rMtx[0][1];
148 rMtx[2][0] = rMtx[0][2];
149 rMtx[2][1] = rMtx[1][2];
150 nvs = rMtx[2][2];
151 if (SDinvXform(rMtx, rMtx) != SDEnone)
152 return(1); /* colinear values */
153 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 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 sqerr += n*d*d;
162 }
163 if (sqerr <= nvs*SMOOTH_MSE) /* below absolute MSE threshold? */
164 return(1);
165 /* OR below relative MSE threshold? */
166 return(sqerr*nvs <= xvec[2]*xvec[2]*SMOOTH_MSER);
167 }
168
169 /* Create new lobe based on integrated samples in region */
170 static int
171 create_lobe(RBFVAL *rvp, int x0, int x1, int y0, int y1)
172 {
173 double vtot = 0.0;
174 int nv = 0;
175 double wxsum = 0.0, wysum = 0.0, wtsum = 0.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 if (dsf_grid[x][y].sum.n) {
182 const double v = dsf_grid[x][y].sum.v;
183 const int n = dsf_grid[x][y].sum.n;
184
185 if (v > 0) {
186 double wt = v / (double)n;
187 wxsum += wt * x;
188 wysum += wt * y;
189 wtsum += wt;
190 }
191 vtot += v;
192 nv += n;
193 }
194 if (!nv) {
195 fprintf(stderr, "%s: internal - missing samples in create_lobe\n",
196 progname);
197 exit(1);
198 }
199 if (vtot <= 0) /* only create positive lobes */
200 return(0);
201 /* peak value based on integral */
202 vtot *= (x1-x0)*(y1-y0)*(2.*M_PI/GRIDRES/GRIDRES)/(double)nv;
203 rad = (RSCA/(double)GRIDRES)*(x1-x0);
204 rvp->peak = vtot / ((2.*M_PI) * rad*rad);
205 rvp->crad = ANG2R(rad); /* put peak at centroid */
206 rvp->gx = (int)(wxsum/wtsum + .5);
207 rvp->gy = (int)(wysum/wtsum + .5);
208 return(1);
209 }
210
211 /* Recursive function to build radial basis function representation */
212 static int
213 build_rbfrep(RBFVAL **arp, int *np, int x0, int x1, int y0, int y1)
214 {
215 int xmid = (x0+x1)>>1;
216 int ymid = (y0+y1)>>1;
217 int branched[4];
218 int nadded, nleaves;
219 /* need to make this a leaf? */
220 if (empty_region(x0, xmid, y0, ymid) ||
221 empty_region(xmid, x1, y0, ymid) ||
222 empty_region(x0, xmid, ymid, y1) ||
223 empty_region(xmid, x1, ymid, y1))
224 return(0);
225 /* add children (branches+leaves) */
226 if ((branched[0] = build_rbfrep(arp, np, x0, xmid, y0, ymid)) < 0)
227 return(-1);
228 if ((branched[1] = build_rbfrep(arp, np, xmid, x1, y0, ymid)) < 0)
229 return(-1);
230 if ((branched[2] = build_rbfrep(arp, np, x0, xmid, ymid, y1)) < 0)
231 return(-1);
232 if ((branched[3] = build_rbfrep(arp, np, xmid, x1, ymid, y1)) < 0)
233 return(-1);
234 nadded = branched[0] + branched[1] + branched[2] + branched[3];
235 nleaves = !branched[0] + !branched[1] + !branched[2] + !branched[3];
236 if (!nleaves) /* nothing but branches? */
237 return(nadded);
238 /* combine 4 leaves into 1? */
239 if ((nleaves == 4) & (x1-x0 <= MAX_RAD) &&
240 smooth_region(x0, x1, y0, y1))
241 return(0);
242 /* need more array space? */
243 if ((*np+nleaves-1)>>RBFALLOCB != (*np-1)>>RBFALLOCB) {
244 *arp = (RBFVAL *)realloc(*arp,
245 sizeof(RBFVAL)*(*np+nleaves-1+(1<<RBFALLOCB)));
246 if (*arp == NULL)
247 return(-1);
248 }
249 /* create lobes for leaves */
250 if (!branched[0] && create_lobe(*arp+*np, x0, xmid, y0, ymid)) {
251 ++(*np); ++nadded;
252 }
253 if (!branched[1] && create_lobe(*arp+*np, xmid, x1, y0, ymid)) {
254 ++(*np); ++nadded;
255 }
256 if (!branched[2] && create_lobe(*arp+*np, x0, xmid, ymid, y1)) {
257 ++(*np); ++nadded;
258 }
259 if (!branched[3] && create_lobe(*arp+*np, xmid, x1, ymid, y1)) {
260 ++(*np); ++nadded;
261 }
262 return(nadded);
263 }
264
265 /* Count up filled nodes and build RBF representation from current grid */
266 RBFNODE *
267 make_rbfrep()
268 {
269 RBFNODE *newnode;
270 RBFVAL *rbfarr;
271 int nn;
272 /* compute minimum BSDF */
273 comp_bsdf_min();
274 /* create RBF node list */
275 rbfarr = NULL; nn = 0;
276 if (build_rbfrep(&rbfarr, &nn, 0, GRIDRES, 0, GRIDRES) <= 0) {
277 if (nn)
278 goto memerr;
279 fprintf(stderr,
280 "%s: warning - skipping bad incidence (%.1f,%.1f)\n",
281 progname, theta_in_deg, phi_in_deg);
282 return(NULL);
283 }
284 /* (re)allocate RBF array */
285 newnode = (RBFNODE *)realloc(rbfarr,
286 sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
287 if (newnode == NULL)
288 goto memerr;
289 /* copy computed lobes into RBF node */
290 memmove(newnode->rbfa, newnode, sizeof(RBFVAL)*nn);
291 newnode->ord = -1;
292 newnode->next = NULL;
293 newnode->ejl = NULL;
294 newnode->invec[2] = sin((M_PI/180.)*theta_in_deg);
295 newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2];
296 newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2];
297 newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
298 newnode->vtotal = .0;
299 newnode->nrbf = nn;
300 /* compute sum for normalization */
301 while (nn-- > 0)
302 newnode->vtotal += rbf_volume(&newnode->rbfa[nn]);
303 #ifdef DEBUG
304 fprintf(stderr, "Built RBF with %d lobes\n", newnode->nrbf);
305 fprintf(stderr, "Integrated DSF at (%.1f,%.1f) deg. is %.2f\n",
306 get_theta180(newnode->invec), get_phi360(newnode->invec),
307 newnode->vtotal);
308 #endif
309 insert_dsf(newnode);
310 return(newnode);
311 memerr:
312 fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
313 exit(1);
314 }