--- ray/src/cv/bsdfrbf.c 2013/10/21 18:33:15 2.12 +++ ray/src/cv/bsdfrbf.c 2014/03/21 00:42:46 2.23 @@ -1,5 +1,5 @@ #ifndef lint -static const char RCSid[] = "$Id: bsdfrbf.c,v 2.12 2013/10/21 18:33:15 greg Exp $"; +static const char RCSid[] = "$Id: bsdfrbf.c,v 2.23 2014/03/21 00:42:46 greg Exp $"; #endif /* * Radial basis function representation for BSDF data. @@ -7,6 +7,20 @@ static const char RCSid[] = "$Id: bsdfrbf.c,v 2.12 201 * G. Ward */ +/**************************************************************** +1) Collect samples into a grid using the Shirley-Chiu + angular mapping from a hemisphere to a square. + +2) Compute an adaptive quadtree by subdividing the grid so that + each leaf node has at least one sample up to as many + samples as fit nicely on a plane to within a certain + MSE tolerance. + +3) Place one Gaussian lobe at each leaf node in the quadtree, + sizing it to have a radius equal to the leaf size and + a volume equal to the energy in that node. +*****************************************************************/ + #define _USE_MATH_DEFINES #include #include @@ -21,13 +35,13 @@ static const char RCSid[] = "$Id: bsdfrbf.c,v 2.12 201 #define SMOOTH_MSE 5e-5 /* acceptable mean squared error */ #endif #ifndef SMOOTH_MSER -#define SMOOTH_MSER 0.07 /* acceptable relative MSE */ +#define SMOOTH_MSER 0.03 /* acceptable relative MSE */ #endif #define MAX_RAD (GRIDRES/8) /* maximum lobe radius */ #define RBFALLOCB 10 /* RBF allocation block size */ - /* our loaded grid for this incident angle */ + /* our loaded grid or comparison DSFs */ GRIDVAL dsf_grid[GRIDRES][GRIDRES]; /* Start new DSF input grid */ @@ -58,9 +72,7 @@ add_bsdf_data(double theta_out, double phi_out, double ovec[1] = sin((M_PI/180.)*phi_out) * ovec[2]; ovec[2] = sqrt(1. - ovec[2]*ovec[2]); - if (val <= 0) /* truncate to zero */ - val = 0; - else if (!isDSF) + if (!isDSF) val *= ovec[2]; /* convert from BSDF to DSF */ /* update BSDF histogram */ @@ -69,16 +81,16 @@ add_bsdf_data(double theta_out, double phi_out, double pos_from_vec(pos, ovec); - dsf_grid[pos[0]][pos[1]].vsum += val; - dsf_grid[pos[0]][pos[1]].nval++; + dsf_grid[pos[0]][pos[1]].sum.v += val; + dsf_grid[pos[0]][pos[1]].sum.n++; } /* Compute minimum BSDF from histogram (does not clear) */ static void comp_bsdf_min() { - int cnt; - int i, target; + unsigned long cnt, target; + int i; cnt = 0; for (i = HISTLEN; i--; ) @@ -102,7 +114,7 @@ empty_region(int x0, int x1, int y0, int y1) for (x = x0; x < x1; x++) for (y = y0; y < y1; y++) - if (dsf_grid[x][y].nval) + if (dsf_grid[x][y].sum.n) return(0); return(1); } @@ -120,41 +132,42 @@ smooth_region(int x0, int x1, int y0, int y1) memset(xvec, 0, sizeof(xvec)); for (x = x0; x < x1; x++) for (y = y0; y < y1; y++) - if ((n = dsf_grid[x][y].nval) > 0) { - double z = dsf_grid[x][y].vsum; - rMtx[0][0] += n*x*x; - rMtx[0][1] += n*x*y; - rMtx[0][2] += n*x; - rMtx[1][1] += n*y*y; - rMtx[1][2] += n*y; - rMtx[2][2] += n; + if ((n = dsf_grid[x][y].sum.n) > 0) { + double z = dsf_grid[x][y].sum.v; + rMtx[0][0] += x*x*(double)n; + rMtx[0][1] += x*y*(double)n; + rMtx[0][2] += x*(double)n; + rMtx[1][1] += y*y*(double)n; + rMtx[1][2] += y*(double)n; + rMtx[2][2] += (double)n; xvec[0] += x*z; xvec[1] += y*z; xvec[2] += z; } rMtx[1][0] = rMtx[0][1]; + rMtx[2][0] = rMtx[0][2]; rMtx[2][1] = rMtx[1][2]; nvs = rMtx[2][2]; if (SDinvXform(rMtx, rMtx) != SDEnone) - return(0); + return(1); /* colinear values */ A = DOT(rMtx[0], xvec); B = DOT(rMtx[1], xvec); C = DOT(rMtx[2], xvec); sqerr = 0.0; /* compute mean squared error */ for (x = x0; x < x1; x++) for (y = y0; y < y1; y++) - if ((n = dsf_grid[x][y].nval) > 0) { - double d = A*x + B*y + C - dsf_grid[x][y].vsum/n; + if ((n = dsf_grid[x][y].sum.n) > 0) { + double d = A*x + B*y + C - dsf_grid[x][y].sum.v/n; sqerr += n*d*d; } if (sqerr <= nvs*SMOOTH_MSE) /* below absolute MSE threshold? */ return(1); - /* below relative MSE threshold? */ + /* OR below relative MSE threshold? */ return(sqerr*nvs <= xvec[2]*xvec[2]*SMOOTH_MSER); } /* Create new lobe based on integrated samples in region */ -static void +static int create_lobe(RBFVAL *rvp, int x0, int x1, int y0, int y1) { double vtot = 0.0; @@ -164,14 +177,16 @@ create_lobe(RBFVAL *rvp, int x0, int x1, int y0, int y /* compute average for region */ for (x = x0; x < x1; x++) for (y = y0; y < y1; y++) { - vtot += dsf_grid[x][y].vsum; - nv += dsf_grid[x][y].nval; + vtot += dsf_grid[x][y].sum.v; + nv += dsf_grid[x][y].sum.n; } if (!nv) { fprintf(stderr, "%s: internal - missing samples in create_lobe\n", progname); exit(1); } + if (vtot <= 0) /* only create positive lobes */ + return(0); /* peak value based on integral */ vtot *= (x1-x0)*(y1-y0)*(2.*M_PI/GRIDRES/GRIDRES)/(double)nv; rad = (RSCA/(double)GRIDRES)*(x1-x0); @@ -179,6 +194,7 @@ create_lobe(RBFVAL *rvp, int x0, int x1, int y0, int y rvp->crad = ANG2R(rad); rvp->gx = (x0+x1)>>1; rvp->gy = (y0+y1)>>1; + return(1); } /* Recursive function to build radial basis function representation */ @@ -209,7 +225,8 @@ build_rbfrep(RBFVAL **arp, int *np, int x0, int x1, in if (!nleaves) /* nothing but branches? */ return(nadded); /* combine 4 leaves into 1? */ - if (nleaves == 4 && x1-x0 < MAX_RAD && smooth_region(x0, x1, y0, y1)) + if ((nleaves == 4) & (x1-x0 <= MAX_RAD) && + smooth_region(x0, x1, y0, y1)) return(0); /* need more array space? */ if ((*np+nleaves-1)>>RBFALLOCB != (*np-1)>>RBFALLOCB) { @@ -219,15 +236,18 @@ build_rbfrep(RBFVAL **arp, int *np, int x0, int x1, in return(-1); } /* create lobes for leaves */ - if (!branched[0]) - create_lobe(*arp+(*np)++, x0, xmid, y0, ymid); - if (!branched[1]) - create_lobe(*arp+(*np)++, xmid, x1, y0, ymid); - if (!branched[2]) - create_lobe(*arp+(*np)++, x0, xmid, ymid, y1); - if (!branched[3]) - create_lobe(*arp+(*np)++, xmid, x1, ymid, y1); - nadded += nleaves; + if (!branched[0] && create_lobe(*arp+*np, x0, xmid, y0, ymid)) { + ++(*np); ++nadded; + } + if (!branched[1] && create_lobe(*arp+*np, xmid, x1, y0, ymid)) { + ++(*np); ++nadded; + } + if (!branched[2] && create_lobe(*arp+*np, x0, xmid, ymid, y1)) { + ++(*np); ++nadded; + } + if (!branched[3] && create_lobe(*arp+*np, xmid, x1, ymid, y1)) { + ++(*np); ++nadded; + } return(nadded); } @@ -242,8 +262,13 @@ make_rbfrep() comp_bsdf_min(); /* create RBF node list */ rbfarr = NULL; nn = 0; - if (build_rbfrep(&rbfarr, &nn, 0, GRIDRES, 0, GRIDRES) <= 0) - goto memerr; + if (build_rbfrep(&rbfarr, &nn, 0, GRIDRES, 0, GRIDRES) <= 0) { + if (nn) + goto memerr; + fprintf(stderr, "%s: no usable data in make_rbfrep()\n", + progname); + exit(1); + } /* (re)allocate RBF array */ newnode = (RBFNODE *)realloc(rbfarr, sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));