ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.5
Committed: Sat Aug 25 22:39:03 2012 UTC (11 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.4: +47 -47 lines
Log Message:
Switched from BSDF interpolation to DSF (BSDF*cos(theta_e)) interpolation

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.5 static const char RCSid[] = "$Id: pabopto2xml.c,v 2.4 2012/08/25 02:53:06 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Convert PAB-Opto measurements to XML format using tensor tree representation
6     * Employs Bonneel et al. Earth Mover's Distance interpolant.
7     *
8     * G.Ward
9     */
10    
11     #define _USE_MATH_DEFINES
12     #include <stdio.h>
13     #include <stdlib.h>
14     #include <string.h>
15     #include <ctype.h>
16     #include <math.h>
17     #include "bsdf.h"
18    
19     #ifndef GRIDRES
20     #define GRIDRES 200 /* max. grid resolution per side */
21     #endif
22    
23 greg 2.3 #define RSCA 2.7 /* radius scaling factor (empirical) */
24 greg 2.2
25     #define R2ANG(c) (((c)+.5)*(M_PI/(1<<16)))
26     #define ANG2R(r) (int)((r)*((1<<16)/M_PI))
27 greg 2.1
28     typedef struct {
29 greg 2.5 float vsum; /* DSF sum */
30 greg 2.1 unsigned short nval; /* number of values in sum */
31 greg 2.2 unsigned short crad; /* radius (coded angle) */
32 greg 2.1 } GRIDVAL; /* grid value */
33    
34     typedef struct {
35 greg 2.5 float peak; /* lobe value at peak */
36 greg 2.2 unsigned short crad; /* radius (coded angle) */
37 greg 2.1 unsigned char gx, gy; /* grid position */
38     } RBFVAL; /* radial basis function value */
39    
40     typedef struct s_rbflist {
41     struct s_rbflist *next; /* next in our RBF list */
42     FVECT invec; /* incident vector direction */
43     int nrbf; /* number of RBFs */
44     RBFVAL rbfa[1]; /* RBF array (extends struct) */
45 greg 2.5 } RBFLIST; /* RBF representation of DSF @ 1 incidence */
46 greg 2.1
47     /* our loaded grid for this incident angle */
48     static double theta_in_deg, phi_in_deg;
49 greg 2.5 static GRIDVAL dsf_grid[GRIDRES][GRIDRES];
50 greg 2.1
51 greg 2.5 /* processed incident DSF measurements */
52     static RBFLIST *dsf_list = NULL;
53 greg 2.1
54 greg 2.3 /* Compute outgoing vector from grid position */
55     static void
56     vec_from_pos(FVECT vec, int xpos, int ypos)
57 greg 2.1 {
58 greg 2.3 double uv[2];
59     double r2;
60    
61     SDsquare2disk(uv, (1./GRIDRES)*(xpos+.5), (1./GRIDRES)*(ypos+.5));
62     /* uniform hemispherical projection */
63     r2 = uv[0]*uv[0] + uv[1]*uv[1];
64     vec[0] = vec[1] = sqrt(2. - r2);
65     vec[0] *= uv[0];
66     vec[1] *= uv[1];
67     vec[2] = 1. - r2;
68 greg 2.1 }
69    
70     /* Compute grid position from normalized outgoing vector */
71     static void
72     pos_from_vec(int pos[2], const FVECT vec)
73     {
74     double sq[2]; /* uniform hemispherical projection */
75     double norm = 1./sqrt(1. + vec[2]);
76    
77     SDdisk2square(sq, vec[0]*norm, vec[1]*norm);
78    
79     pos[0] = (int)(sq[0]*GRIDRES);
80     pos[1] = (int)(sq[1]*GRIDRES);
81     }
82    
83 greg 2.5 /* Evaluate RBF for DSF at the given normalized outgoing direction */
84 greg 2.1 static double
85     eval_rbfrep(const RBFLIST *rp, const FVECT outvec)
86     {
87     double res = .0;
88     const RBFVAL *rbfp;
89     FVECT odir;
90     double sig2;
91     int n;
92    
93     rbfp = rp->rbfa;
94     for (n = rp->nrbf; n--; rbfp++) {
95     vec_from_pos(odir, rbfp->gx, rbfp->gy);
96 greg 2.2 sig2 = R2ANG(rbfp->crad);
97     sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2);
98 greg 2.1 if (sig2 > -19.)
99 greg 2.5 res += rbfp->peak * exp(sig2);
100 greg 2.1 }
101     return(res);
102     }
103    
104 greg 2.3 /* Count up filled nodes and build RBF representation from current grid */
105     static RBFLIST *
106     make_rbfrep(void)
107     {
108 greg 2.4 int niter = 6;
109 greg 2.3 int nn;
110     RBFLIST *newnode;
111     int i, j;
112    
113     nn = 0; /* count selected bins */
114     for (i = 0; i < GRIDRES; i++)
115     for (j = 0; j < GRIDRES; j++)
116 greg 2.5 nn += (dsf_grid[i][j].nval > 0);
117 greg 2.3 /* allocate RBF array */
118     newnode = (RBFLIST *)malloc(sizeof(RBFLIST) + sizeof(RBFVAL)*(nn-1));
119     if (newnode == NULL) {
120     fputs("Out of memory in make_rbfrep\n", stderr);
121     exit(1);
122     }
123     newnode->next = NULL;
124     newnode->invec[2] = sin(M_PI/180.*theta_in_deg);
125     newnode->invec[0] = cos(M_PI/180.*phi_in_deg)*newnode->invec[2];
126     newnode->invec[1] = sin(M_PI/180.*phi_in_deg)*newnode->invec[2];
127     newnode->invec[2] = sqrt(1. - newnode->invec[2]*newnode->invec[2]);
128     newnode->nrbf = nn;
129     nn = 0; /* fill RBF array */
130     for (i = 0; i < GRIDRES; i++)
131     for (j = 0; j < GRIDRES; j++)
132 greg 2.5 if (dsf_grid[i][j].nval) {
133     newnode->rbfa[nn].peak =
134     dsf_grid[i][j].vsum /=
135     (double)dsf_grid[i][j].nval;
136     dsf_grid[i][j].nval = 1;
137     newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5;
138 greg 2.3 newnode->rbfa[nn].gx = i;
139     newnode->rbfa[nn].gy = j;
140     ++nn;
141     }
142     /* iterate for better convergence */
143     while (niter--) {
144 greg 2.4 double dsum = .0, dsum2 = .0;
145 greg 2.3 nn = 0;
146     for (i = 0; i < GRIDRES; i++)
147     for (j = 0; j < GRIDRES; j++)
148 greg 2.5 if (dsf_grid[i][j].nval) {
149 greg 2.3 FVECT odir;
150 greg 2.4 /* double corr; */
151 greg 2.3 vec_from_pos(odir, i, j);
152 greg 2.5 newnode->rbfa[nn++].peak *= /* corr = */
153     dsf_grid[i][j].vsum /
154 greg 2.3 eval_rbfrep(newnode, odir);
155 greg 2.4 /*
156     dsum += corr - 1.;
157     dsum2 += (corr-1.)*(corr-1.);
158     */
159 greg 2.3 }
160 greg 2.4 /*
161     fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n",
162     100.*dsum/(double)nn,
163     100.*sqrt(dsum2/(double)nn));
164     */
165 greg 2.3 }
166 greg 2.5 newnode->next = dsf_list;
167     return(dsf_list = newnode);
168 greg 2.3 }
169    
170 greg 2.1 /* Load a set of measurements corresponding to a particular incident angle */
171     static int
172     load_bsdf_meas(const char *fname)
173     {
174     FILE *fp = fopen(fname, "r");
175     int inp_is_DSF = -1;
176     double theta_out, phi_out, val;
177     char buf[2048];
178     int n, c;
179    
180     if (fp == NULL) {
181     fputs(fname, stderr);
182     fputs(": cannot open\n", stderr);
183     return(0);
184     }
185 greg 2.5 memset(dsf_grid, 0, sizeof(dsf_grid));
186 greg 2.1 /* read header information */
187     while ((c = getc(fp)) == '#' || c == EOF) {
188     if (fgets(buf, sizeof(buf), fp) == NULL) {
189     fputs(fname, stderr);
190     fputs(": unexpected EOF\n", stderr);
191     fclose(fp);
192     return(0);
193     }
194     if (!strcmp(buf, "format: theta phi DSF\n")) {
195     inp_is_DSF = 1;
196     continue;
197     }
198     if (!strcmp(buf, "format: theta phi BSDF\n")) {
199     inp_is_DSF = 0;
200     continue;
201     }
202     if (sscanf(buf, "intheta %lf", &theta_in_deg) == 1)
203     continue;
204     if (sscanf(buf, "inphi %lf", &phi_in_deg) == 1)
205     continue;
206     if (sscanf(buf, "incident_angle %lf %lf",
207     &theta_in_deg, &phi_in_deg) == 2)
208     continue;
209     }
210     if (inp_is_DSF < 0) {
211     fputs(fname, stderr);
212     fputs(": unknown format\n", stderr);
213     fclose(fp);
214     return(0);
215     }
216     ungetc(c, fp); /* read actual data */
217     while (fscanf(fp, "%lf %lf %lf\n", &theta_out, &phi_out, &val) == 3) {
218     FVECT ovec;
219     int pos[2];
220    
221     ovec[2] = sin(M_PI/180.*theta_out);
222     ovec[0] = cos(M_PI/180.*phi_out) * ovec[2];
223     ovec[1] = sin(M_PI/180.*phi_out) * ovec[2];
224     ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
225    
226 greg 2.5 if (!inp_is_DSF)
227     val *= ovec[2]; /* convert from BSDF to DSF */
228 greg 2.1
229     pos_from_vec(pos, ovec);
230    
231 greg 2.5 dsf_grid[pos[0]][pos[1]].vsum += val;
232     dsf_grid[pos[0]][pos[1]].nval++;
233 greg 2.1 }
234     n = 0;
235     while ((c = getc(fp)) != EOF)
236     n += !isspace(c);
237     if (n)
238     fprintf(stderr,
239     "%s: warning: %d unexpected characters past EOD\n",
240     fname, n);
241     fclose(fp);
242     return(1);
243     }
244    
245     /* Compute radii for non-empty bins */
246     /* (distance to furthest empty bin for which non-empty bin is the closest) */
247     static void
248     compute_radii(void)
249     {
250 greg 2.4 unsigned int fill_grid[GRIDRES][GRIDRES];
251     unsigned short fill_cnt[GRIDRES][GRIDRES];
252 greg 2.2 FVECT ovec0, ovec1;
253     double ang2, lastang2;
254     int r, i, j, jn, ii, jj, inear, jnear;
255    
256     r = GRIDRES/2; /* proceed in zig-zag */
257 greg 2.1 for (i = 0; i < GRIDRES; i++)
258     for (jn = 0; jn < GRIDRES; jn++) {
259     j = (i&1) ? jn : GRIDRES-1-jn;
260 greg 2.5 if (dsf_grid[i][j].nval) /* find empty grid pos. */
261 greg 2.1 continue;
262 greg 2.2 vec_from_pos(ovec0, i, j);
263 greg 2.1 inear = jnear = -1; /* find nearest non-empty */
264 greg 2.2 lastang2 = M_PI*M_PI;
265 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
266     if (ii < 0) continue;
267     if (ii >= GRIDRES) break;
268     for (jj = j-r; jj <= j+r; jj++) {
269     if (jj < 0) continue;
270     if (jj >= GRIDRES) break;
271 greg 2.5 if (!dsf_grid[ii][jj].nval)
272 greg 2.1 continue;
273 greg 2.2 vec_from_pos(ovec1, ii, jj);
274     ang2 = 2. - 2.*DOT(ovec0,ovec1);
275     if (ang2 >= lastang2)
276 greg 2.1 continue;
277 greg 2.2 lastang2 = ang2;
278 greg 2.1 inear = ii; jnear = jj;
279     }
280     }
281 greg 2.2 if (inear < 0) {
282     fputs("Could not find non-empty neighbor!\n", stderr);
283     exit(1);
284     }
285     ang2 = sqrt(lastang2);
286     r = ANG2R(ang2); /* record if > previous */
287 greg 2.5 if (r > dsf_grid[inear][jnear].crad)
288     dsf_grid[inear][jnear].crad = r;
289 greg 2.2 /* next search radius */
290     r = ang2*(2.*GRIDRES/M_PI) + 1;
291 greg 2.1 }
292 greg 2.4 /* blur radii over hemisphere */
293 greg 2.1 memset(fill_grid, 0, sizeof(fill_grid));
294 greg 2.4 memset(fill_cnt, 0, sizeof(fill_cnt));
295 greg 2.1 for (i = 0; i < GRIDRES; i++)
296     for (j = 0; j < GRIDRES; j++) {
297 greg 2.5 if (!dsf_grid[i][j].crad)
298 greg 2.4 continue; /* missing distance */
299 greg 2.5 r = R2ANG(dsf_grid[i][j].crad)*(2.*RSCA*GRIDRES/M_PI);
300 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
301     if (ii < 0) continue;
302     if (ii >= GRIDRES) break;
303     for (jj = j-r; jj <= j+r; jj++) {
304     if (jj < 0) continue;
305     if (jj >= GRIDRES) break;
306 greg 2.4 if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r)
307 greg 2.1 continue;
308 greg 2.5 fill_grid[ii][jj] += dsf_grid[i][j].crad;
309 greg 2.4 fill_cnt[ii][jj]++;
310 greg 2.1 }
311     }
312     }
313 greg 2.4 /* copy back averaged radii */
314 greg 2.1 for (i = 0; i < GRIDRES; i++)
315     for (j = 0; j < GRIDRES; j++)
316 greg 2.4 if (fill_cnt[i][j])
317 greg 2.5 dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j];
318 greg 2.1 }
319    
320     /* Cull points for more uniform distribution */
321     static void
322     cull_values(void)
323     {
324 greg 2.2 FVECT ovec0, ovec1;
325     double maxang, maxang2;
326     int i, j, ii, jj, r;
327 greg 2.1 /* simple greedy algorithm */
328     for (i = 0; i < GRIDRES; i++)
329     for (j = 0; j < GRIDRES; j++) {
330 greg 2.5 if (!dsf_grid[i][j].nval)
331 greg 2.1 continue;
332 greg 2.5 if (!dsf_grid[i][j].crad)
333 greg 2.2 continue; /* shouldn't happen */
334     vec_from_pos(ovec0, i, j);
335 greg 2.5 maxang = 2.*R2ANG(dsf_grid[i][j].crad);
336 greg 2.2 if (maxang > ovec0[2]) /* clamp near horizon */
337     maxang = ovec0[2];
338     r = maxang*(2.*GRIDRES/M_PI) + 1;
339     maxang2 = maxang*maxang;
340 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
341     if (ii < 0) continue;
342     if (ii >= GRIDRES) break;
343     for (jj = j-r; jj <= j+r; jj++) {
344     if (jj < 0) continue;
345     if (jj >= GRIDRES) break;
346 greg 2.5 if (!dsf_grid[ii][jj].nval)
347 greg 2.1 continue;
348 greg 2.2 if ((ii == i) & (jj == j))
349     continue; /* don't get self-absorbed */
350     vec_from_pos(ovec1, ii, jj);
351     if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
352 greg 2.1 continue;
353 greg 2.2 /* absorb sum */
354 greg 2.5 dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum;
355     dsf_grid[i][j].nval += dsf_grid[ii][jj].nval;
356 greg 2.2 /* keep value, though */
357 greg 2.5 dsf_grid[ii][jj].vsum /= (double)dsf_grid[ii][jj].nval;
358     dsf_grid[ii][jj].nval = 0;
359 greg 2.1 }
360     }
361     }
362     }
363    
364    
365     #if 1
366     /* Test main produces a Radiance model from the given input file */
367     int
368     main(int argc, char *argv[])
369     {
370     char buf[128];
371     FILE *pfp;
372     double bsdf;
373     FVECT dir;
374     int i, j, n;
375    
376     if (argc != 2) {
377     fprintf(stderr, "Usage: %s input.dat > output.rad\n", argv[0]);
378     return(1);
379     }
380     if (!load_bsdf_meas(argv[1]))
381     return(1);
382    
383     compute_radii();
384     cull_values();
385 greg 2.3 make_rbfrep();
386     /* produce spheres at meas. */
387     puts("void plastic yellow\n0\n0\n5 .6 .4 .01 .04 .08\n");
388 greg 2.1 puts("void plastic pink\n0\n0\n5 .5 .05 .9 .04 .08\n");
389     n = 0;
390     for (i = 0; i < GRIDRES; i++)
391     for (j = 0; j < GRIDRES; j++)
392 greg 2.5 if (dsf_grid[i][j].vsum > .0f) {
393 greg 2.1 vec_from_pos(dir, i, j);
394 greg 2.5 bsdf = dsf_grid[i][j].vsum / dir[2];
395     if (dsf_grid[i][j].nval) {
396 greg 2.3 printf("pink cone c%04d\n0\n0\n8\n", ++n);
397     printf("\t%.6g %.6g %.6g\n",
398 greg 2.1 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
399 greg 2.3 printf("\t%.6g %.6g %.6g\n",
400 greg 2.1 dir[0]*(bsdf+.005), dir[1]*(bsdf+.005),
401     dir[2]*(bsdf+.005));
402 greg 2.3 puts("\t.003\t0\n");
403     } else {
404     vec_from_pos(dir, i, j);
405     printf("yellow sphere s%04d\n0\n0\n", ++n);
406     printf("4 %.6g %.6g %.6g .0015\n\n",
407     dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
408     }
409 greg 2.1 }
410     /* output continuous surface */
411     puts("void trans tgreen\n0\n0\n7 .7 1 .7 .04 .04 .9 .9\n");
412     fflush(stdout);
413 greg 2.5 sprintf(buf, "gensurf tgreen bsdf - - - %d %d", GRIDRES-1, GRIDRES-1);
414 greg 2.1 pfp = popen(buf, "w");
415     if (pfp == NULL) {
416     fputs(buf, stderr);
417     fputs(": cannot start command\n", stderr);
418     return(1);
419     }
420     for (i = 0; i < GRIDRES; i++)
421     for (j = 0; j < GRIDRES; j++) {
422     vec_from_pos(dir, i, j);
423 greg 2.5 bsdf = eval_rbfrep(dsf_list, dir) / dir[2];
424 greg 2.1 fprintf(pfp, "%.8e %.8e %.8e\n",
425     dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
426     }
427     return(pclose(pfp)==0 ? 0 : 1);
428     }
429     #endif