--- ray/src/rt/ambcomp.c 2014/05/09 22:53:11 2.57 +++ ray/src/rt/ambcomp.c 2014/09/04 09:09:08 2.66 @@ -1,5 +1,5 @@ #ifndef lint -static const char RCSid[] = "$Id: ambcomp.c,v 2.57 2014/05/09 22:53:11 greg Exp $"; +static const char RCSid[] = "$Id: ambcomp.c,v 2.66 2014/09/04 09:09:08 greg Exp $"; #endif /* * Routines to compute "ambient" values using Monte Carlo @@ -21,7 +21,7 @@ static const char RCSid[] = "$Id: ambcomp.c,v 2.57 201 #include "ambient.h" #include "random.h" -#ifdef NEWAMB +#ifndef OLDAMB extern void SDsquare2disk(double ds[2], double seedx, double seedy); @@ -33,9 +33,11 @@ typedef struct { typedef struct { RAY *rp; /* originating ray sample */ - FVECT ux, uy; /* tangent axis unit vectors */ int ns; /* number of samples per axis */ + int sampOK; /* acquired full sample set? */ COLOR acoef; /* division contribution coefficient */ + double acol[3]; /* accumulated color */ + FVECT ux, uy; /* tangent axis unit vectors */ AMBSAMP sa[1]; /* sample array (extends struct) */ } AMBHEMI; /* ambient sample hemisphere */ @@ -48,74 +50,37 @@ typedef struct { } FFTRI; /* vectors and coefficients for Hessian calculation */ -static AMBHEMI * -inithemi( /* initialize sampling hemisphere */ - COLOR ac, - RAY *r, - double wt +static int +ambsample( /* initial ambient division sample */ + AMBHEMI *hp, + int i, + int j, + int n ) { - AMBHEMI *hp; - double d; - int n, i; - /* set number of divisions */ - if (ambacc <= FTINY && - wt > (d = 0.8*intens(ac)*r->rweight/(ambdiv*minweight))) - wt = d; /* avoid ray termination */ - n = sqrt(ambdiv * wt) + 0.5; - i = 1 + 5*(ambacc > FTINY); /* minimum number of samples */ - if (n < i) - n = i; - /* allocate sampling array */ - hp = (AMBHEMI *)malloc(sizeof(AMBHEMI) + sizeof(AMBSAMP)*(n*n - 1)); - if (hp == NULL) - return(NULL); - hp->rp = r; - hp->ns = n; - /* assign coefficient */ - copycolor(hp->acoef, ac); - d = 1.0/(n*n); - scalecolor(hp->acoef, d); - /* make tangent plane axes */ - hp->uy[0] = 0.5 - frandom(); - hp->uy[1] = 0.5 - frandom(); - hp->uy[2] = 0.5 - frandom(); - for (i = 3; i--; ) - if ((-0.6 < r->ron[i]) & (r->ron[i] < 0.6)) - break; - if (i < 0) - error(CONSISTENCY, "bad ray direction in inithemi"); - hp->uy[i] = 1.0; - VCROSS(hp->ux, hp->uy, r->ron); - normalize(hp->ux); - VCROSS(hp->uy, r->ron, hp->ux); - /* we're ready to sample */ - return(hp); -} - - -/* Sample ambient division and apply weighting coefficient */ -static int -getambsamp(RAY *arp, AMBHEMI *hp, int i, int j, int n) -{ + AMBSAMP *ap = &ambsam(hp,i,j); + RAY ar; int hlist[3], ii; double spt[2], zd; + /* generate hemispherical sample */ /* ambient coefficient for weight */ if (ambacc > FTINY) - setcolor(arp->rcoef, AVGREFL, AVGREFL, AVGREFL); + setcolor(ar.rcoef, AVGREFL, AVGREFL, AVGREFL); else - copycolor(arp->rcoef, hp->acoef); - if (rayorigin(arp, AMBIENT, hp->rp, arp->rcoef) < 0) + copycolor(ar.rcoef, hp->acoef); + if (rayorigin(&ar, AMBIENT, hp->rp, ar.rcoef) < 0) return(0); if (ambacc > FTINY) { - multcolor(arp->rcoef, hp->acoef); - scalecolor(arp->rcoef, 1./AVGREFL); + multcolor(ar.rcoef, hp->acoef); + scalecolor(ar.rcoef, 1./AVGREFL); } hlist[0] = hp->rp->rno; hlist[1] = j; hlist[2] = i; multisamp(spt, 2, urand(ilhash(hlist,3)+n)); - if (!n) { /* avoid border samples for n==0 */ + /* avoid coincident samples */ + if (!n && (0 < i) & (i < hp->ns-1) && + (0 < j) & (j < hp->ns-1)) { if ((spt[0] < 0.1) | (spt[0] >= 0.9)) spt[0] = 0.1 + 0.8*frandom(); if ((spt[1] < 0.1) | (spt[1] >= 0.9)) @@ -124,41 +89,38 @@ getambsamp(RAY *arp, AMBHEMI *hp, int i, int j, int n) SDsquare2disk(spt, (j+spt[1])/hp->ns, (i+spt[0])/hp->ns); zd = sqrt(1. - spt[0]*spt[0] - spt[1]*spt[1]); for (ii = 3; ii--; ) - arp->rdir[ii] = spt[0]*hp->ux[ii] + + ar.rdir[ii] = spt[0]*hp->ux[ii] + spt[1]*hp->uy[ii] + zd*hp->rp->ron[ii]; - checknorm(arp->rdir); + checknorm(ar.rdir); dimlist[ndims++] = AI(hp,i,j) + 90171; - rayvalue(arp); /* evaluate ray */ - ndims--; /* apply coefficient */ - multcolor(arp->rcol, arp->rcoef); + rayvalue(&ar); /* evaluate ray */ + ndims--; + if (ar.rt <= FTINY) + return(0); /* should never happen */ + multcolor(ar.rcol, ar.rcoef); /* apply coefficient */ + if (ar.rt*ap->d < 1.0) /* new/closer distance? */ + ap->d = 1.0/ar.rt; + if (!n) { /* record first vertex & value */ + if (ar.rt > 10.0*thescene.cusize) + ar.rt = 10.0*thescene.cusize; + VSUM(ap->p, ar.rorg, ar.rdir, ar.rt); + copycolor(ap->v, ar.rcol); + } else { /* else update recorded value */ + hp->acol[RED] -= colval(ap->v,RED); + hp->acol[GRN] -= colval(ap->v,GRN); + hp->acol[BLU] -= colval(ap->v,BLU); + zd = 1.0/(double)(n+1); + scalecolor(ar.rcol, zd); + zd *= (double)n; + scalecolor(ap->v, zd); + addcolor(ap->v, ar.rcol); + } + addcolor(hp->acol, ap->v); /* add to our sum */ return(1); } -static AMBSAMP * -ambsample( /* initial ambient division sample */ - AMBHEMI *hp, - int i, - int j -) -{ - AMBSAMP *ap = &ambsam(hp,i,j); - RAY ar; - /* generate hemispherical sample */ - if (!getambsamp(&ar, hp, i, j, 0) || ar.rt <= FTINY) { - memset(ap, 0, sizeof(AMBSAMP)); - return(NULL); - } - ap->d = 1.0/ar.rt; /* limit vertex distance */ - if (ar.rt > 10.0*thescene.cusize) - ar.rt = 10.0*thescene.cusize; - VSUM(ap->p, ar.rorg, ar.rdir, ar.rt); - copycolor(ap->v, ar.rcol); - return(ap); -} - - /* Estimate errors based on ambient division differences */ static float * getambdiffs(AMBHEMI *hp) @@ -213,13 +175,12 @@ getambdiffs(AMBHEMI *hp) /* Perform super-sampling on hemisphere (introduces bias) */ static void -ambsupersamp(double acol[3], AMBHEMI *hp, int cnt) +ambsupersamp(AMBHEMI *hp, int cnt) { float *earr = getambdiffs(hp); double e2rem = 0; AMBSAMP *ap; RAY ar; - double asum[3]; float *ep; int i, j, n, nss; @@ -234,28 +195,81 @@ ambsupersamp(double acol[3], AMBHEMI *hp, int cnt) if (e2rem <= FTINY) goto done; /* nothing left to do */ nss = *ep/e2rem*cnt + frandom(); - asum[0] = asum[1] = asum[2] = 0.0; - for (n = 1; n <= nss; n++) { - if (!getambsamp(&ar, hp, i, j, n)) { - nss = n-1; - break; - } - addcolor(asum, ar.rcol); - } - if (nss) { /* update returned ambient value */ - const double ssf = 1./(nss + 1.); - for (n = 3; n--; ) - acol[n] += ssf*asum[n] + - (ssf - 1.)*colval(ap->v,n); - } - e2rem -= *ep++; /* update remainders */ - cnt -= nss; + for (n = 1; n <= nss && ambsample(hp,i,j,n); n++) + --cnt; + e2rem -= *ep++; /* update remainder */ } done: free(earr); } +static AMBHEMI * +samp_hemi( /* sample indirect hemisphere */ + COLOR rcol, + RAY *r, + double wt +) +{ + AMBHEMI *hp; + double d; + int n, i, j; + /* set number of divisions */ + if (ambacc <= FTINY && + wt > (d = 0.8*intens(rcol)*r->rweight/(ambdiv*minweight))) + wt = d; /* avoid ray termination */ + n = sqrt(ambdiv * wt) + 0.5; + i = 1 + 8*(ambacc > FTINY); /* minimum number of samples */ + if (n < i) + n = i; + /* allocate sampling array */ + hp = (AMBHEMI *)malloc(sizeof(AMBHEMI) + sizeof(AMBSAMP)*(n*n - 1)); + if (hp == NULL) + error(SYSTEM, "out of memory in samp_hemi"); + hp->rp = r; + hp->ns = n; + hp->acol[RED] = hp->acol[GRN] = hp->acol[BLU] = 0.0; + memset(hp->sa, 0, sizeof(AMBSAMP)*n*n); + hp->sampOK = 0; + /* assign coefficient */ + copycolor(hp->acoef, rcol); + d = 1.0/(n*n); + scalecolor(hp->acoef, d); + /* make tangent plane axes */ + hp->uy[0] = 0.5 - frandom(); + hp->uy[1] = 0.5 - frandom(); + hp->uy[2] = 0.5 - frandom(); + for (i = 3; i--; ) + if ((-0.6 < r->ron[i]) & (r->ron[i] < 0.6)) + break; + if (i < 0) + error(CONSISTENCY, "bad ray direction in samp_hemi"); + hp->uy[i] = 1.0; + VCROSS(hp->ux, hp->uy, r->ron); + normalize(hp->ux); + VCROSS(hp->uy, r->ron, hp->ux); + /* sample divisions */ + for (i = hp->ns; i--; ) + for (j = hp->ns; j--; ) + hp->sampOK += ambsample(hp, i, j, 0); + copycolor(rcol, hp->acol); + if (!hp->sampOK) { /* utter failure? */ + free(hp); + return(NULL); + } + if (hp->sampOK < hp->ns*hp->ns) { + hp->sampOK *= -1; /* soft failure */ + return(hp); + } + n = ambssamp*wt + 0.5; + if (n > 8) { /* perform super-sampling? */ + ambsupersamp(hp, n); + copycolor(rcol, hp->acol); + } + return(hp); /* all is well */ +} + + /* Return brightness of farthest ambient sample */ static double back_ambval(AMBHEMI *hp, const int n1, const int n2, const int n3) @@ -596,10 +610,13 @@ static uint32 ambcorral(AMBHEMI *hp, FVECT uv[2], const double r0, const double r1) { const double max_d = 1.0/(minarad*ambacc + 0.001); - const double ang_res = 0.5*PI/(hp->ns-1); - const double ang_step = ang_res/((int)(16/PI*ang_res) + (1+FTINY)); + const double ang_res = 0.5*PI/hp->ns; + const double ang_step = ang_res/((int)(16/PI*ang_res) + 1.01); double avg_d = 0; uint32 flgs = 0; + FVECT vec; + double u, v; + double ang, a1; int i, j; /* don't bother for a few samples */ if (hp->ns < 12) @@ -617,21 +634,31 @@ ambcorral(AMBHEMI *hp, FVECT uv[2], const double r0, c for (i = 0; i < hp->ns; i++) for (j = 0; j < hp->ns; j += !i|(i==hp->ns-1) ? 1 : hp->ns-1) { AMBSAMP *ap = &ambsam(hp,i,j); - FVECT vec; - double u, v; - double ang, a1; - int abp; if ((ap->d <= FTINY) | (ap->d >= max_d)) continue; /* too far or too near */ VSUB(vec, ap->p, hp->rp->rop); - u = DOT(vec, uv[0]) * ap->d; - v = DOT(vec, uv[1]) * ap->d; - if ((r0*r0*u*u + r1*r1*v*v) * ap->d*ap->d <= 1.0) + u = DOT(vec, uv[0]); + v = DOT(vec, uv[1]); + if ((r0*r0*u*u + r1*r1*v*v) * ap->d*ap->d <= u*u + v*v) continue; /* occluder outside ellipse */ ang = atan2a(v, u); /* else set direction flags */ - for (a1 = ang-.5*ang_res; a1 <= ang+.5*ang_res; a1 += ang_step) + for (a1 = ang-ang_res; a1 <= ang+ang_res; a1 += ang_step) flgs |= 1L<<(int)(16/PI*(a1 + 2.*PI*(a1 < 0))); } + /* add low-angle incident (< 20deg) */ + if (fabs(hp->rp->rod) <= 0.342) { + u = -DOT(hp->rp->rdir, uv[0]); + v = -DOT(hp->rp->rdir, uv[1]); + if ((r0*r0*u*u + r1*r1*v*v) > hp->rp->rot*hp->rp->rot) { + ang = atan2a(v, u); + ang += 2.*PI*(ang < 0); + ang *= 16/PI; + if ((ang < .5) | (ang >= 31.5)) + flgs |= 0x80000001; + else + flgs |= 3L<<(int)(ang-.5); + } + } return(flgs); } @@ -648,15 +675,12 @@ doambient( /* compute ambient component */ uint32 *crlp /* returned (optional) */ ) { - AMBHEMI *hp = inithemi(rcol, r, wt); - int cnt; + AMBHEMI *hp = samp_hemi(rcol, r, wt); FVECT my_uv[2]; - double d, K, acol[3]; + double d, K; AMBSAMP *ap; - int i, j; - /* check/initialize */ - if (hp == NULL) - return(0); + int i; + /* clear return values */ if (uv != NULL) memset(uv, 0, sizeof(FVECT)*2); if (ra != NULL) @@ -667,34 +691,15 @@ doambient( /* compute ambient component */ dg[0] = dg[1] = 0.0; if (crlp != NULL) *crlp = 0; - /* sample the hemisphere */ - acol[0] = acol[1] = acol[2] = 0.0; - cnt = 0; - for (i = hp->ns; i--; ) - for (j = hp->ns; j--; ) - if ((ap = ambsample(hp, i, j)) != NULL) { - addcolor(acol, ap->v); - ++cnt; - } - if (!cnt) { - setcolor(rcol, 0.0, 0.0, 0.0); - free(hp); - return(0); /* no valid samples */ + if (hp == NULL) /* sampling falure? */ + return(0); + + if ((ra == NULL) & (pg == NULL) & (dg == NULL) || + (hp->sampOK < 0) | (hp->ns < 9)) { + free(hp); /* Hessian not requested/possible */ + return(-1); /* value-only return value */ } - if (cnt < hp->ns*hp->ns) { /* incomplete sampling? */ - copycolor(rcol, acol); - free(hp); - return(-1); /* return value w/o Hessian */ - } - cnt = ambssamp*wt + 0.5; /* perform super-sampling? */ - if (cnt > 8) - ambsupersamp(acol, hp, cnt); - copycolor(rcol, acol); /* final indirect irradiance/PI */ - if ((ra == NULL) & (pg == NULL) & (dg == NULL)) { - free(hp); - return(-1); /* no radius or gradient calc. */ - } - if ((d = bright(acol)) > FTINY) { /* normalize Y values */ + if ((d = bright(rcol)) > FTINY) { /* normalize Y values */ d = 0.99*(hp->ns*hp->ns)/d; K = 0.01; } else { /* or fall back on geometric Hessian */ @@ -729,7 +734,7 @@ doambient( /* compute ambient component */ if (ra[1] < minarad) ra[1] = minarad; } - ra[0] *= d = 1.0/sqrt(sqrt(wt)); + ra[0] *= d = 1.0/sqrt(wt); if ((ra[1] *= d) > 2.0*ra[0]) ra[1] = 2.0*ra[0]; if (ra[1] > maxarad) {