ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.1
Committed: Fri Oct 19 04:14:29 2012 UTC (11 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Broke pabopto2xml into pabopto2bsdf and bsdf2ttree

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id$";
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.7 /* radius scaling factor (empirical) */
19 #endif
20 /* our loaded grid for this incident angle */
21 GRIDVAL dsf_grid[GRIDRES][GRIDRES];
22
23 /* Start new DSF input grid */
24 void
25 new_bsdf_data(double new_theta, double new_phi)
26 {
27 if (!new_input_direction(new_theta, new_phi))
28 exit(1);
29 memset(dsf_grid, 0, sizeof(dsf_grid));
30 }
31
32 /* Add BSDF data point */
33 void
34 add_bsdf_data(double theta_out, double phi_out, double val, int isDSF)
35 {
36 FVECT ovec;
37 int pos[2];
38
39 if (!output_orient) /* check output orientation */
40 output_orient = 1 - 2*(theta_out > 90.);
41 else if (output_orient > 0 ^ theta_out < 90.) {
42 fputs("Cannot handle output angles on both sides of surface\n",
43 stderr);
44 exit(1);
45 }
46 ovec[2] = sin((M_PI/180.)*theta_out);
47 ovec[0] = cos((M_PI/180.)*phi_out) * ovec[2];
48 ovec[1] = sin((M_PI/180.)*phi_out) * ovec[2];
49 ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
50
51 if (!isDSF)
52 val *= ovec[2]; /* convert from BSDF to DSF */
53
54 pos_from_vec(pos, ovec);
55
56 dsf_grid[pos[0]][pos[1]].vsum += val;
57 dsf_grid[pos[0]][pos[1]].nval++;
58 }
59
60 /* Compute radii for non-empty bins */
61 /* (distance to furthest empty bin for which non-empty bin is the closest) */
62 static void
63 compute_radii(void)
64 {
65 unsigned int fill_grid[GRIDRES][GRIDRES];
66 unsigned short fill_cnt[GRIDRES][GRIDRES];
67 FVECT ovec0, ovec1;
68 double ang2, lastang2;
69 int r, i, j, jn, ii, jj, inear, jnear;
70
71 r = GRIDRES/2; /* proceed in zig-zag */
72 for (i = 0; i < GRIDRES; i++)
73 for (jn = 0; jn < GRIDRES; jn++) {
74 j = (i&1) ? jn : GRIDRES-1-jn;
75 if (dsf_grid[i][j].nval) /* find empty grid pos. */
76 continue;
77 ovec_from_pos(ovec0, i, j);
78 inear = jnear = -1; /* find nearest non-empty */
79 lastang2 = M_PI*M_PI;
80 for (ii = i-r; ii <= i+r; ii++) {
81 if (ii < 0) continue;
82 if (ii >= GRIDRES) break;
83 for (jj = j-r; jj <= j+r; jj++) {
84 if (jj < 0) continue;
85 if (jj >= GRIDRES) break;
86 if (!dsf_grid[ii][jj].nval)
87 continue;
88 ovec_from_pos(ovec1, ii, jj);
89 ang2 = 2. - 2.*DOT(ovec0,ovec1);
90 if (ang2 >= lastang2)
91 continue;
92 lastang2 = ang2;
93 inear = ii; jnear = jj;
94 }
95 }
96 if (inear < 0) {
97 fprintf(stderr,
98 "%s: Could not find non-empty neighbor!\n",
99 progname);
100 exit(1);
101 }
102 ang2 = sqrt(lastang2);
103 r = ANG2R(ang2); /* record if > previous */
104 if (r > dsf_grid[inear][jnear].crad)
105 dsf_grid[inear][jnear].crad = r;
106 /* next search radius */
107 r = ang2*(2.*GRIDRES/M_PI) + 3;
108 }
109 /* blur radii over hemisphere */
110 memset(fill_grid, 0, sizeof(fill_grid));
111 memset(fill_cnt, 0, sizeof(fill_cnt));
112 for (i = 0; i < GRIDRES; i++)
113 for (j = 0; j < GRIDRES; j++) {
114 if (!dsf_grid[i][j].crad)
115 continue; /* missing distance */
116 r = R2ANG(dsf_grid[i][j].crad)*(2.*RSCA*GRIDRES/M_PI);
117 for (ii = i-r; ii <= i+r; ii++) {
118 if (ii < 0) continue;
119 if (ii >= GRIDRES) break;
120 for (jj = j-r; jj <= j+r; jj++) {
121 if (jj < 0) continue;
122 if (jj >= GRIDRES) break;
123 if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r)
124 continue;
125 fill_grid[ii][jj] += dsf_grid[i][j].crad;
126 fill_cnt[ii][jj]++;
127 }
128 }
129 }
130 /* copy back blurred radii */
131 for (i = 0; i < GRIDRES; i++)
132 for (j = 0; j < GRIDRES; j++)
133 if (fill_cnt[i][j])
134 dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j];
135 }
136
137 /* Cull points for more uniform distribution, leave all nval 0 or 1 */
138 static void
139 cull_values(void)
140 {
141 FVECT ovec0, ovec1;
142 double maxang, maxang2;
143 int i, j, ii, jj, r;
144 /* simple greedy algorithm */
145 for (i = 0; i < GRIDRES; i++)
146 for (j = 0; j < GRIDRES; j++) {
147 if (!dsf_grid[i][j].nval)
148 continue;
149 if (!dsf_grid[i][j].crad)
150 continue; /* shouldn't happen */
151 ovec_from_pos(ovec0, i, j);
152 maxang = 2.*R2ANG(dsf_grid[i][j].crad);
153 if (maxang > ovec0[2]) /* clamp near horizon */
154 maxang = ovec0[2];
155 r = maxang*(2.*GRIDRES/M_PI) + 1;
156 maxang2 = maxang*maxang;
157 for (ii = i-r; ii <= i+r; ii++) {
158 if (ii < 0) continue;
159 if (ii >= GRIDRES) break;
160 for (jj = j-r; jj <= j+r; jj++) {
161 if (jj < 0) continue;
162 if (jj >= GRIDRES) break;
163 if (!dsf_grid[ii][jj].nval)
164 continue;
165 if ((ii == i) & (jj == j))
166 continue; /* don't get self-absorbed */
167 ovec_from_pos(ovec1, ii, jj);
168 if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
169 continue;
170 /* absorb sum */
171 dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum;
172 dsf_grid[i][j].nval += dsf_grid[ii][jj].nval;
173 /* keep value, though */
174 dsf_grid[ii][jj].vsum /= (float)dsf_grid[ii][jj].nval;
175 dsf_grid[ii][jj].nval = 0;
176 }
177 }
178 }
179 /* final averaging pass */
180 for (i = 0; i < GRIDRES; i++)
181 for (j = 0; j < GRIDRES; j++)
182 if (dsf_grid[i][j].nval > 1) {
183 dsf_grid[i][j].vsum /= (float)dsf_grid[i][j].nval;
184 dsf_grid[i][j].nval = 1;
185 }
186 }
187
188 /* Count up filled nodes and build RBF representation from current grid */
189 RBFNODE *
190 make_rbfrep(void)
191 {
192 int niter = 16;
193 double lastVar, thisVar = 100.;
194 int nn;
195 RBFNODE *newnode;
196 int i, j;
197 /* compute RBF radii */
198 compute_radii();
199 /* coagulate lobes */
200 cull_values();
201 nn = 0; /* count selected bins */
202 for (i = 0; i < GRIDRES; i++)
203 for (j = 0; j < GRIDRES; j++)
204 nn += dsf_grid[i][j].nval;
205 /* allocate RBF array */
206 newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
207 if (newnode == NULL) {
208 fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
209 exit(1);
210 }
211 newnode->ord = -1;
212 newnode->next = NULL;
213 newnode->ejl = NULL;
214 newnode->invec[2] = sin((M_PI/180.)*theta_in_deg);
215 newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2];
216 newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2];
217 newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
218 newnode->vtotal = 0;
219 newnode->nrbf = nn;
220 nn = 0; /* fill RBF array */
221 for (i = 0; i < GRIDRES; i++)
222 for (j = 0; j < GRIDRES; j++)
223 if (dsf_grid[i][j].nval) {
224 newnode->rbfa[nn].peak = dsf_grid[i][j].vsum;
225 newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5;
226 newnode->rbfa[nn].gx = i;
227 newnode->rbfa[nn].gy = j;
228 ++nn;
229 }
230 /* iterate to improve interpolation accuracy */
231 do {
232 double dsum = 0, dsum2 = 0;
233 nn = 0;
234 for (i = 0; i < GRIDRES; i++)
235 for (j = 0; j < GRIDRES; j++)
236 if (dsf_grid[i][j].nval) {
237 FVECT odir;
238 double corr;
239 ovec_from_pos(odir, i, j);
240 newnode->rbfa[nn++].peak *= corr =
241 dsf_grid[i][j].vsum /
242 eval_rbfrep(newnode, odir);
243 dsum += corr - 1.;
244 dsum2 += (corr-1.)*(corr-1.);
245 }
246 lastVar = thisVar;
247 thisVar = dsum2/(double)nn;
248 #ifdef DEBUG
249 fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n",
250 100.*dsum/(double)nn,
251 100.*sqrt(thisVar));
252 #endif
253 } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar);
254
255 nn = 0; /* compute sum for normalization */
256 while (nn < newnode->nrbf)
257 newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
258
259 insert_dsf(newnode);
260
261 return(newnode);
262 }