BidirectionalPathTracer.cpp

Go to the documentation of this file.
00001 /**<!-------------------------------------------------------------------->
00002    @file   BidirectionalBidirectionalPathTracer.cpp
00003    @author Travis Fischer (fisch0920@gmail.com)
00004    @author Matthew Jacobs (jacobs.mh@gmail.com)
00005    @date   Fall 2008
00006 
00007    @brief
00008       Unbiased bidirectional path tracer with support for efficient direct 
00009    illumination
00010    <!-------------------------------------------------------------------->**/
00011 
00012 #include "BidirectionalPathTracer.h"
00013 #include <DirectIllumination.h>
00014 #include <RenderOutput.h>
00015 #include <SurfacePoint.h>
00016 #include <Material.h>
00017 #include <Viewport.h>
00018 #include <HDRImage.h>
00019 #include <Rgba.h>
00020 #include <Camera.h>
00021 #include <Scene.h>
00022 #include <QtCore>
00023 #include <Path.h>
00024 #include <Ray.h>
00025 #include <sstream>
00026 
00027 //#define DEBUG_PATHS
00028 
00029 /**
00030  * Notes:
00031  *    k = 2:
00032  *       only contribution is from directly visible lights (eye -> light)
00033  *       other paths won't match up with the incident direction (other vertex -> eye)
00034  *    k = 3:
00035  *       
00036  */
00037 
00038 #if 1
00039 
00040 void BidirectionalPathTracer::sample(PointSample &sample) {
00041    Path path(this);
00042    Path eye(this);
00043    
00044    /*if (m_images == NULL) {
00045       m_images = new HDRImage*[10];
00046       m_filters = new ProgressiveFilterValue<SpectralSampleSet>*[10];
00047       for(unsigned i = 10; i--;) {
00048          m_images[i] = new HDRImage(480, 480);
00049          m_filters[i] = new ProgressiveFilterValue<SpectralSampleSet>[480 * 480];
00050       }
00051    }*/
00052    
00053    { // initialize first vertex to current samplepoint on camera's lens
00054       SurfacePoint *pt = new SurfacePoint();
00055       const real_t pA = 1.0; // 'sampling' uniformly on film plane
00056       
00057       m_camera->getPoint(*pt, UV(sample.position));
00058       eye.prepend(PathVertex(pt, 1, pA));
00059    }
00060    
00061    bool debug = 
00062 #ifdef DEBUG_PATHS
00063       true;
00064 #else
00065    // 240, 71  = direct k = 2 from light source
00066    // 240, 200 = back wall
00067    // 241, 361 = lower back wall
00068       ((unsigned)(sample.position[0] * m_output->getViewport().getWidth()) == 240 && 
00069        (unsigned)(sample.position[1] * m_output->getViewport().getHeight()) == 360);
00070 #endif
00071    
00072    if (debug) {
00073       cerr << "eye" << endl;
00074       cerr << "----------------------------------------------------------------" << endl;
00075    }
00076    
00077    generateE(eye, debug);
00078    
00079    if (debug)
00080       cerr << eye << endl;
00081    
00082    if (!eye.front().pt->emitter->isEmitter()) {
00083       if (debug) {
00084          cerr << "light" << endl;
00085          cerr << "----------------------------------------------------------------" << endl;
00086       }
00087       
00088       generateL(path);
00089    }
00090    
00091    if (debug)
00092       cerr << path << endl;
00093    
00094    const unsigned actualS = path.length();
00095    bool valid = path.append(eye);
00096    
00097    if (debug) {
00098       cerr << "complete: " << (valid ? "valid" : "invalid") << endl;
00099       cerr << "----------------------------------------------------------------" << endl;
00100       cerr << path << endl << endl;
00101    }
00102    
00103    const unsigned length = path.length() - (!valid);
00104    const unsigned N = (((length + 1) * (length + 2)) / 2) - 3;
00105    real_t *pdfs = new real_t[N];
00106    real_t *sums = new real_t[length];
00107    unsigned n   = 0;
00108    SpectralSampleSet L;
00109    
00110    // calculate densities of all possible combinations of light and eye 
00111    // subpaths for use with multiple importance sampling
00112    for(unsigned k = 2; k <= length; ++k) {
00113       sums[k - 1] = 0;
00114       
00115       real_t *pdf = pdfs + n;
00116       //path.getPdfs(k, actualS, pdf);
00117       
00118       // s ranges from 0 up to k; t ranges from k down to 0 (all inclusive)
00119       for(unsigned t = k + 1, s = 0; t--; ++s, ++n) {
00120          ASSERT(k == s + t);
00121          
00122          pdf[s] = path.getPdf(s, t, false);
00123          ASSERT(pdf[s] >= 0);
00124          
00125          // power heuristic with beta=2 for multiple importance sampling
00126          pdf[s] *= pdf[s];
00127          
00128          if (debug) {
00129             cerr << "k = " << k << " (" << s << ", " << t << ") { c = " << path.getContribution(s, t) << ", p = " << pdf[s] << " }" << endl;
00130          }
00131          
00132          sums[k - 1] += pdf[s];
00133       }
00134    }
00135    
00136    if (debug)
00137       cerr << endl << endl;
00138    
00139    ASSERT(n == N);
00140    
00141    if (debug) 
00142       cerr << endl << path << endl;
00143    
00144    // add weighted contributions from all possible combinations of light and 
00145    // eye subpaths using multiple importance sampling
00146    for(unsigned k = 2, n = 0; k <= length; ++k) {
00147       if (sums[k - 1] == 0)
00148          continue;
00149       
00150       const real_t *pdf = pdfs + n;
00151       
00152       // s ranges from 0 up to k; t ranges from k down to 0 (all inclusive)
00153       for(unsigned t = k + 1, s = 0; t--; ++s, ++n) {
00154          ASSERT(k == s + t);
00155          
00156          /*if (debug) {
00157             const real_t weight = pdf[s] / sums[k - 1];
00158             const SpectralSampleSet &c = path.getContribution(s, t);
00159             
00160             cerr << "k = " << k << " (" << s << ", " << t << ") { C = " << path.getContribution(s, t) << ", p = " << pdf[s] << ", C* = " << weight * c << " }" << endl;
00161          }*/
00162          
00163          // TODO: russian roulette on 'w' if it's really small to avoid extra 
00164          // intersection test (except for edge cases where intersection test 
00165          // isn't needed; eg, s=0,t=0,and k=length
00166          if (pdf[s] > 0) {
00167             const real_t weight = (pdf[s]) / sums[k - 1];
00168             const SpectralSampleSet &c = path.getContribution(s, t);
00169             
00170             /*if (k < 10) {
00171                unsigned x, y;
00172                m_output->getViewport().getBin(sample.position, x, y);
00173                
00174                m_filters[k][y * 480 + x].addSample(c, weight);
00175                m_images[k]->setPixel<RgbaHDR>(y, x, m_filters[k][y * 480 + x].getValue().getRGB());
00176             }*/
00177             
00178             L += weight * c;
00179          }
00180       }
00181    }
00182    
00183    safeDeleteArray(pdfs);
00184    safeDeleteArray(sums);
00185    sample.value.setValue(L);
00186    
00187    if (debug)
00188       cerr << "total: " << L << endl;
00189 }
00190 
00191 #endif
00192 
00193 void BidirectionalPathTracer::finalize() {
00194    /*for(unsigned i = 10; i--;) {
00195       stringstream s;
00196       s << "out" << i << ".png";
00197       const std::string &f = s.str();
00198       cerr << "saving " << f << endl;
00199       m_images[i]->save(f);
00200    }*/
00201 }
00202 
00203 bool BidirectionalPathTracer::generate(Path &light) {
00204    Path eye(this);
00205    
00206    generateL(light);
00207    generateE(eye);
00208    
00209    return (light.append(eye));
00210 }
00211 
00212 void BidirectionalPathTracer::generateL(Path &light) {
00213    
00214    do {
00215       const bool roulette = (light.length() > 0);
00216       
00217       if (!light.append(roulette))
00218          break;
00219    } while(1);
00220    
00221    ASSERT(light.length() > 0);
00222 }
00223 
00224 void BidirectionalPathTracer::generateE(Path &eye) {
00225    generateE(eye, false);
00226 }
00227 
00228 void BidirectionalPathTracer::generateE(Path &eye, bool debug) {
00229    do {
00230       const bool roulette = (eye.length() > 2);
00231       
00232       if (debug)
00233          cerr << eye << endl << endl;
00234       
00235       if (!eye.prepend(roulette))
00236          break;
00237       
00238       if (eye.front().pt->emitter->isEmitter())
00239          break;
00240    } while(1);
00241    
00242    ASSERT(eye.length() > 0);
00243 }
00244 
00245 #if 0
00246 
00247 void BidirectionalPathTracer::sample(PointSample &sample) {
00248    Path eye(this);
00249    
00250    { // initialize first vertex to current samplepoint on camera's lens
00251       SurfacePoint *pt = new SurfacePoint();
00252       const real_t pA = 1.0; // 'sampling' uniformly on film plane
00253       
00254       m_camera->getPoint(*pt, UV(sample.position));
00255       eye.prepend(PathVertex(pt, pA));
00256    }
00257    
00258    real_t pCont = 1;
00259    SpectralSampleSet L;
00260    
00261    do {
00262       if (!eye.prepend())
00263          break;
00264       
00265       const PathVertex &z = eye[1];
00266       PathVertex &x       = eye[0];
00267       
00268       if (eye.length() == 1)//x.bsdf->isSpecular())
00269          L += x.pt->emitter->getLe(-x.wi) * z.alphaE;
00270       //else if (x.pt->emitter->isEmitter())
00271       //   break;
00272       
00273       // estimate direct illumination
00274       L += m_directIllumination->evaluate(*x.pt.get()) * z.alphaE;
00275       
00276       const SpectralSampleSet &fs = z.fs / z.pdfE;
00277       
00278       // russian roulette
00279       pCont = (eye.length() < 12 ? (fs != SpectralSampleSet::black()) : 
00280                MIN(0.95, fs[fs.getMaxFrequency()]));
00281       
00282       if (pCont < Random::sample(0, 1))
00283          break;
00284       
00285       x.alphaE /= pCont;
00286    } while(1);
00287    
00288    sample.value.setValue(L);
00289    
00290 /*  // bidirectional path contributions
00291    Path light(this);
00292    pCont = 1;
00293    
00294    bool ret = light.append();
00295    if (!ret) {
00296       ASSERT(0 && "sampling initial location on light source failed");
00297       return;
00298    }
00299    
00300    do {
00301       if (!light.append())
00302          break;
00303       
00304       const PathVertex &y = light[light.length() - 2];
00305       PathVertex &x       = light[light.length() - 1];
00306       
00307       // TODO: estimate direct contribution to film plane
00308       const SpectralSampleSet &fs = y.fs / y.pdfL;
00309       
00310       // russian roulette
00311       pCont = (light.length() < 3 ? (fs != SpectralSampleSet::black()) : 
00312                MIN(1, fs[fs.getMaxFrequency()]));
00313       
00314       if (pCont < Random::sample(0, 1))
00315          break;
00316       
00317       x.alphaL /= pCont;
00318    } while(1);
00319    
00320    if (!light.append(eye))
00321       return;
00322    
00323    // note: overall, n = (((length + 1) * (length + 2)) / 2) - 1
00324    unsigned n = 1;
00325    
00326    // add contributions from all possible combinations of light and eye subpaths
00327    // TODO: multiple importance sampling to weight individual contributions
00328    for(unsigned k = 1; k <= light.length(); ++k) {
00329       unsigned s = 0;
00330       
00331       // s ranges from 0 up to k; t ranges from k down to 0
00332       for(unsigned t = k + 1; t--; ++s, ++n) {
00333          ASSERT(k == s + t);
00334          
00335          L += light.getContribution(s, t);
00336       }
00337    }
00338    
00339    sample.value.setValue(L / n);*/
00340 }
00341 #endif
00342 

Generated on 28 Feb 2009 for Milton by doxygen 1.5.6