/* Radnom Walk Radiosity */ #include #include #include #include #include "error.h" #include "scene.h" #include "statistics.h" #include "mcrad.h" #include "mcradP.h" #include "tracepath.h" #include "stochjacobi.h" #include "ccr.h" static void RwrPrintPatchData(FILE *out, PATCH *patch) { PrintElement(out, TOPLEVEL_ELEMENT(patch)); } static void RwrInit(void) { mcr.method = RWR; McrInit(); } static void PrintStats(void) { fprintf(stderr, "%g secs., total radiance rays = %ld", mcr.cpu_secs, mcr.traced_rays); fprintf(stderr, ", total flux = "); ColorPrint(stderr, mcr.total_flux); if (mcr.importance_driven) fprintf(stderr, "\ntotal importance rays = %ld, total importance = %g, total_area = %g", mcr.imp_traced_rays, mcr.total_ymp, total_area); fprintf(stderr, "\n"); } /* used as unnormalised probability for mimicking global lines */ static double PatchArea(PATCH *P) { return P->area; } /* probability proportional to power to be propagated. */ static double ScalarSourcePower(PATCH *P) { COLOR radiance = SOURCE_RAD(P); return /* M_PI * */ P->area * COLORSUMABSCOMPONENTS(radiance); } /* returns a double instead of a float in order to make it useful as * a survival probability function */ static double ScalarReflectance(PATCH *P) { return McrScalarReflectance(P); } static COLOR *GetSelfEmittedRadiance(ELEMENT *elem) { static COLOR Ed[MAX_BASIS_SIZE]; CLEARCOEFFICIENTS(Ed, elem->basis); Ed[0] = EMITTANCE(elem->pog.patch); return Ed; } static COLOR *GetSourceRadiance(ELEMENT *elem) { static COLOR Ed[MAX_BASIS_SIZE]; CLEARCOEFFICIENTS(Ed, elem->basis); Ed[0] = elem->source_rad; return Ed; } /* subtracts (1-rho) * control radiosity from the source radiosity of each patch */ static void ReduceSource(void) { ForAllPatches(P, Patches) { COLOR newsrcrad, rho; COLORSETMONOCHROME(newsrcrad, 1.); rho = REFLECTANCE(P); COLORSUBTRACT(newsrcrad, rho, newsrcrad); /* 1-rho */ COLORPROD(newsrcrad, mcr.control_radiance, newsrcrad); /* (1-rho) * beta */ COLORSUBTRACT(SOURCE_RAD(P), newsrcrad, newsrcrad); /* E - (1-rho) beta */ SOURCE_RAD(P) = newsrcrad; } EndForAll; } static double ScoreWeight(PATH *path, int n) { double w = 0.; int t = path->nrnodes - ((mcr.rw_numlast>0) ? mcr.rw_numlast : 1); switch (mcr.rw_estimator_kind) { case RW_COLLISION: w = 1.; break; case RW_ABSORPTION: if (n == path->nrnodes-1) /* last node */ w = 1./(1. - path->nodes[n].probability); break; case RW_SURVIVAL: if (n < path->nrnodes-1) /* not last node */ w = 1./path->nodes[n].probability; break; case RW_LAST_BUT_NTH: if (n == t-1) { int i = path->nrnodes-1; PATHNODE *node = &path->nodes[i]; w = 1./(1. - node->probability); /* absorption prob of the last node */ for (i--, node--; i>=n; i--, node--) w /= node->probability; /* survival prob of n...nrnodes-2th node */ } break; case RW_NLAST: if (n == t) { /* 1/absorption probability of the last path node */ w = 1./(1. - path->nodes[path->nrnodes-1].probability); } else if (n > t) { w = 1.; } break; default: Fatal(-1, "ScoreWeight", "Unknown random walk estimator kind %d", mcr.rw_estimator_kind); } return w; } static void ShootingScore(PATH *path, long nr_paths, double (*birth_prob)(PATCH *)) { COLOR accum_pow; int n; PATHNODE *node = &path->nodes[0]; /* path->nodes[0].probability is birth probability of the path */ COLORSCALE((node->patch->area/node->probability), SOURCE_RAD(node->patch), accum_pow); for (n=1, node++; nnrnodes; n++, node++) { double uin=0., vin=0., uout=0., vout=0., r=1., w; int i; PATCH *P = node->patch; COLOR Rd = REFLECTANCE(P); COLORPROD(accum_pow, Rd, accum_pow); #ifdef HOMCR PatchUniformUV(P, &node->inpoint, &uin, &vin); if (!mcr.continuous_random_walk) { r=0.; if (nnrnodes-1) { /* not continuous random walk and not node of absorption */ PatchUniformUV(P, &node->outpoint, &uout, &vout); } } #endif /*HOMCR*/ w = ScoreWeight(path, n); for (i=0; isize; i++) { double dual = BAS(P)->dualfunction[i](uin,vin) / P->area; COLORADDSCALED(RECEIVED_RAD(P)[i], (w*dual/(double)nr_paths), accum_pow, RECEIVED_RAD(P)[i]); #ifdef HOMCR if (!mcr.continuous_random_walk) { double basf = BAS(P)->function[i](uout, vout); r += dual * P->area * basf; } #endif /*HOMCR*/ } COLORSCALE((r/node->probability), accum_pow, accum_pow); } } static void ShootingUpdate(PATCH *P, double w) { double k, old_quality; old_quality = QUALITY(P); QUALITY(P) += w; if (QUALITY(P) < EPSILON) return; k = old_quality / QUALITY(P); /* subtract selfemitted rad */ COLORSUBTRACT(RAD(P)[0], SOURCE_RAD(P), RAD(P)[0]); /* weight with previous results */ SCALECOEFFICIENTS(k, RAD(P), BAS(P)); SCALECOEFFICIENTS((1.-k), RECEIVED_RAD(P), BAS(P)); ADDCOEFFICIENTS(RAD(P), RECEIVED_RAD(P), BAS(P)); /* re-add selfemitted rad */ COLORADD(RAD(P)[0], SOURCE_RAD(P), RAD(P)[0]); /* clear unshot and received radiance */ CLEARCOEFFICIENTS(UNSHOT_RAD(P), BAS(P)); CLEARCOEFFICIENTS(RECEIVED_RAD(P), BAS(P)); } static void DoShootingIteration(void) { long nr_walks; nr_walks = mcr.initial_nr_rays; #ifdef HOMCR if (mcr.continuous_random_walk) nr_walks *= approxdesc[mcr.approx_type].basis_size; else nr_walks *= pow(approxdesc[mcr.approx_type].basis_size, 1./(1. - COLORMAXCOMPONENT(average_reflectivity))); #endif fprintf(stderr, "Shooting iteration %d (%ld paths, approximately %ld rays)\n", mcr.iteration_nr, nr_walks, (long)floor((double)nr_walks / (1. - COLORMAXCOMPONENT(average_reflectivity)))); TracePaths(nr_walks, ScalarSourcePower, ScalarReflectance, ShootingScore, ShootingUpdate); } /* determines control radiosity value for collision gathering estimator */ static COLOR DetermineGatheringControlRadiosity(void) { COLOR c1, c2, cr; COLORCLEAR(c1); COLORCLEAR(c2); ForAllPatches(P, Patches) { COLOR absorb, rho, Ed, num, denom; COLORSETMONOCHROME(absorb, 1.); rho = REFLECTANCE(P); COLORSUBTRACT(absorb, rho, absorb); /* 1-rho */ Ed = SOURCE_RAD(P); COLORPROD(absorb, Ed, num); COLORADDSCALED(c1, P->area, num, c1); /* A_P (1-rho_P) E_P */ COLORPROD(absorb, absorb, denom); COLORADDSCALED(c2, P->area, denom, c2); /* A_P (1-rho_P)^2 */ } EndForAll; COLORDIV(c1, c2, cr); fprintf(stderr, "Control radiosity value = "); ColorPrint(stderr, cr); fprintf(stderr, ", luminosity = %g\n", ColorLuminance(cr)); return cr; } static void CollisionGatheringScore(PATH *path, long nr_paths, double (*birth_prob)(PATCH *)) { COLOR accum_rad; int n; PATHNODE *node = &path->nodes[path->nrnodes-1]; accum_rad = SOURCE_RAD(node->patch); for (n=path->nrnodes-2, node--; n>=0; n--, node--) { double uin=0., vin=0., uout=0., vout=0., r=1.; int i; PATCH *P = node->patch; COLOR Rd = REFLECTANCE(P); COLORPROD(Rd, accum_rad, accum_rad); #ifdef HOMCR PatchUniformUV(P, &node->outpoint, &uout, &vout); if (!mcr.continuous_random_walk) { r=0.; if (n>0) { /* not continuous random walk and not birth node */ PatchUniformUV(P, &node->inpoint, &uin, &vin); } } #endif /*HOMCR*/ for (i=0; isize; i++) { double dual = BAS(P)->dualfunction[i](uout,vout); /* = dual basis f * area */ COLORADDSCALED(RECEIVED_RAD(P)[i], dual, accum_rad, RECEIVED_RAD(P)[i]); #ifdef HOMCR if (!mcr.continuous_random_walk) { double basf = BAS(P)->function[i](uin, vin); r += basf * dual; } #endif /*HOMCR*/ } NG(P) ++; COLORSCALE((r/node->probability), accum_rad, accum_rad); COLORADD(accum_rad, SOURCE_RAD(P), accum_rad); } } static void GatheringUpdate(PATCH *P, double w) { /* use unshot rad for accumulating sum of contributions */ ADDCOEFFICIENTS(UNSHOT_RAD(P), RECEIVED_RAD(P), BAS(P)); COPYCOEFFICIENTS(RAD(P), UNSHOT_RAD(P), BAS(P)); /* divide by nr of samples */ if (NG(P) > 0) SCALECOEFFICIENTS((1./(double)NG(P)), RAD(P), BAS(P)); /* add source radiance (source term estimation suppresion!) */ COLORADD(RAD(P)[0], SOURCE_RAD(P), RAD(P)[0]); if (mcr.constant_control_variate) { /* add constant control radiosity value */ COLOR cr = mcr.control_radiance; if (mcr.indirect_only) { COLOR Rd = REFLECTANCE(P); COLORPROD(Rd, mcr.control_radiance, cr); } COLORADD(RAD(P)[0], cr, RAD(P)[0]); } CLEARCOEFFICIENTS(RECEIVED_RAD(P), BAS(P)); } static void DoGatheringIteration(void) { long nr_walks = mcr.initial_nr_rays; #ifdef HOMCR if (mcr.continuous_random_walk) nr_walks *= approxdesc[mcr.approx_type].basis_size; else nr_walks *= pow(approxdesc[mcr.approx_type].basis_size, 1./(1. - COLORMAXCOMPONENT(average_reflectivity))); #endif if (mcr.constant_control_variate && mcr.iteration_nr == 1) { /* constant control variate for gathering random walk radiosity */ mcr.control_radiance = DetermineGatheringControlRadiosity(); ReduceSource(); /* do this only once!! */ } fprintf(stderr, "Collision gathering iteration %d (%ld paths, approximately %ld rays)\n", mcr.iteration_nr, nr_walks, (long)floor((double)nr_walks / (1. - COLORMAXCOMPONENT(average_reflectivity)))); TracePaths(nr_walks, PatchArea, ScalarReflectance, CollisionGatheringScore, GatheringUpdate); } static COLOR OneMinusRho(ELEMENT *elem) { COLOR rho = REFLECTANCE(elem->pog.patch); COLOR oneminusrho; COLORSETMONOCHROME(oneminusrho, 1.); COLORSUBTRACT(oneminusrho, rho, oneminusrho); return oneminusrho; } static COLOR DetermineShootingControlRadiosity(void) { return DetermineControlRadiosity(GetSourceRadiance, OneMinusRho); } static void UpdateSourceIllum(ELEMENT *elem, double w) { COPYCOEFFICIENTS(elem->rad, elem->received_rad, elem->basis); elem->source_rad = elem->received_rad[0]; CLEARCOEFFICIENTS(elem->unshot_rad, elem->basis); CLEARCOEFFICIENTS(elem->received_rad, elem->basis); } static void DoFirstShot(void) { long nr_rays = mcr.initial_nr_rays * approxdesc[mcr.approx_type].basis_size; fprintf(stderr, "First shot (%ld rays):\n", nr_rays); DoStochasticJacobiIteration(nr_rays, GetSelfEmittedRadiance, NULL, UpdateSourceIllum); PrintStats(); } static int RwrDoStep(void) { McrPreStep(); if (mcr.iteration_nr == 1) { if (mcr.indirect_only) DoFirstShot(); } switch (mcr.rw_estimator_type) { case RW_SHOOTING: DoShootingIteration(); break; case RW_GATHERING: DoGatheringIteration(); break; default: Fatal(-1, "RwrDoStep", "Unknown random walk estimator type %d", mcr.rw_estimator_type); } PatchListIterate(Patches, McrPatchComputeNewColor); McrPostStep(); return FALSE; /* never converged */ } static void RwrTerminate(void) { /* TerminateLightSampling(); */ McrTerminate(); } static char *RwrGetStats(void) { static char stats[2000]; char *p; int n; p = stats; sprintf(p, "Random Walk Radiosity\nStatistics\n\n%n", &n); p += n; sprintf(p, "Iteration nr: %d\n%n", mcr.iteration_nr, &n); p += n; sprintf(p, "CPU time: %g secs\n%n", mcr.cpu_secs, &n); p += n; sprintf(p, "Memory usage: %ld KBytes.\n%n", GetMemoryUsage()/1024, &n); p += n; sprintf(p, "Radiance rays: %ld\n%n", mcr.traced_rays, &n); p += n; sprintf(p, "Importance rays: %ld\n%n", mcr.imp_traced_rays, &n); p += n; return stats; } RADIANCEMETHOD RandomWalkRadiosity = { "RandomWalk", 3, "Random Walk Radiosity", "randwalkButton", McrDefaults, RwrParseOptions, RwrPrintOptions, RwrInit, RwrDoStep, RwrTerminate, McrGetRadiance, McrCreatePatchData, RwrPrintPatchData, McrDestroyPatchData, RwrCreateControlPanel, RwrUpdateControlPanel, RwrShowControlPanel, RwrHideControlPanel, RwrGetStats, (void (*)(void))NULL, /* use default rendering method */ McrRecomputeDisplayColors, McrUpdateMaterial, (void (*)(FILE *))NULL /* use default VRML model saver */ };