src/rcrt/gi/PhotonMap.cpp

Go to the documentation of this file.
00001 #include "PhotonMap.h"
00002 #include <boost/thread/thread.hpp>
00003 #include <boost/bind.hpp>
00004 #include "../Ray.h"
00005 #include "../materials/Material.h"
00006 
00007 using namespace std;
00008 
00009 namespace rcrt
00010 {
00011 
00012 PhotonMap::PhotonMap(Scene* s, const float& maxRad, const int& maxB, const float& flatten):
00013         scene(s),globalTree(maxRad * maxRad), causticTree(maxRad * maxRad),
00014         maxRadius(maxRad), maxBounces(maxB), currentRadius(maxRad / 8.0f),flat(flatten)
00015 {
00016 }
00017 
00018 PhotonMap::~PhotonMap()
00019 {
00020         for(unsigned int i = 0; i < photons.size(); i++)
00021                 delete photons[i];
00022 }
00023 
00024 void PhotonMap::generate(const int& noPhotons)
00025 {
00026         photons.reserve(noPhotons);
00027         caustics.reserve(noPhotons/4);
00028         vector<Light*>* lights = scene->getLights();
00029         //compute how many photons to send from each light
00030         //send more from brighter lights
00031         int noPhotonsPL[lights->size()];
00032         float totalPower = 0;
00033         int phots = 0;
00034         for(unsigned int i = 0; i < lights->size(); i++){
00035                 totalPower += lights->at(i)->getPower().avg();
00036         }
00037         for(unsigned int i = 0; i < lights->size(); i++){
00038                 noPhotonsPL[i] = (int)floor((lights->at(i)->getPower().avg() / totalPower) * noPhotons);
00039                 phots += noPhotonsPL[i];
00040         }
00041         phots = noPhotons - phots;
00042         unsigned int i = 0;
00043         while(phots > 0){//distribute remaining photons
00044                 if(i >= lights->size())
00045                         i = 0;
00046                 noPhotonsPL[i]++;
00047                 phots--;
00048         }
00049         
00050         RGBColor totalPowerRem(0);
00051         
00052         //multi-threaded sending of photons 
00053         i = 0;
00054         //compute first light extra if there is an uneven number of lights
00055         if(lights->size() % 2 != 0){
00056                 vector<Photon*> caustics1;
00057                 caustics1.reserve(noPhotonsPL[0]/4);
00058                 vector<Photon*> photons1;
00059                 photons1.reserve(noPhotonsPL[0]);
00060                 int storedTotal = 0;
00061                 cout << "[PhotonMap] Sending "<<noPhotonsPL[0]<<" photons for light: " << 0 << endl;
00062                 boost::thread first(boost::bind(boost::mem_fn(&PhotonMap::generateJob), this, lights->at(0),
00063                                 &photons1, &caustics1, boost::cref(noPhotonsPL[0]), boost::ref(storedTotal)));  
00064                 i++;
00065                 first.join();
00066                 const float weight = 1.0f / float(storedTotal);
00067                 cout << "[PhotonMap]    Sent " << noPhotonsPL[0] << " photons, with weight " << weight << endl;
00068                 for(unsigned int k = 0; k < caustics1.size(); k++){
00069                         Photon* p = caustics1[k];
00070                         p->setWeight(weight);
00071                         caustics.push_back(p);
00072                         totalPowerRem = totalPowerRem + p->getPower();
00073                 }
00074                 for(unsigned int k = 0; k < photons1.size(); k++){
00075                         Photon* p = photons1[k];
00076                         p->setWeight(weight);
00077                         photons.push_back(photons1[k]);
00078                         //cout << "[PhotonMap] photon " << k << " p=" << p->getPower() << " b=" << p->getBounces() << endl;
00079                         totalPowerRem = totalPowerRem + p->getPower();
00080                 }
00081         }
00082         //work two threads & light sources at the same time
00083         for(; i < lights->size(); i += 2){
00084                 cout << "[PhotonMap] Sending "<< noPhotonsPL[i] << " photons for light: " << i << endl;
00085                 cout << "[PhotonMap] Sending "<< noPhotonsPL[i+1] << " photons for light: " << i+1 << endl;
00086                 vector<Photon*> caustics1;//reserve some space for caustic photons
00087                 caustics1.reserve(noPhotonsPL[i]/4);
00088                 vector<Photon*> caustics2;
00089                 caustics2.reserve(noPhotonsPL[i+1]/4);
00090                 vector<Photon*> photons1;
00091                 photons1.reserve(noPhotonsPL[i]);
00092                 vector<Photon*> photons2;
00093                 photons2.reserve(noPhotonsPL[i+1]);
00094                 int storedTotal1 = 0;
00095                 int storedTotal2 = 0;
00096                 
00097                 boost::thread first(boost::bind(boost::mem_fn(&PhotonMap::generateJob), this, lights->at(i),
00098                                 &photons1, &caustics1, boost::cref(noPhotonsPL[i]), boost::ref(storedTotal1)));
00099                 boost::thread second(boost::bind(boost::mem_fn(&PhotonMap::generateJob), this, lights->at(i+1),
00100                                 &photons2, &caustics2, boost::cref(noPhotonsPL[i+1]), boost::ref(storedTotal2)));
00101                 
00102                 first.join();
00103                 const float weight1 = 1.0f / float(storedTotal1);
00104                 cout << "[PhotonMap]    Sent " << noPhotonsPL[i] << " photons with weight "<< weight1 << endl;
00105                 second.join();
00106                 const float weight2 = 1.0f / float(storedTotal2);
00107                 cout << "[PhotonMap]    Sent " << noPhotonsPL[i+1] << " photons with weight "<< weight2 << endl;
00108                 for(unsigned int k = 0; k < caustics1.size(); k++){
00109                         Photon* p = caustics1[k];
00110                         p->setWeight(weight1);
00111                         caustics.push_back(p);
00112                         totalPowerRem = totalPowerRem + p->getPower();
00113                 }
00114                 for(unsigned int k = 0; k < caustics2.size(); k++){
00115                         Photon* p = caustics2[k];
00116                         p->setWeight(weight2);
00117                         caustics.push_back(p);
00118                         totalPowerRem = totalPowerRem + p->getPower();
00119                 }
00120                 for(unsigned int k = 0; k < photons1.size(); k++){
00121                         Photon* p = photons1[k];
00122                         p->setWeight(weight1);
00123                         photons.push_back(p);
00124                         totalPowerRem = totalPowerRem + p->getPower();
00125                 }
00126                 for(unsigned int k = 0; k < photons2.size(); k++){
00127                         Photon* p = photons2[k];
00128                         p->setWeight(weight2);
00129                         photons.push_back(p);
00130                         totalPowerRem = totalPowerRem + p->getPower();
00131                 }
00132         }
00133         //got all the photons that we want, generate the trees
00134         cout << "          building global photon kdtree of size "<< photons.size() << endl;
00135         globalTree.buildTree(&photons);
00136         cout << "          building caustic photon kdtree of size " << caustics.size() << endl;
00137         causticTree.buildTree(&caustics);
00138         cout << "     total power in the photon map: " << totalPowerRem << endl;
00139 }
00140 
00141 void PhotonMap::generateJob(Light* light, vector<Photon*>* photons, vector<Photon*>* caustics,
00142                 const int& noPhotons, int& storedTotal)
00143 {
00144         int storedGlobal = 0;
00145         int storedCaustic = 0;
00146         while(storedGlobal+storedCaustic < noPhotons){
00147                 //cout << " sending number "<< i << endl;
00148                 Photon* photon = new Photon();
00149                 //get photon from light
00150                 light->emitPhoton(photon);
00151                 //cout << "  emitted photon " << photon->getPos() << photon->getDir() << endl;
00152                 //trace photon
00153                 Ray photRay(photon->getPos(), photon->getDir());
00154                 Intersection is(scene->intersect(photRay));
00155                 //cout << "  traced photon ray" << endl;
00156                 if(!is.isValid()){
00157                         //does not hit anything
00158                         //get a new photon
00159                         delete photon;
00160                         continue;
00161                 } else {
00162                         complex<float> prevIOR(1,0);
00163                         is.setLastIOR(prevIOR);
00164                         Material* prevMat = is.getPrimitive()->getMaterial();
00165                         RGBColor prevPower = photon->getPower();
00166                         Vec3D prevIncDir = photon->getDir();
00167                         bool prevBackSide = is.backSide();
00168                         ScatterEvent scEvent = prevMat->scatterPhoton(is, photon);
00169                         ScatterEvent lastScEvent = scEvent;
00170                         if(scEvent == INVALID || scEvent == STORE){
00171                                 delete photon;
00172                                 continue;
00173                         }
00174                         //photon->addBounce();
00175                         bool causticPath = false;
00176                         
00177                         while(scEvent != INVALID
00178                                         && scEvent != STORE){
00179                                 if(photon->getBounces() == maxBounces){
00180                                         scEvent = STORE;
00181                                         break;
00182                                 }
00183                                 //store a photon at the current intersection
00184                                 if(photon->getBounces() > 2 && scEvent == DIFFUSE) {
00185                                         if(causticPath){
00186                                                 caustics->push_back(
00187                                                         new Photon(prevPower,
00188                                                                         prevIncDir, photon->getPos(), photon->getBounces()));
00189                                                 storedCaustic++;                                                
00190                                         } else {
00191                                                 photons->push_back(
00192                                                         new Photon(prevPower,
00193                                                                         prevIncDir, photon->getPos(), photon->getBounces()));
00194                                                 
00195                                                 storedGlobal++;
00196                                         }
00197                                 }
00198                                 
00199                                 causticPath = causticPath || scEvent == SPECULAR || scEvent == REFRACTED;
00200                                 
00201                                 Ray nextRay = Ray(photon->getPos(), photon->getDir());
00202                                 is = scene->intersect(photRay);
00203                                 if(!is.isValid()){//terminate path
00204                                         scEvent = INVALID;
00205                                         break;
00206                                 }
00207                                 /*if(prevMat->refracts() && !prevBackSide){
00208                                         //we traveled through a refracting medium to this point
00209                                         //attenuate the power
00210                                         const RGBColor& absorb(prevMat->getAbsorbance());
00211                                         const float& dist(is.getDistance());
00212                                         photon->scalePower(RGBColor(exp(-dist * absorb.r()),
00213                                                         exp(-dist * absorb.g()),
00214                                                         exp(-dist * absorb.b())));
00215                                 }
00216                                 prevBackSide = is.backSide();*/
00217                                 if(lastScEvent == REFRACTED)
00218                                         is.setLastIOR(prevMat->getIOR());
00219                                 else
00220                                         is.setLastIOR(complex<float>(1,0));
00221                                 prevMat = is.getPrimitive()->getMaterial();
00222                                 photRay = nextRay;
00223                                 prevPower = photon->getPower();
00224                                 prevIncDir = photon->getDir();
00225                                 lastScEvent = scEvent;
00226                                 scEvent = prevMat->scatterPhoton(is, photon);
00227                                 photon->addBounce();
00228                                 //don't follow photons that are too dark
00229                                 if(photon->getPower().sum()-0.001f <= numeric_limits<float>::epsilon()){
00230                                         scEvent = INVALID;
00231                                         break;
00232                                 }
00233                         }
00234                         if(scEvent == INVALID){
00235                                 // we hit something useless / the path has terminated
00236                                 // without getting absorbed or 
00237                                 // store with previous values (incoming)
00238                                 photon->setDir(prevIncDir);
00239                                 photon->setPower(prevPower);
00240                                 //store the photon
00241                                 photons->push_back(photon);
00242                                 storedGlobal++;
00243                         } else {
00244                                 photons->push_back(photon);
00245                                 storedGlobal++;
00246                         }                       
00247                 }
00248         }
00249         
00250         storedTotal = storedGlobal + storedCaustic;
00251 }
00252 
00253 RGBColor PhotonMap::getRadiance(const Vec3D& wOut,
00254                 const int& noGPhotons, const int& noCPhotons, Intersection& is)
00255 {       
00256         Material* mat = is.getPrimitive()->getMaterial();
00257         Vec3D normal(mat->getShadingNormal(is));
00258         const Point3D& pos(is.getPosition());   
00259         
00260         //float currRSqr = currentRadius*currentRadius;
00261         float currRSqr = maxRadius*maxRadius;
00262         //bool tooFew = false;
00263         std::vector<PKDSearchP> global;
00264         if(noGPhotons != 0) globalTree.getKNearest(&global, noGPhotons, pos, normal, currRSqr, flat);
00265         
00266         std::vector<PKDSearchP> caustic;
00267         if(noCPhotons != 0) causticTree.getKNearest(&caustic, noCPhotons, pos, normal, currRSqr/2, flat);
00268         
00269         static const float gauss_norm = 1.8790;
00270         static const float alpha = 0.918 * gauss_norm;
00271         static const float beta = 1.953;
00272         static const float divG = 1.0f / (1.0 - exp(-beta));
00273         
00274         //cout << "hurz" << endl;
00275         float maxGR = -numeric_limits<float>::infinity();
00276         
00277         for(unsigned int i = 0; i < global.size(); i++){
00278                 const float& distSqr(global[i].distSqr);
00279                 //cout << "distSqr " << distSqr << endl;
00280                 if(distSqr > maxGR) maxGR = distSqr;            
00281         }
00282         
00283         RGBColor globalR(0);
00284         for(unsigned int i = 0; i < global.size(); i++){
00285                 const float& distSqr(global[i].distSqr);
00286                 const float gauss = alpha * (1.0 - (1.0 - exp( -beta * distSqr / (M_PI * maxGR) )) * divG);
00287                 Photon* p = global[i].p;
00288                 globalR = globalR + p->getPower() * fabs(normal * p->getDir())
00289                                         * p->getWeight() * mat->sample(wOut, p->getDir(), is)
00290                                         * gauss;
00291         }
00292         
00293         float maxCR = -numeric_limits<float>::infinity();
00294         
00295         for(unsigned int i = 0; i < caustic.size(); i++){
00296                 const float& distSqr(caustic[i].distSqr);
00297                 if(distSqr > maxCR) maxCR = distSqr;
00298         }
00299         
00300         RGBColor causticR(0);
00301         for(unsigned int i = 0; i < caustic.size(); i++){
00302                 const float& distSqr(caustic[i].distSqr);
00303                 const float gauss = alpha * (1.0 - (1.0 - exp( -beta * distSqr / (M_PI * maxCR) )) * divG);
00304                 Photon* p = caustic[i].p;
00305                 causticR = causticR + p->getPower() * fabs(normal * p->getDir())
00306                                         * p->getWeight() * mat->sample(wOut, p->getDir(), is)
00307                                         * gauss;
00308         }
00309         //cout << globalR << causticR << endl;
00310         return globalR  + causticR;
00311 }
00312 
00313 const float& PhotonMap::getMaxRadius() const
00314 {
00315         return maxRadius;
00316 }
00317 
00318 }

Generated on Thu Jan 31 19:26:19 2008 for RenderingCompetitionRayTracer by  doxygen 1.5.3