/* shooting.c: Southwell Galerkin radiosity (progressive refinement radiosity) */ #include #include "galerkinP.h" #include "scene.h" #include "vertex.h" #include "statistics.h" #include "coefficients.h" #include "camera.h" #include "potential.h" #include "formfactor.h" #include "render.h" #include "basis.h" #include "coefficients.h" #include "error.h" /* Returns the patch with highest unshot power, weighted with indirect * importance if importance-driven (see Bekaert&Willems, "Importance-driven * Progressive refinement radiosity", EGRW'95, Dublin. */ static PATCH *ChooseRadianceShootingPatch(void) { PATCH *shooting_patch, *pot_shooting_patch; float power, maxpower, powerimp, maxpowerimp; PATCHLIST *pl; maxpower = maxpowerimp = 0.; shooting_patch = pot_shooting_patch = (PATCH *)NULL; for (pl=Patches; pl; pl=pl->next) { PATCH *patch = pl->patch; power = M_PI * patch->area * COLORSUMABSCOMPONENTS(UNSHOT_RADIANCE(patch)); if (power > maxpower) { shooting_patch = patch; maxpower = power; } if (gal.importance_driven) { /* for importance-driven progressive refinement radiosity, choose the patch * with highest indirectly received potential times power. */ powerimp = (POTENTIAL(patch).f - patch->direct_potential) * power; if (powerimp > maxpowerimp) { pot_shooting_patch = patch; maxpowerimp = powerimp; } } } if (gal.importance_driven && pot_shooting_patch) return pot_shooting_patch; return shooting_patch; } /* Propagates unshot radiance and potential from source to receiver over the link. */ static void ShootUnshotRadianceAndPotentialOverLink(INTERACTION *link) { COLOR *srcrad, *rcvrad; srcrad = link->src->unshot_radiance; rcvrad = link->rcv->received_radiance; if (link->nrcv==1 && link->nsrc==1) { COLORADDSCALED(rcvrad[0], link->K.f, srcrad[0], rcvrad[0]); } else { int alpha, beta, a, b; a = MIN(link->nrcv, link->rcv->basis_size); b = MIN(link->nsrc, link->src->basis_size); for (alpha=0; alphaK.p[alpha*link->nsrc + beta], srcrad[beta], rcvrad[alpha]); } } if (gal.importance_driven) { /* propagate unshot potential of the source to the receiver */ float K = ((link->nrcv==1 && link->nsrc==1) ? link->K.f : link->K.p[0]); COLOR srcrho; if (IsCluster(link->src)) { COLORSETMONOCHROME(srcrho, 1.); } else srcrho = REFLECTIVITY(link->src->pog.patch); link->rcv->received_potential.f += K * COLORMAXCOMPONENT(srcrho) * link->src->unshot_potential.f; } } /* Propagates and clears the unshot radiance and potential of the element and * all its subelements */ static void ShootUnshotRadianceAndPotential(ELEMENT *elem) { ITERATE_REGULAR_SUBELEMENTS(elem, ShootUnshotRadianceAndPotential); ITERATE_IRREGULAR_SUBELEMENTS(elem, ShootUnshotRadianceAndPotential); InteractionListIterate(elem->interactions, ShootUnshotRadianceAndPotentialOverLink); CLEARCOEFFICIENTS(elem->unshot_radiance, elem->basis_size); elem->unshot_potential.f = 0.; } static void ClearUnshotRadianceAndPotential(ELEMENT *elem) { ITERATE_REGULAR_SUBELEMENTS(elem, ClearUnshotRadianceAndPotential); ITERATE_IRREGULAR_SUBELEMENTS(elem, ClearUnshotRadianceAndPotential); CLEARCOEFFICIENTS(elem->unshot_radiance, elem->basis_size); elem->unshot_potential.f = 0.; } /* Creates initial links if necessary. Propagates the unshot radiance of * the patch into the environment. Finally clears the unshot radiance * at all levels of the element hierarchy for the patch. */ static void PatchPropagateUnshotRadianceAndPotential(PATCH *shooting_patch) { ELEMENT *top = TOPLEVEL_ELEMENT(shooting_patch); if (!(top->flags & INTERACTIONS_CREATED)) { if (gal.clustered) CreateInitialLinkWithTopCluster(top, SOURCE); else CreateInitialLinks(top, SOURCE); top->flags |= INTERACTIONS_CREATED; } /* Recusrively refines the interactions of the shooting patch * and computes radiance and potential transport. */ RefineInteractions(top); /* EnforceEnergyConservation(shooting_patch); should be done before transport */ /* Clear the unshot radiance at all levels */ ClearUnshotRadianceAndPotential(top); } /* Makes the hierarchical representation of potential consistent over all * all levels. */ static float ShootingPushPullPotential(ELEMENT *elem, float down) { float up; int i; down += elem->received_potential.f / elem->area; elem->received_potential.f = 0.; up = 0.; if (!elem->regular_subelements && !elem->irregular_subelements) { up = down; } if (elem->regular_subelements) { for (i=0; i<4; i++) up += 0.25 * ShootingPushPullPotential(elem->regular_subelements[i], down); } if (elem->irregular_subelements) { ELEMENTLIST *subellist; for (subellist=elem->irregular_subelements; subellist; subellist=subellist->next) { ELEMENT *subel = subellist->element; if (!IsCluster(elem)) down = 0.; /* don't push to irregular surface subelements */ up += subel->area / elem->area * ShootingPushPullPotential(subel, down); } } elem->potential.f += up; elem->unshot_potential.f += up; return up; } static void PatchUpdateRadianceAndPotential(PATCH *patch) { if (gal.importance_driven) ShootingPushPullPotential(TOPLEVEL_ELEMENT(patch), 0.); PushPullRadiance(TOPLEVEL_ELEMENT(patch)); COLORADDSCALED(gal.ambient_radiance, patch->area, UNSHOT_RADIANCE(patch), gal.ambient_radiance); } static void DoPropagate(PATCH *shooting_patch) { /* propagate the unshot power of the shooting patch into the environment. */ PatchPropagateUnshotRadianceAndPotential(shooting_patch); /* recompute the colors of all patches, not only the patches that received * radiance from the shooting patch, since the ambient term has also changed. */ if (gal.clustered) { if (gal.importance_driven) ShootingPushPullPotential(gal.top_cluster, 0.); PushPullRadiance(gal.top_cluster); gal.ambient_radiance = gal.top_cluster->unshot_radiance[0]; } else { COLORCLEAR(gal.ambient_radiance); PatchListIterate(Patches, PatchUpdateRadianceAndPotential); COLORSCALE(1./total_area, gal.ambient_radiance, gal.ambient_radiance); } PatchListIterate(Patches, PatchRecomputeColor); } static int PropagateRadiance(void) { PATCH *shooting_patch; /* choose a shooting patch. also accumulates the total unshot power into * gal.ambient_radiance. */ shooting_patch = ChooseRadianceShootingPatch(); if (!shooting_patch) return TRUE; RenderSetColor(&Yellow); RenderPatchOutline(shooting_patch); DoPropagate(shooting_patch); return FALSE; } /* Called for each patch after direct potential has changed (because the * virtual camera has changed). */ void ShootingUpdateDirectPotential(ELEMENT *elem, float potential_increment) { if (elem->regular_subelements) { int i; for (i=0; i<4; i++) ShootingUpdateDirectPotential(elem->regular_subelements[i], potential_increment); } elem->direct_potential.f += potential_increment; elem->potential.f += potential_increment; elem->unshot_potential.f += potential_increment; } /* Recomputes the potential and unshot potential of the cluster and its subclusters * based on the potential of the contained patches. */ static void ClusterUpdatePotential(ELEMENT *clus) { if (IsCluster(clus)) { ELEMENTLIST *subcluslist; clus->potential.f = 0.; clus->unshot_potential.f = 0.; for (subcluslist=clus->irregular_subelements; subcluslist; subcluslist=subcluslist->next) { ELEMENT *subclus = subcluslist->element; ClusterUpdatePotential(subclus); clus->potential.f += subclus->area * subclus->potential.f; clus->unshot_potential.f += subclus->area * subclus->unshot_potential.f; } clus->potential.f /= clus->area; clus->unshot_potential.f /= clus->area; } } /* Chooses the patch with highest unshot importance (potential times * area), see Bekaert&Willems, EGRW'95 (Dublin) */ static PATCH *ChoosePotentialShootingPatch(void) { float maximp = 0.; PATCH *shooting_patch = (PATCH *)NULL; PATCHLIST *pl; for (pl = Patches; pl; pl=pl->next) { PATCH *patch = pl->patch; float imp = patch->area * fabs(UNSHOT_POTENTIAL(patch).f); if (imp > maximp) { shooting_patch = patch; maximp = imp; } } return shooting_patch; } static void PropagatePotential(void) { PATCH *shooting_patch; shooting_patch = ChoosePotentialShootingPatch(); if (shooting_patch) { RenderSetColor(&White); RenderPatchOutline(shooting_patch); DoPropagate(shooting_patch); } else fprintf(stderr, "No patches with unshot potential??\n"); } /* One step of the progressive refinement radiosity algorithm */ int ReallyDoShootingStep(void) { if (gal.importance_driven) { if (gal.iteration_nr<=1 || Camera.changed) { UpdateDirectPotential(); ForAllPatches(P, Patches) { ELEMENT *top = TOPLEVEL_ELEMENT(P); float potential_increment = P->direct_potential - top->direct_potential.f; ShootingUpdateDirectPotential(top, potential_increment); } EndForAll; Camera.changed = FALSE; if (gal.clustered) ClusterUpdatePotential(gal.top_cluster); } PropagatePotential(); } return PropagateRadiance(); } int DoShootingStep(void) { /* float last_cpu_time = gal.cpu_secs; int converged = FALSE; while (!converged && gal.cpu_secs < last_cpu_time + 5) converged = ReallyDoShootingStep(); return converged; */ return ReallyDoShootingStep(); } /* updates the radiance on a surface element due to a change of the material * properties */ static void ElementUpdateMaterial(ELEMENT *top, COLOR oldrho, COLOR newrho, COLOR olde, COLOR newe) { COLOR Btmp[MAXBASISSIZE]; if (top->regular_subelements) { CLEARCOEFFICIENTS(top->radiance, top->basis_size); CLEARCOEFFICIENTS(top->unshot_radiance, top->basis_size); ForAllRegularSubelements(child, top) { ElementUpdateMaterial(child, oldrho, newrho, olde, newe); Pull(top, Btmp, child, child->radiance); ADDCOEFFICIENTS(top->radiance, Btmp, top->basis_size); Pull(top, Btmp, child, child->unshot_radiance); ADDCOEFFICIENTS(top->unshot_radiance, Btmp, top->basis_size); } EndForAll; } else { int i; COPYCOEFFICIENTS(Btmp, top->radiance, top->basis_size); COLORSUBTRACT(Btmp[0], olde, Btmp[0]); for (i=0; ibasis_size; i++) { COLORDIV(Btmp[i], oldrho, Btmp[i]); COLORPROD(Btmp[i], newrho, Btmp[i]); } COLORADD(Btmp[0], newe, Btmp[0]); for (i=0; ibasis_size; i++) { COLORADD(top->unshot_radiance[i], Btmp[i], top->unshot_radiance[i]); COLORSUBTRACT(top->unshot_radiance[i], top->radiance[i], top->unshot_radiance[i]); top->radiance[i] = Btmp[i]; } } } static void ClusterUpdateMaterial(ELEMENT *clus) { if (clus->irregular_subelements) { CLEARCOEFFICIENTS(clus->radiance, clus->basis_size); CLEARCOEFFICIENTS(clus->unshot_radiance, clus->basis_size); ForAllIrregularSubelements(child, clus) { COLOR Btmp[MAXBASISSIZE]; ClusterUpdateMaterial(child); Pull(clus, Btmp, child, child->radiance); ADDCOEFFICIENTS(clus->radiance, Btmp, clus->basis_size); Pull(clus, Btmp, child, child->unshot_radiance); ADDCOEFFICIENTS(clus->unshot_radiance, Btmp, clus->basis_size); } EndForAll; } } void ShootingUpdateMaterial(MATERIAL *oldmaterial, MATERIAL *newmaterial) { /* TODO */ Warning("ShootingUpdateMaterial", "Out of order"); #ifdef NEVER POINT p = {0.,0.,0.}; COLOR newrho = BsdfDiffuseReflectance(newmaterial->bsdf, &p), oldrho = BsdfDiffuseReflectance(oldmaterial->bsdf, &p), newe = EdfDiffuseRadiance(newmaterial->edf, &p), olde = EdfDiffuseRadiance(oldmaterial->edf, &p); { static int wgiv=0; if (!wgiv) { Warning("ShootingUpdateMaterial", "will only work for non-textured materials"); wgiv = 1; } } ForAllPatches(p, Patches) { if (p->surface->material == oldmaterial) ElementUpdateMaterial(TOPLEVEL_ELEMENT(p), oldrho, newrho, olde, newe); } EndForAll; if (gal.clustered) ClusterUpdateMaterial(gal.top_cluster); #endif }