/* Compute normalized distribution scattering functions for comparison */ static void compute_nDSFs(const RBFNODE *rbf0, const RBFNODE *rbf1) { const double nf0 = (GRIDRES*GRIDRES) / rbf0->vtotal; const double nf1 = (GRIDRES*GRIDRES) / rbf1->vtotal; int x, y; FVECT dv; for (x = GRIDRES; x--; ) for (y = GRIDRES; y--; ) { ovec_from_pos(dv, x, y); /* cube root (brightness) */ dsf_grid[x][y].val[0] = pow(nf0*eval_rbfrep(rbf0, dv), .3333); dsf_grid[x][y].val[1] = pow(nf1*eval_rbfrep(rbf1, dv), .3333); } }
/* Estimate single-lobe radius for DSF at the given outgoing angle */ static double est_DSFrad(const RBFNODE *rbf, const FVECT outvec) { const double rad_epsilon = 0.01; const double DSFtarget = 0.60653066 * eval_rbfrep(rbf,outvec) * COSF(outvec[2]); double inside_rad = rad_epsilon; double outside_rad = 0.5; double DSFinside = eval_DSFsurround(rbf, outvec, inside_rad); double DSFoutside = eval_DSFsurround(rbf, outvec, outside_rad); #define interp_rad inside_rad + (outside_rad-inside_rad) * \ (DSFtarget-DSFinside) / (DSFoutside-DSFinside) /* Newton's method (sort of) */ do { double test_rad = interp_rad; double DSFtest; if ((test_rad >= outside_rad) | (test_rad <= inside_rad)) test_rad = .5*(inside_rad + outside_rad); DSFtest = eval_DSFsurround(rbf, outvec, test_rad); if (DSFtest > DSFtarget) { inside_rad = test_rad; DSFinside = DSFtest; } else { outside_rad = test_rad; DSFoutside = DSFtest; } } while (outside_rad-inside_rad > rad_epsilon); return(.5*(inside_rad + outside_rad)); #undef interp_rad }
/* Compute average DSF value at the given radius from central vector */ static double eval_DSFsurround(const RBFNODE *rbf, const FVECT outvec, const double rad) { const int ninc = 12; const double phinc = 2.*M_PI/ninc; double sum = 0; int n = 0; FVECT tvec; int i; /* compute initial vector */ if (output_orient*outvec[2] >= 1.-FTINY) { tvec[0] = tvec[2] = 0; tvec[1] = 1; } else { tvec[0] = tvec[1] = 0; tvec[2] = 1; } geodesic(tvec, outvec, tvec, rad, GEOD_RAD); /* average surrounding DSF */ for (i = 0; i < ninc; i++) { if (i) spinvector(tvec, tvec, outvec, phinc); if (tvec[2] > 0 ^ output_orient > 0) continue; sum += eval_rbfrep(rbf, tvec) * COSF(tvec[2]); ++n; } if (n < 2) /* should never happen! */ return(sum); return(sum/(double)n); }
/* Conservative estimate of average BSDF value from current DSF's */ static void comp_bsdf_spec(void) { double vmod_sum = 0; double rad_sum = 0; int n = 0; double *cost_list = NULL; double max_cost = 1.; RBFNODE *rbf; FVECT sdv; /* sort by incident altitude */ for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) n++; if (n >= 10) cost_list = (double *)malloc(sizeof(double)*n); if (cost_list == NULL) { bsdf_spec_val = 0; bsdf_spec_rad = 0; return; } n = 0; for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) cost_list[n++] = rbf->invec[2]*input_orient; qsort(cost_list, n, sizeof(double), dbl_cmp); max_cost = cost_list[(n+3)/4]; /* accept 25% nearest grazing */ free(cost_list); n = 0; for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) { double this_rad, cosfact, vest; if (rbf->invec[2]*input_orient > max_cost) continue; sdv[0] = -rbf->invec[0]; sdv[1] = -rbf->invec[1]; sdv[2] = rbf->invec[2]*(2*(input_orient==output_orient) - 1); cosfact = COSF(sdv[2]); this_rad = est_DSFrad(rbf, sdv); vest = eval_rbfrep(rbf, sdv) * cosfact * (2.*M_PI) * this_rad*this_rad; if (vest > rbf->vtotal) /* don't over-estimate energy */ vest = rbf->vtotal; vmod_sum += vest / cosfact; /* remove cosine factor */ rad_sum += this_rad; ++n; } bsdf_spec_rad = rad_sum/(double)n; bsdf_spec_val = vmod_sum/(2.*M_PI*n*bsdf_spec_rad*bsdf_spec_rad); }
/* Interpolate and output a radial basis function BSDF representation */ static void eval_rbf(void) { ANGLE_BASIS *abp = get_basis(kbasis); float bsdfarr[MAXPATCHES*MAXPATCHES]; FVECT vin, vout; RBFNODE *rbf; double sum; int i, j, n; /* sanity check */ if (abp->nangles > MAXPATCHES) { fprintf(stderr, "%s: too many patches!\n", progname); exit(1); } data_prologue(); /* begin output */ for (i = 0; i < abp->nangles; i++) { if (input_orient > 0) /* use incident patch center */ fi_getvec(vin, i+.5*(i>0), abp); else bi_getvec(vin, i+.5*(i>0), abp); rbf = advect_rbf(vin); /* compute radial basis func */ for (j = 0; j < abp->nangles; j++) { sum = 0; /* sample over exiting patch */ for (n = npsamps; n--; ) { if (output_orient > 0) fo_getvec(vout, j+(n+frandom())/npsamps, abp); else bo_getvec(vout, j+(n+frandom())/npsamps, abp); sum += eval_rbfrep(rbf, vout) / vout[2]; } bsdfarr[j*abp->nangles + i] = sum*output_orient/npsamps; } if (rbf != NULL) free(rbf); } n = 0; /* write out our matrix */ for (j = 0; j < abp->nangles; j++) { for (i = 0; i < abp->nangles; i++) printf("\t%.3e\n", bsdfarr[n++]); putchar('\n'); } data_epilogue(); /* finish output */ }
/* Count up filled nodes and build RBF representation from current grid */ RBFNODE * make_rbfrep(void) { int niter = 16; double lastVar, thisVar = 100.; int nn; RBFNODE *newnode; RBFVAL *itera; int i, j; /* compute RBF radii */ compute_radii(); /* coagulate lobes */ cull_values(); nn = 0; /* count selected bins */ for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) nn += dsf_grid[i][j].nval; /* compute minimum BSDF */ comp_bsdf_min(); /* allocate RBF array */ newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1)); if (newnode == NULL) goto memerr; newnode->ord = -1; newnode->next = NULL; newnode->ejl = NULL; newnode->invec[2] = sin((M_PI/180.)*theta_in_deg); newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2]; newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2]; newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]); newnode->vtotal = 0; newnode->nrbf = nn; nn = 0; /* fill RBF array */ for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) if (dsf_grid[i][j].nval) { newnode->rbfa[nn].peak = dsf_grid[i][j].vsum; newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5; newnode->rbfa[nn].gx = i; newnode->rbfa[nn].gy = j; ++nn; } /* iterate to improve interpolation accuracy */ itera = (RBFVAL *)malloc(sizeof(RBFVAL)*newnode->nrbf); if (itera == NULL) goto memerr; memcpy(itera, newnode->rbfa, sizeof(RBFVAL)*newnode->nrbf); do { double dsum = 0, dsum2 = 0; nn = 0; for (i = 0; i < GRIDRES; i++) for (j = 0; j < GRIDRES; j++) if (dsf_grid[i][j].nval) { FVECT odir; double corr; ovec_from_pos(odir, i, j); itera[nn++].peak *= corr = dsf_grid[i][j].vsum / eval_rbfrep(newnode, odir); dsum += 1. - corr; dsum2 += (1.-corr)*(1.-corr); } memcpy(newnode->rbfa, itera, sizeof(RBFVAL)*newnode->nrbf); lastVar = thisVar; thisVar = dsum2/(double)nn; #ifdef DEBUG fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n", 100.*dsum/(double)nn, 100.*sqrt(thisVar)); #endif } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar); free(itera); nn = 0; /* compute sum for normalization */ while (nn < newnode->nrbf) newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]); #ifdef DEBUG fprintf(stderr, "Integrated DSF at (%.1f,%.1f) deg. is %.2f\n", get_theta180(newnode->invec), get_phi360(newnode->invec), newnode->vtotal); #endif insert_dsf(newnode); return(newnode); memerr: fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname); exit(1); }