/* Stochastic Relaxation Radiosity (currently only stochastic Jacobi) */ #include #include #include #include #include "error.h" #include "scene.h" #include "statistics.h" #include "render.h" #include "vrml.h" #include "pools.h" #include "mcrad.h" #include "mcradP.h" #include "hierarchy.h" #include "stochjacobi.h" static void SrrInit(void) { mcr.method = SRR; McrInit(); } static char *SrrGetStats(void) { static char stats[2000]; char *p; int n; p = stats; sprintf(p, "Stochastic Relaxation 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, "%ld elements (%ld clusters, %ld surfaces)\n%n", hierarchy.nr_elements, hierarchy.nr_clusters, hierarchy.nr_elements - hierarchy.nr_clusters, &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; } /* randomly returns floor(x) or ceil(x) so that the expected value is equal to x */ static long RandomRound(float x) { long l = (long)floor(x); if (drand48() < (x - (float)l)) l++; return l; } static void SrrRecomputeDisplayColors(void) { if (hierarchy.topcluster) { ForAllLeafElements(hierarchy.topcluster, ElementComputeNewVertexColors); ForAllLeafElements(hierarchy.topcluster, ElementAdjustTVertexColors); } else { PatchListIterate(Patches, McrPatchComputeNewColor); } } /* Computes quality factor on given leaf element (see PhD Ph.Bekaert p.152). * In the basic algorithms by Neumann et al. the quality factor would * correspond to the inverse of the elementary ray power. The quality factor * indicates the quality of the radiosity solution on a given leaf element. * The quality factor after different iterations is additive. It is used in order * to properly merge the result of new iterations with the result of previous * iterations properly taking into account the number of rays and importance * distribution. */ static double QualityFactor(ELEMENT *elem, double w) { #ifdef IDMCR if (mcr.importance_driven) { return w * elem->imp; } #endif /* IDMCR */ return w / ElementScalarReflectance(elem); } static COLOR *ElementUnshotRadiance(ELEMENT *elem) { return elem->unshot_rad; } static void ElementIncrementRadiance(ELEMENT *elem, double w) { /* Each incremental iteration computes a different contribution to the * solution. The quality factor of the result remains constant. */ if (mcr.discard_incremental) { elem->quality = 0.0; { static int wgiv = FALSE; if (!wgiv) Warning("ElementIncrementRadiance", "Solution of incremental Jacobi steps receives zero quality"); wgiv = TRUE; } } else elem->quality = QualityFactor(elem, w); ADDCOEFFICIENTS(elem->rad, elem->received_rad, elem->basis); COPYCOEFFICIENTS(elem->unshot_rad, elem->received_rad, elem->basis); if (mcr.set_source) { /* copy direct illumination and forget selfemitted illumination */ elem->rad[0] = elem->source_rad = elem->received_rad[0]; } CLEARCOEFFICIENTS(elem->received_rad, elem->basis); } static void PrintIncrementalRadianceStats(void) { fprintf(stderr, "%g secs., radiance rays = %ld (%ld not to background), unshot flux = ", mcr.cpu_secs, mcr.traced_rays, mcr.traced_rays-mcr.nrmisses); ColorPrint(stderr, mcr.unshot_flux); fprintf(stderr, ", total flux = "); ColorPrint(stderr, mcr.total_flux); #ifdef IDMCR fprintf(stderr, ", indirect importance weighted unshot flux = "); ColorPrint(stderr, mcr.imp_unshot_flux); #endif fprintf(stderr, "\n"); } static void DoIncrementalRadianceIterations(void) { double ref_unshot; long step_nr=0; int weighted_sampling = mcr.weighted_sampling; int importance_driven = mcr.importance_driven; if (!mcr.incremental_uses_importance) mcr.importance_driven = FALSE; /* temporarily switch it off */ mcr.weighted_sampling = FALSE; PrintIncrementalRadianceStats(); ref_unshot = COLORSUMABSCOMPONENTS(mcr.unshot_flux); #ifdef IDMCR if (mcr.incremental_uses_importance) { ref_unshot = COLORSUMABSCOMPONENTS(mcr.imp_unshot_flux); } #endif while (1) { /* choose nr of rays so that power carried by each ray remains equal, and * proportional to the number of basis functions in the rad. approx. */ double unshot_fraction; long nr_rays; unshot_fraction = COLORSUMABSCOMPONENTS(mcr.unshot_flux) / ref_unshot; #ifdef IDMCR if (mcr.incremental_uses_importance) { unshot_fraction = COLORSUMABSCOMPONENTS(mcr.imp_unshot_flux) / ref_unshot; } #endif if (unshot_fraction < 0.01) break; /* only 1/100th of selfemitted power remains unshot */ nr_rays = RandomRound(unshot_fraction * (double)mcr.initial_nr_rays * approxdesc[mcr.approx_type].basis_size); step_nr ++; fprintf(stderr, "Incremental radiance propagation step %ld: %.3f%% unshot power left.\n", step_nr, 100.*unshot_fraction); DoStochasticJacobiIteration(nr_rays, ElementUnshotRadiance, NULL, ElementIncrementRadiance); mcr.set_source = FALSE; /* direct illumination is copied to SOURCE_FLUX(P) only * the first time. */ McrUpdateCpuSecs(); PrintIncrementalRadianceStats(); if (unshot_fraction > 0.3) { SrrRecomputeDisplayColors(); RenderNewDisplayList(); RenderScene(); } } mcr.importance_driven = importance_driven; /* switch it back on if it was on */ mcr.weighted_sampling = weighted_sampling; } #ifdef IDMCR static float ElementUnshotImportance(ELEMENT *elem) { return elem->unshot_imp; } static void ElementIncrementImportance(ELEMENT *elem, double w) { elem->imp += elem->received_imp; elem->unshot_imp = elem->received_imp; elem->received_imp = 0.; } static void PrintIncrementalImportanceStats(void) { fprintf(stderr, "%g secs., importance rays = %ld, unshot importance = %g, total importance = %g, total area = %g\n", mcr.cpu_secs, mcr.imp_traced_rays, mcr.unshot_ymp, mcr.total_ymp, total_area); } static void DoIncrementalImportanceIterations(void) { long step_nr=0; int radiance_driven = mcr.radiance_driven; int do_h_meshing = hierarchy.do_h_meshing; CLUSTERING_MODE clustering = hierarchy.clustering; int weighted_sampling = mcr.weighted_sampling; if (mcr.source_ymp < EPSILON) { fprintf(stderr, "No source importance!!\n"); return; } mcr.radiance_driven = FALSE; /* temporary switch it off */ hierarchy.do_h_meshing = FALSE; hierarchy.clustering = NO_CLUSTERING; mcr.weighted_sampling = FALSE; PrintIncrementalRadianceStats(); while (1) { /* choose nr of rays so that power carried by each ray is the same, and * proportional to the number of basis functions in the rad. approx. */ double unshot_fraction = mcr.unshot_ymp / mcr.source_ymp; long nr_rays = RandomRound(unshot_fraction * (double)mcr.initial_nr_rays); if (unshot_fraction < 0.01) break; step_nr ++; fprintf(stderr, "Incremental importance propagation step %ld: %.3f%% unshot importance left.\n", step_nr, 100.*unshot_fraction); DoStochasticJacobiIteration(nr_rays, NULL, ElementUnshotImportance, ElementIncrementImportance); McrUpdateCpuSecs(); PrintIncrementalImportanceStats(); } mcr.radiance_driven = radiance_driven; /* switch on again */ hierarchy.do_h_meshing = do_h_meshing; hierarchy.clustering = clustering; mcr.weighted_sampling = weighted_sampling; } #endif /*IDMCR*/ static COLOR *ElementRadiance(ELEMENT *elem) { return elem->rad; } static void ElementUpdateRadiance(ELEMENT *elem, double w) { double k = (double)mcr.prev_traced_rays / (double)(mcr.traced_rays > 0 ? mcr.traced_rays : 1); if (!mcr.naive_merging) { double quality = QualityFactor(elem, w); if (elem->quality < EPSILON) { k = 0.; /* solution of this iteration takes over */ } else if (quality < EPSILON) { k = 1.; /* keep result of previous iterations */ } else if (elem->quality + quality > EPSILON) { k = elem->quality / (elem->quality + quality); } else /* quality of new solution is so high that it must take over */ k = 0.; elem->quality += quality; /* add quality */ } /* subtract source radiosity */ COLORSUBTRACT(elem->rad[0], elem->source_rad, elem->rad[0]); /* combine with previous results */ SCALECOEFFICIENTS(k, elem->rad, elem->basis); SCALECOEFFICIENTS((1.-k), elem->received_rad, elem->basis); ADDCOEFFICIENTS(elem->rad, elem->received_rad, elem->basis); /* re-add source radiosity */ COLORADD(elem->rad[0], elem->source_rad, elem->rad[0]); /* clear unshot and received radiance */ CLEARCOEFFICIENTS(elem->unshot_rad, elem->basis); CLEARCOEFFICIENTS(elem->received_rad, elem->basis); } static void PrintRegularStats(void) { fprintf(stderr, "%g secs., radiance rays = %ld (%ld not to background), unshot flux = ", mcr.cpu_secs, mcr.traced_rays, mcr.traced_rays-mcr.nrmisses); 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"); } static void DoRegularRadianceIteration(void) { fprintf(stderr, "Regular radiance iteration %d:\n", mcr.iteration_nr); DoStochasticJacobiIteration(mcr.rays_per_iteration, ElementRadiance, NULL, ElementUpdateRadiance); McrUpdateCpuSecs(); PrintRegularStats(); } #ifdef IDMCR static float ElementImportance(ELEMENT *elem) { return elem->imp; } static void ElementUpdateImportance(ELEMENT *elem, double w) { double k = (double)mcr.prev_imp_traced_rays/(double)mcr.imp_traced_rays; elem->imp = k * (elem->imp-elem->source_imp) + (1.-k) * elem->received_imp + elem->source_imp; elem->unshot_imp = elem->received_imp = 0.; } static void DoRegularImportanceIteration(void) { long nr_rays; int do_h_meshing = hierarchy.do_h_meshing; CLUSTERING_MODE clustering = hierarchy.clustering; int weighted_sampling = mcr.weighted_sampling; hierarchy.do_h_meshing = FALSE; hierarchy.clustering = NO_CLUSTERING; mcr.weighted_sampling = FALSE; nr_rays = mcr.imp_rays_per_iteration; fprintf(stderr, "Regular importance iteration %d:\n", mcr.iteration_nr); DoStochasticJacobiIteration(nr_rays, NULL, ElementImportance, ElementUpdateImportance); McrUpdateCpuSecs(); PrintRegularStats(); hierarchy.do_h_meshing = do_h_meshing; hierarchy.clustering = clustering; mcr.weighted_sampling = weighted_sampling; } #endif /* Resets to zero all kind of things that should be reset to zero after a first * iteration of which the result only is to be used as the input of subsequent * iterations. Basically, everything that needs to be divided by the number of * rays except radiosity and importance needs to be reset to zero. This is * required for some of the experimental stuff to work. */ static void ElementDiscardIncremental(ELEMENT *elem) { elem->quality = 0.; /* recurse */ ForAllChildrenElements(elem, ElementDiscardIncremental); } static void DiscardIncremental(void) { mcr.nr_weighted_rays = mcr.old_nr_weighted_rays = 0; mcr.traced_rays = mcr.prev_traced_rays = 0; ElementDiscardIncremental(hierarchy.topcluster); } static int SrrDoStep(void) { McrPreStep(); /* do some real work now */ if (mcr.iteration_nr == 1) { int initial_nr_of_rays = 0; if (mcr.do_nondiffuse_first_shot) DoNonDiffuseFirstShot(); initial_nr_of_rays = mcr.traced_rays; #ifdef IDMCR if (mcr.importance_driven) { if (!mcr.incremental_uses_importance) { Warning(NULL, "Importance is only used from the second iteration on ..."); } else if (mcr.importance_updated) { mcr.importance_updated = FALSE; /* propagate importance changes */ DoIncrementalImportanceIterations(); if (mcr.importance_updated_from_scratch) mcr.imp_rays_per_iteration = mcr.imp_traced_rays; } } #endif DoIncrementalRadianceIterations(); /* subsequent regular iteratoins will take as many rays as in the whole * sequence of incremental iteration steps. */ mcr.rays_per_iteration = mcr.traced_rays - initial_nr_of_rays; if (mcr.discard_incremental) DiscardIncremental(); } else { #ifdef IDMCR if (mcr.importance_driven) { if (mcr.importance_updated) { mcr.importance_updated = FALSE; /* propagate importance changes */ DoIncrementalImportanceIterations(); if (mcr.importance_updated_from_scratch) mcr.imp_rays_per_iteration = mcr.imp_traced_rays; } else { DoRegularImportanceIteration(); } } #endif DoRegularRadianceIteration(); } SrrRecomputeDisplayColors(); McrPostStep(); fprintf(stderr, "%s\n", SrrGetStats()); return FALSE; /* always continu computing (never fully converged) */ } static void McrRenderPatch(PATCH *patch) { extern void RenderPatch(PATCH *patch); /* default RenderPatch routine in ../render.c */ if (mcr.inited) McrForAllSurfaceLeafs(TOPLEVEL_ELEMENT(patch), RenderElement); else RenderPatch(patch); /* not yet initialised */ } static void SrrRender(void) { if (renderopts.frustum_culling) RenderWorldOctree(McrRenderPatch); else PatchListIterate(Patches, McrRenderPatch); } RADIANCEMETHOD StochasticRelaxationRadiosity = { "StochJacobi", 3, "Stochastic Jacobi Radiosity", "stochrelaxButton", McrDefaults, SrrParseOptions, SrrPrintOptions, SrrInit, SrrDoStep, McrTerminate, McrGetRadiance, McrCreatePatchData, McrPrintPatchData, McrDestroyPatchData, SrrCreateControlPanel, SrrUpdateControlPanel, SrrShowControlPanel, SrrHideControlPanel, SrrGetStats, SrrRender, SrrRecomputeDisplayColors, McrUpdateMaterial, McrWriteVRML };