Radiosity.cc

Go to the documentation of this file.
00001 #include <gra/reflection/Radiosity.h>
00002 #include <esg/explorer/ShadowExplorer.h>
00003 #include <esg/explorer/ObjsExplorer.h>
00004 #include <assert.h>
00005 
00006 using namespace gra;
00007 
00009 
00010 Radiosity::Patch::Patch(PolygonalEnergy * e,
00011                         Mesh::Plane     * p,
00012                         const Vector3   & re,
00013                         const Vector3   & em,
00014                         const Vector3   & c,
00015                         float             a)
00016     : pEnergy(e),
00017       pMeshPlane(p),
00018       reflectance(re),
00019       emittance(em),
00020       normal(p->a, p->b, p->c),
00021       centre(c),
00022       area(a)
00023 {
00024 }
00025 
00026 
00028 
00029 float Radiosity::_edge_area(const Vector3& p,const Vector3& q,const Vector3& n)
00030 { 
00031     /*
00032      * Numerically stable edge-area calculator. (Including degenerate cases.)
00033      * Andrew J. Willmott, 1992
00034      *
00035      * 4 dots, 1 cross, 1 atan per edge.
00036      */
00037 
00038     const float epsilon = 1e-6;
00039     float sinFactor, qdotp, projArea;
00040     Vector3 aux;
00041 
00042     qdotp =  p.dot(q);
00043     sinFactor = p.lengthSquared() * q.lengthSquared() - Math::sqr(qdotp);
00044 
00045     aux.cross(p,q);
00046     aux.negate();
00047     projArea = aux.dot(n);
00048 
00049     if (sinFactor > 0) {
00050         // If SinFactor > 0 we may apply the formula without problem 
00051         sinFactor = sqrt(sinFactor);
00052         return(projArea * (PI/2.0 - atan(qdotp / sinFactor)) / 
00053                (2 * PI * sinFactor));
00054     } else if (qdotp > epsilon) // If not, we have three remaining cases... 
00055         return(0);     // CASE 1: p and q point in the same direction }
00056     else if (qdotp < -epsilon)
00057         return(0.5);   // CASE 2: Viewpoint lies on the edge }
00058     else
00059         return(0.125); // CASE 3: Viewpoint lies at either end of the edge }
00060 }
00061 
00062 float Radiosity::_est_patch_factor(Patch&         patch,
00063                                    const Vector3& point,
00064                                    const Vector3& normal)
00065 {
00066     /*
00067      * Elemental factor from the patch "patch" to point
00068      * essentially cos theta * cos phi * Ap / (pi * r^2)
00069      */
00070 
00071     float   result, rad;
00072     Vector3 temp(point);
00073 
00074     temp.sub(patch.centre);
00075 
00076     result = (patch.normal.x * temp.x +
00077               patch.normal.y * temp.y +
00078               patch.normal.z * temp.z);
00079         
00080     if (result <= 0) result = 0;
00081     else {
00082         result *= -normal.dot(temp);
00083         if (result <= 0) result = 0;
00084         else {
00085             rad = Math::sqr(temp.lengthSquared());
00086             result *= patch.area;
00087             result /= (rad * PI); 
00088         }
00089     }
00090         
00091     return result;
00092 }
00093 
00094 float Radiosity::_patch_factor(Patch&         patch,
00095                                const Vector3& point,
00096                                const Vector3& normal)
00097 {
00098     /*
00099      * Common terminology for this is the polygon-to-point form factor. 
00100      * The 'form-factor' mentioned in radiosity papers is the average
00101      * patch factor across a patch.
00102      */
00103 
00104     assert(patch.pMeshPlane);
00105 
00106     Vector3 x1, x2, x3;
00107     float   sum     = 0.0;
00108     bool    isFirst = true;
00109     
00110     Mesh::Edge * pE = patch.pMeshPlane->any_edge; assert(pE);
00111     do {
00112         // x2 = first vertex of the actual edge
00113         if (pE->LeftLoop == patch.pMeshPlane)
00114             x2.set(pE->V1->x, pE->V1->y, pE->V1->z);
00115         else
00116             x2.set(pE->V2->x, pE->V2->y, pE->V2->z);
00117         x2.sub(point);
00118 
00119         if (isFirst) { // remember the first vertex
00120             x3.set(x2); 
00121             isFirst = false;
00122         } else 
00123             sum += _edge_area(x1, x2, normal);
00124             
00125         x1.set(x2);  // x1 becames the first vertex of next edge
00126         
00127         // go to next the edge in loop
00128         if (pE->LeftLoop == patch.pMeshPlane) pE = pE->LeftOut;
00129         else                                  pE = pE->RightOut;
00130     } while (pE != patch.pMeshPlane->any_edge);
00131 
00132     sum += _edge_area(x1, x3, normal);  // end loop
00133 
00134     if (sum > 0.0) return sum;
00135     else           return 0.0;
00136 }
00137 
00138 #define _MAX(a,b) (((a)<(b))?(b):(a))
00139 float Radiosity::_est_form_factor(Patch& from, Patch& to)
00140 {
00141     float result, rad, npt, res2;
00142     Vector3 temp(to.centre);
00143 
00144     temp.sub(from.centre);
00145     
00146     result = -(to.normal.x*temp.x + to.normal.y*temp.y + to.normal.z*temp.z);
00147     if (result <= 0.0) return 0.0;
00148         
00149     npt = (from.normal.x*temp.x + from.normal.y*temp.y + from.normal.z*temp.z);
00150     if (npt <= 0.0) return 0.0;
00151     
00152     rad = Math::sqr(temp.lengthSquared());
00153     result *= from.area / PI;
00154     res2 = result * _MAX(npt, 0.25 * sqrt(from.area));
00155 
00156     if (_quadLevel == 0 || rad * _dFError >= res2) return result*(npt/rad);
00157     
00158     // result is blowing up!
00159     // let's bail to the patch factor formula
00160 
00161     Vector3 v;
00162     float   sum  = 0.0;
00163     int     numV = 0;
00164     
00165     if (_quadLevel > 1) {
00166         // use more-accurate PF estimate:
00167         // sample corners
00168         Mesh::Edge * pE = to.pMeshPlane->any_edge; assert(pE);
00169         do {
00170             if (pE->LeftLoop == to.pMeshPlane)
00171                 v.set(pE->V1->x, pE->V1->y, pE->V1->z);
00172             else
00173                 v.set(pE->V2->x, pE->V2->y, pE->V2->z);
00174             sum += _patch_factor(from, v, to.normal);
00175             numV++;
00176             // go to next the edge in loop
00177             if (pE->LeftLoop == to.pMeshPlane) pE = pE->LeftOut;
00178             else                               pE = pE->RightOut;
00179         } while (pE != to.pMeshPlane->any_edge);
00180         
00181         if (_quadLevel > 2) {
00182             // estimate ff from tetrahedron constructed from
00183             // corner samples and centre. this will
00184             // underestimate the true ff slightly.
00185             sum += 2.0 * _patch_factor(from, to.centre, to.normal);
00186             sum /= numV + 2;
00187         } else // underestimate curve
00188             sum /= numV;
00189     } else
00190         sum = _patch_factor(from, to.centre, to.normal);
00191         
00192     return sum;
00193 }
00194 #undef _MAX
00195 
00196 void Radiosity::_make_form_factors(Patch& fromPatch, Vector3 result[])
00197 {
00198     float rho, factor, visibility;
00199     int   i = 0;
00200 
00201     for (Patch * curPatch = _scenePatches.firstItem();
00202          curPatch;
00203          curPatch = _scenePatches.nextItem())
00204     {
00205         rho = curPatch->reflectance.length();
00206         if (rho < 0.0001) { result[i++].set(0,0,0); continue; }
00207 
00208         factor = _est_form_factor(fromPatch, *curPatch);
00209         
00210 #ifdef RAD_SPEEDY
00211         if (rho * factor < 1e-5) { result[i++].set(0,0,0); continue; }
00212 #endif
00213 
00214         visibility = _visibility(fromPatch, *curPatch);
00215                 
00216         if (visibility > 0.0001) {
00217             result[i].set(curPatch->reflectance);
00218             result[i++].scale(factor * visibility);
00219         } else 
00220             result[i++].set(0,0,0);
00221     }
00222 }
00223 
00224 float Radiosity::_visibility(Patch& from, Patch& to)
00225 // Returns visibility of the centre of this patch from p.
00226 {
00227     float   d;
00228 
00229     // Is 'to.centre' completely behind this patch?
00230     d = from.normal.dot(from.centre);
00231     if (from.normal.dot(to.centre) <= d) return 0.0;
00232 
00233     // Is this patch completely behind 'to' plane?
00234     d = to.normal.dot(to.centre);
00235     if (from.centre.dot(to.normal) <= d) return 0.0;
00236 
00237     // If neither is the case, we have at least partial
00238     // visibility, so ray cast to estimate it
00239     assert(_pIntersector);
00240     
00241     Vector3 dir(to.centre);
00242     dir.sub(from.centre);
00243     float  maxDist = dir.length();
00244     dir.normalize();
00245     
00246     _pIntersector->init(ENV_WANT_INTERFERENCE, maxDist);
00247 #warning "FIX ME: Light OID"
00248     ShadowExplorer explorer(from.centre, dir, maxDist); 
00249     if (explorer.result()) return 0.0;
00250     else                   return 1.0;
00251 }
00252 
00253 
00254 // ------------- public --------------
00255 
00256 void Radiosity::setScene(Scene * pScene)
00257 {
00258     ReflectionModel::setScene(pScene);
00259     if (!_pScene || !_pScene->root()) return;
00260 
00261     ObjsExplorer          explorer;
00262     Matrix4               trMat;
00263     bool                  transformed;
00264     Mesh*                 pMesh;
00265     //EnergyCoat*           pE;
00266     PolygonalEnergy*      pE;
00267     AutoPtr<EnergyCoat> * pAE;
00268     AutoPtr<Mesh>       * pAMesh;
00269     Vector3               emission;
00270     Vector3               reflectance;
00271     MatVisitor            visitor;
00272 
00273     _scenePatches.deleteAll();
00274 
00275     explorer.explore(*(_pScene->root()));
00276     
00277     for (SceneGraphObject * pObj = explorer.result(trMat, transformed);
00278          pObj;
00279          pObj = explorer.result(trMat, transformed))
00280     {
00281         // inspect polygonal energy
00282         if (!pObj->supportsEnergy()) continue;
00283         
00284         pAE = pObj->getEnergyState();
00285         if (!pAE) continue;
00286 
00287         if (!pAE->origObject() ||
00288             pAE->origObject()->type() != EnergyCoat::POLYGONAL) continue;
00289         
00290         pE = (PolygonalEnergy*) pAE->origObject();
00291         if (pE->getRegionsDescr() != PolygonalEnergy::FACET_BASED) continue;
00292         
00293         pAMesh = pE->getMesh();
00294         if (!pAMesh) continue;
00295         
00296         pMesh = pAMesh->origObject();
00297         if (!pMesh) continue;
00298 
00299         // inspect diffuse coeficient replacing BRDF
00300         visitor.init();
00301         pObj->inspectMaterials(visitor);
00302         reflectance.set(visitor.diffuse());
00303         
00304         // initialize patches for current energy coat
00305         pMesh->resetActSolid();
00306         do {
00307             pMesh->resetActPlane();
00308             do {
00309                 if (!(pE->getRegionEnergy(pMesh->getActPlaneID(), emission)))
00310                     //emission.set(0., 0., 0.);
00311                     fprintf(stderr,"Radiosity::setScene(): Uninitilized facet energy or facet ID mismatch\n");
00312 
00313                 //fprintf(stderr,"%i: %f %f %f\n",pMesh->getActPlaneID(),emission.x,emission.y,emission.z);
00314                 _scenePatches.append(new Patch((PolygonalEnergy*) pE,
00315                                                pMesh->getActPlane(),
00316                                                reflectance,
00317                                                emission,
00318                                                pMesh->getActPlaneCentroid(),
00319                                                pMesh->getActPlaneArea()));
00320             } while (pMesh->goToNextPlane());
00321         } while (pMesh->goToNextSolid());
00322     }
00323 }
00324 
00325 Color3f* Radiosity::illuminatePoint(PointEnv&)
00326 {
00327     float    power, maxPower;
00328     Patch *  pMaxPowerPatch = _scenePatches.firstItem();
00329     Vector3  rgbToLum(1.0/3.0, 1.0/3.0, 1.0/3.0);
00330     Vector3  radToShoot;
00331     Vector3  envPower;
00332     Vector3  deltaRad;
00333     float    origShoot = 0;
00334     int      iterations = 0;
00335     unsigned i;
00336     Vector3  envRefl(0,0,0);
00337     float    envArea = 0.0;
00338     unsigned maxPowerIndex;
00339     float    error = 1.0;
00340     Vector3  envRad(1,1,1);
00341     bool     finished = false;
00342 
00343     if (!_pIntersector) return NULL;
00344 
00345     Vector3* ffRow = new Vector3 [_scenePatches.length()];
00346     Vector3* S     = new Vector3 [_scenePatches.length()];
00347 
00348     // setup the ambient term + emittance
00349     i = 0;
00350     for (Patch * curPatch = _scenePatches.firstItem();
00351          curPatch;
00352          curPatch = _scenePatches.nextItem())
00353     {
00354         envRefl.add(curPatch->reflectance * curPatch->area);
00355         envArea += curPatch->area;
00356         S[i].set(curPatch->emittance);
00357         i++;
00358     }
00359     // R = 1 / (1 - avgReflectance):
00360     envRefl.x /= 1 / (1 - envRefl.x / envArea);
00361     envRefl.y /= 1 / (1 - envRefl.y / envArea);
00362     envRefl.z /= 1 / (1 - envRefl.z / envArea);
00363 
00364     // distribute energy
00365     while (!finished) {
00366         maxPower = 0;
00367         envPower.set(0,0,0);
00368         maxPowerIndex = 0;
00369         pMaxPowerPatch = NULL;
00370 
00371         iterations++;
00372 
00373         // find patch with maximal energy
00374         i = 0;
00375         for (Patch * curPatch = _scenePatches.firstItem();
00376              curPatch;
00377              curPatch = _scenePatches.nextItem())
00378         {
00379             power = rgbToLum.dot(S[i] * curPatch->area);
00380             if (maxPower < power) {
00381                 maxPower = power;
00382                 pMaxPowerPatch = curPatch;
00383                 maxPowerIndex = i;
00384             }
00385             i++;
00386         }
00387 
00388         assert(pMaxPowerPatch);
00389 
00390         // if (idle()) return false;
00391 
00392         radToShoot.set(S[maxPowerIndex]);
00393         S[maxPowerIndex].set(0,0,0);
00394 
00395         _make_form_factors(*pMaxPowerPatch, ffRow);
00396 
00397         envPower.set(0,0,0);
00398         error = 0.0;
00399 
00400         // energy shooting
00401         i = 0;
00402         for (Patch * curPatch = _scenePatches.firstItem();
00403              curPatch;
00404              curPatch = _scenePatches.nextItem())
00405         {
00406             deltaRad.x = ffRow[i].x * radToShoot.x;
00407             deltaRad.y = ffRow[i].y * radToShoot.y;
00408             deltaRad.z = ffRow[i].z * radToShoot.z;
00409             curPatch->emittance.add(deltaRad);
00410             S[i].add(deltaRad);
00411             error += deltaRad.lengthSquared();
00412             envPower.add(S[i] * curPatch->area);
00413             i++;
00414         }
00415 
00416         if (_useAmbient) {
00417             envRad.x = envRefl.x * envPower.x / envArea;
00418             envRad.y = envRefl.y * envPower.y / envArea;
00419             envRad.z = envRefl.z * envPower.z / envArea;
00420         } else
00421             envRad.set(0,0,0);
00422 
00423         error = sqrt(error);
00424         
00425         if (iterations == 1) origShoot = maxPower;
00426         else
00427             if (maxPower <= 0.01 * origShoot) finished = true;
00428     } // while
00429 
00430 
00431     if (_useAmbient) {
00432         for (Patch * curPatch = _scenePatches.firstItem();
00433              curPatch;
00434              curPatch = _scenePatches.nextItem())
00435         {
00436             curPatch->emittance.x += curPatch->reflectance.x*envRad.x;
00437             curPatch->emittance.y += curPatch->reflectance.x*envRad.y;
00438             curPatch->emittance.z += curPatch->reflectance.x*envRad.z;
00439         }
00440     }
00441     
00442     // set energy of shapes
00443     for (Patch * curPatch = _scenePatches.firstItem();
00444          curPatch;
00445          curPatch = _scenePatches.nextItem())
00446     {
00447         //fprintf(stderr,"%i, %f %f %f\n",curPatch->pMeshPlane->jmeno_roviny,curPatch->emittance.x,curPatch->emittance.y,curPatch->emittance.z);
00448         curPatch->pEnergy->setRegionEnergy(curPatch->pMeshPlane->jmeno_roviny,
00449                                            curPatch->emittance);
00450     }
00451 
00452     delete [] ffRow;
00453     delete [] S;
00454 
00455     return NULL;
00456 }

Generated on Tue Nov 21 15:11:42 2006 for gra by  doxygen 1.4.6