Path.cpp

Go to the documentation of this file.
00001 /**<!-------------------------------------------------------------------->
00002    @file   Path.cpp
00003    @author Travis Fischer (fisch0920@gmail.com)
00004    @author Matthew Jacobs (jacobs.mh@gmail.com)
00005    @date   Fall 2008
00006    
00007    @brief
00008       Core data structure for manipulating a sequence x0,x1,...,xk of points on 
00009    scene surfaces. Paths are the central unit in the path integral formulation
00010    of light transport, upon which path tracing, bidirectional path tracing, and
00011    MLT are all founded.
00012    
00013    Each Path is assumed to either start at an emitter (light subpath), end at a 
00014    sensor (eye subpath), or both start at an emitter and end at a sensor 
00015    (complete, valid path).
00016    <!-------------------------------------------------------------------->**/
00017 
00018 #include <Path.h>
00019 #include <Renderer.h>
00020 #include <Camera.h>
00021 #include <Scene.h>
00022 
00023 SpectralSampleSet Path::getContribution(unsigned s, unsigned t, bool tentative) {
00024    // note s and t are exclusive; u and v are inclusive
00025    const unsigned n = length();
00026    const unsigned u = s - 1; // index of last light subpath vertex
00027    const unsigned v = n - t; // index of last eye   subpath vertex
00028    ASSERT(s + t <= n && n >= 2);
00029    
00030    if (0 == s) {            // empty light subpath
00031       // point lights cannot contribute to 0-length light paths because it is 
00032       // impossible for a point light to be intersected by random eye paths
00033       if (m_vertices[v].pt->shape->getSurfaceArea() < EPSILON)
00034          return SpectralSampleSet::black(); // point light
00035       
00036       Vector3 wo = -m_vertices[v].wi;
00037       
00038       if (v < n - 1) {
00039          wo = (m_vertices[v + 1].pt->position - 
00040                m_vertices[v].pt->position).getNormalized();
00041       }
00042       
00043       return (
00044          m_vertices[v].pt->emitter->getLe(wo) * 
00045          m_vertices[v].alphaE
00046       );
00047    } else if (0 == t) {     // empty eye subpath
00048       // pinhole camera cannot contribute to 0-length eye paths because it is 
00049       // impossible for the pinhole to be intersected by random light paths
00050       if (m_vertices[u].pt->shape->getSurfaceArea() < EPSILON)
00051          return SpectralSampleSet::black(); // pinhole camera
00052       
00053       Vector3 wo = -m_vertices[u].wi;
00054       
00055       if (u > 0) {
00056          wo = (m_vertices[u - 1].pt->position - 
00057                m_vertices[u].pt->position).getNormalized();
00058       }
00059       
00060       return (
00061          m_vertices[u].alphaL * 
00062          m_vertices[u].pt->sensor->getWe(wo)
00063       );
00064    } else {
00065       ASSERT(u < v);
00066       PathVertex &y = m_vertices[u];
00067       PathVertex &z = m_vertices[v];
00068       
00069       // if either connecting vertex is specular, the contribution would be zero
00070       // with probability one (since the specular vertex' BSDF is non-zero over 
00071       // a set of directions with measure zero). we define the contribution of 
00072       // such connecting paths to be zero, thereby disregarding their 
00073       // theoretically zero chance of actually making a contribution
00074       if (y.bsdf->isSpecular() || z.bsdf->isSpecular())
00075          return SpectralSampleSet::black();
00076       
00077       if (s + t == n) {     // special case for full-length paths, where
00078                             // visibility is implicitly true and fs and G 
00079                             // are already known
00080          return (
00081             y.alphaL * 
00082             y.fs * y.GL * z.fs * 
00083             z.alphaE
00084          );
00085       } else {              // general case (visibility check needed)
00086          Vector3 wo = (z.pt->position - y.pt->position);
00087          real_t t   = wo.normalize();
00088          
00089          /*if (!tentative &&  // ensure visibility between y and z
00090              m_renderer->getScene()->intersects(Ray(y.pt->position, wo), t))
00091          {
00092             return SpectralSampleSet::black();
00093          }*/
00094          
00095          if (u > 0) {
00096             y.bsdf->setWi(
00097                (y.pt->position - m_vertices[u - 1].pt->position).getNormalized());
00098          }
00099          
00100          Vector3 wo2 = -wo;
00101          if (v < n - 1)
00102             wo2 = (m_vertices[v + 1].pt->position - z.pt->position).getNormalized();
00103          
00104          z.bsdf->setWi(wo);
00105          
00106          real_t G = ABS(y.pt->normal.dot(wo) * z.pt->normal.dot(-wo)) / (t * t);
00107          
00108          return (
00109             y.alphaL * 
00110             y.bsdf->getBSDF(wo) * G * z.bsdf->getBSDF(wo2) * 
00111             z.alphaE
00112          );
00113       }
00114    }
00115 }
00116 
00117 real_t Path::getPdf(unsigned s, unsigned t, bool tentative) {
00118    // note s and t are exclusive; u and v are inclusive
00119    const unsigned n = length();
00120    const unsigned u = s - 1; // index of last light subpath vertex
00121    const unsigned v = n - t; // index of last eye   subpath vertex
00122    ASSERT(s + t <= n && n >= 2);
00123    
00124    if (0 == s) {            // empty light subpath
00125       // point lights cannot contribute to 0-length light paths because it is 
00126       // impossible for a point light to be intersected by random eye paths
00127       if (m_vertices[v].pt->shape->getSurfaceArea() < EPSILON)
00128          return 0;
00129       
00130       return m_vertices[v].pE;
00131    } else if (0 == t) {     // empty eye subpath
00132       // pinhole camera cannot contribute to 0-length eye paths because it is 
00133       // impossible for the pinhole to be intersected by random light paths
00134       if (m_vertices[u].pt->shape->getSurfaceArea() < EPSILON)
00135          return 0;
00136       
00137       return m_vertices[u].pL;
00138    } else {
00139       ASSERT(u < v);
00140       PathVertex &y = m_vertices[u];
00141       PathVertex &z = m_vertices[v];
00142       
00143       // if either connecting vertex is specular, the contribution would be zero
00144       // with probability one (since the specular vertex' BSDF is non-zero over 
00145       // a set of directions with measure zero). we define the contribution of 
00146       // such connecting paths to be zero, thereby disregarding their 
00147       // theoretically zero chance of actually making a contribution
00148       if (y.bsdf->isSpecular() || z.bsdf->isSpecular())
00149          return 0;
00150       
00151       if (s + t == n) {     // special case for full-length paths, where
00152                             // visibility is implicitly true and fs and G 
00153                             // are already known
00154          return y.pL * z.pE;
00155       } else {              // general case (visibility check needed)
00156          Vector3 wo = (z.pt->position - y.pt->position);
00157          real_t t   = wo.normalize();
00158          
00159          if (!tentative && // ensure visibility between y and z
00160              m_renderer->getScene()->intersects(Ray(y.pt->position, wo), t))
00161             return 0;
00162          
00163          return y.pL * z.pE;
00164       }
00165    }
00166 }
00167 
00168 static inline unsigned P_INDEX(unsigned i, bool adjoint, unsigned n, unsigned k) {
00169    ASSERT(i < k);
00170    
00171    // index of ith vertex in subpath of length k of original path of length n, 
00172    // with the ith vertex chosen from the eye if adjoint or from the light if 
00173    // adjoitn is false
00174    return (adjoint ? (n - k + i) : i);
00175 }
00176 
00177 // note: computing relative pdfs of each of the k + 1 possible ways to generate
00178 // a path with k vertices allows us to disregard the connecting edge and its 
00179 // associated pdf by assuming it is 1 and computing everything else relative to 
00180 // that.
00181 void Path::getPdfs(unsigned k, unsigned s, real_t *pdfs) {
00182    const unsigned n = length();
00183    ASSERT(s <= n && k <= n);
00184    
00185    unsigned t = n - s;
00186    s = MIN(k - t, k);
00187    // TODO: what to set pdf[s] = 1??
00188    // TODO: with scene test.js (simple renderWarmup scene), get basically black if 
00189    // we don't start with pdf = 1 from eye's point-of-view -- incorrect!
00190    
00191    /*for(unsigned i = 2; i <= n; ++i) {
00192       cerr << "k = " << i << ", n = " << n << " (s = " << s << ")" << endl;
00193       cerr << "----------------------------------------------------" << endl;
00194       
00195       for(unsigned j = 0; j < i; ++j)
00196          cerr << j << "\t";
00197       cerr << endl;
00198       
00199       for(unsigned j = 0; j < i; ++j)
00200          cerr << P_INDEX(j, false, n, i) << "\t";
00201       cerr << endl;
00202       for(unsigned j = 0; j < i; ++j)
00203          cerr << P_INDEX(j, true,  n, i) << "\t";
00204       cerr << endl;
00205    }
00206    exit(0);*/
00207    
00208    bzero(pdfs, sizeof(real_t) * (k + 1));
00209    
00210    // pdfs[s] represents the probability density with which this path of length
00211    // k was actually generated; because we're combining pdfs with multiple 
00212    // importance sampling, we really only care about the relative probability 
00213    // densities (since each weight in multiple importance sampling is a ratio 
00214    // of densities -- their ratio is all that matters).  to make the 
00215    // computation (of all possible probability densities with which this 
00216    // k-length path could have been sampled) easier, we define p(s) to be one 
00217    // and compute all other probability densities relative to p(s) by using 
00218    // the quick / easy-to-compute ratios p(s+1)/p(s) and p(s+1)/p(s)
00219    pdfs[s] = 1;
00220    
00221    // p(i) is known; compute ratio p(i+1)/p(i) to determine p(s+1)...p(k)
00222    for(unsigned i = s; i < k; ++i) {
00223       real_t num = 1, den = 1;
00224       
00225       // p(i+1)/p(i) = p^l(i+1)/p^e(i+1)
00226       //             = p(xi+1 | xi, xi-1) / p(xi+1 | xi+2, xi+3)
00227       
00228       if (i == 0) {
00229          const PathVertex &y = m_vertices[P_INDEX(i + 0, false, n, k)];
00230          
00231          num = y.pL;
00232       } else {
00233          const PathVertex &y = m_vertices[P_INDEX(i - 1, false, n, k)];
00234          
00235          num = y.pdfL * y.GL;
00236       }
00237       
00238       if (i == k - 1) {
00239          const PathVertex &z = m_vertices[P_INDEX(i + 0, true,  n, k)];
00240          
00241          den = z.pE;
00242       } else {
00243          const PathVertex &z = m_vertices[P_INDEX(i + 1, true,  n, k)];
00244          
00245          den = z.pdfE * z.GE;
00246       }
00247       
00248       if (num == 0 || den == 0)
00249          break;
00250       
00251       ASSERT(i + 1 <= k);
00252       pdfs[i + 1] = pdfs[i] * (num / den);
00253    }
00254    
00255    // p(i) is known; compute ratio p(i-1)/p(i) to determine p(s-1)...p(0)
00256    for(unsigned i = s; i >= 1; --i) {
00257       real_t num = 1, den = 1;
00258       
00259       if (i == 1) {
00260          const PathVertex &y = m_vertices[P_INDEX(i - 1, false, n, k)];
00261          
00262          den = y.pL;
00263       } else {
00264          const PathVertex &y = m_vertices[P_INDEX(i - 1, false, n, k)];
00265          
00266          den = y.pdfL * y.GL;
00267       }
00268       
00269       if (i == k) {
00270          const PathVertex &z = m_vertices[P_INDEX(i - 1, true,  n, k)];
00271          
00272          num = z.pE;
00273       } else {
00274          const PathVertex &z = m_vertices[P_INDEX(i + 0, true,  n, k)];
00275          
00276          num = z.pdfE * z.GE;
00277       }
00278       
00279       if (num == 0 || den == 0)
00280          break;
00281       
00282       ASSERT(i - 1 <= k);
00283       pdfs[i - 1] = pdfs[i] * (num / den);
00284    }
00285    
00286    // handle specular vertices
00287    for(unsigned i = k; i--;) {
00288       const unsigned indexE = P_INDEX(i, true,  n, k);
00289       const unsigned indexL = P_INDEX(i, false, n, k);
00290       
00291       ASSERT(pdfs[i] >= 0);
00292       
00293       // any path with a connecting edge containing a specular vertex 
00294       // automatically has its contribution set to zero because these paths 
00295       // are impossible to deal with numerically (dirac BSDFs and pdfs).
00296       // instead of counting their contribution, we will instead account for 
00297       // them using one of the other k-1 sampling techniques (in which 
00298       // this specular vertex is not part of the connecting edge)
00299       if (m_vertices[indexE].bsdf->isSpecular() || 
00300           m_vertices[indexL].bsdf->isSpecular())
00301       {
00302          pdfs[i] = pdfs[i + 1] = 0;
00303       }
00304    }
00305 }
00306 
00307 // add a new vertex to a Light subpath
00308 bool Path::append(bool roulette) {
00309    if (length() > 0) // general case
00310       return _samplePathVertex(roulette, false);
00311    
00312    // sample initial location on random light source
00313    EmitterSampler emitterSampler = m_renderer->getScene()->getEmitterSampler();
00314    Event event = emitterSampler.sample();
00315    SurfacePoint *pt = event.getValue<SurfacePoint*>();
00316    const real_t pA  = emitterSampler.getPdf(event);
00317    
00318    m_vertices.push_back(PathVertex(pt, pA, 1));
00319    return true;
00320 }
00321 
00322 // add a new vertex to an Eye subpath
00323 bool Path::prepend(bool roulette) {
00324    if (length() > 0) // general case
00325       return _samplePathVertex(roulette, true);
00326    
00327    // sample initial location on sensor
00328    Camera *camera   = m_renderer->getCamera();
00329    SurfacePoint *pt = new SurfacePoint();
00330    const real_t pA  = 1.0;///(camera->getSurfaceArea());
00331    
00332    camera->getRandomPoint(*pt);
00333    m_vertices.push_front(PathVertex(pt, 1, pA));
00334    return true;
00335 }
00336 
00337 // add a new vertex to an Eye subpath
00338 bool Path::prepend(const PathVertex &v1) {
00339    m_vertices.push_front(v1);
00340    ASSERT(length() == 1);
00341    
00342    return true;
00343 }
00344 
00345 bool Path::append(const Path &path) {
00346    const unsigned s = length();
00347    const unsigned t = path.length();
00348    const unsigned k = s + t;
00349    unsigned invalid = 0;
00350    
00351    // append the underlying PathVertexLists together
00352    m_vertices.insert(m_vertices.begin() + s, 
00353                      path.m_vertices.begin(), path.m_vertices.end());
00354    ASSERT(k == length());
00355    
00356    { // initialize connection between last light subpath vertex and first 
00357      // eye subpath vertex
00358      if (s > 0 && t > 0) {
00359          PathVertex &y = m_vertices[s - 1];
00360          PathVertex &z = m_vertices[s];
00361          
00362          Vector3 wo = (z.pt->position - y.pt->position);
00363          real_t  d  = wo.normalize();
00364          
00365          SpectralSampleSet alpha; // unused
00366          real_t pA; // unused
00367          
00368          if (!_initL(y,  wo, d, z.pt.get(), alpha, pA, false))
00369             invalid |= 1;
00370          
00371          if (!_initE(z, -wo, d, y.pt.get(), alpha, pA, false))
00372             invalid |= 2;
00373          
00374          // ensure visibility between y and z
00375          if (invalid != 3 && m_renderer->getScene()->intersects(Ray(y.pt->position, wo), d)) {
00376             y.GL = z.GE = 0;
00377             
00378             invalid |= 3;
00379          }
00380       }
00381    }
00382    
00383    { // merge cumulative eye and light subpath contributions
00384       // update cumulative eye contribution in original light subpath
00385       for(unsigned i = s; i--;) {
00386          PathVertex &y = m_vertices[i];
00387          
00388          if (i >= k - 1) { // last light vertex connected to empty eye subpath
00389             ASSERT(0 == t);
00390             ASSERT(!invalid);
00391             
00392             if (y.pt->sensor->isSensor()) {
00393                real_t pA = m_renderer->getCamera()->getSurfaceArea();
00394                pA = (pA > EPSILON ? 1.0 / pA : 1);
00395                
00396                ASSERT(0 && "shouldn't get here with pinhole camera!!!");
00397                // TODO: totally unsure about the folling 10~ish lines
00398                
00399                y.bsdf->setWi(y.wi);
00400                
00401                y.alphaE = y.pt->sensor->getWe0() / pA;
00402                y.pE     = pA;
00403                
00404                y.fs     = y.bsdf->getBSDF(-y.wi);
00405                y.pdfE   = y.bsdf->getPdf(Event(-y.wi, y.event));
00406             } else {
00407                y.alphaE = SpectralSampleSet::black();
00408                y.pE     = 0;
00409                
00410                y.fs     = SpectralSampleSet::black();
00411                y.pdfE   = 0;
00412             }
00413          } else {
00414             const PathVertex &z = m_vertices[i + 1];
00415             
00416             y.alphaE = (z.pdfE > 0 ? z.alphaE * z.fs / z.pdfE : SpectralSampleSet::black());
00417             y.pE     = z.pE * z.pdfE * z.GE;
00418          }
00419       }
00420       
00421       // update cumulative light contribution in orignal eye subpath
00422       for(unsigned i = 0; i < t; ++i) {
00423          PathVertex &z = m_vertices[s + i];
00424          
00425          if (0 == s + i) { // last eye vertex connected to empty light subpath
00426             ASSERT(0 == s);
00427             ASSERT(!invalid);
00428             
00429             if (z.pt->emitter->isEmitter()) {
00430                const Event event(z.pt.get());
00431                const real_t pA = 
00432                   m_renderer->getScene()->getEmitterSampler().getPdf(event);
00433                
00434                // shouldn't have to setWi for z's BSDF cause it's an emitter
00435                const Vector3 &wo = -z.wi;
00436                
00437                z.alphaL = z.pt->emitter->getLe0() / pA;
00438                z.pL     = pA;
00439                
00440                z.fs     = z.bsdf->getBSDF(wo);
00441                z.pdfL   = z.bsdf->getPdf(Event(wo, z.event));
00442             } else {
00443                z.alphaL = SpectralSampleSet::black();
00444                z.pL     = 0;
00445                
00446                z.fs     = SpectralSampleSet::black();
00447                z.pdfL   = 0;
00448             }
00449          } else {
00450             const PathVertex &y = m_vertices[s + i - 1];
00451             
00452             z.alphaL = (y.pdfL > 0 ? y.alphaL * y.fs / y.pdfL : SpectralSampleSet::black());
00453             z.pL     = y.pL * y.pdfL * y.GL;
00454          }
00455       }
00456    }
00457    
00458 /*#ifdef DEBUG
00459    //cerr << (*this) << endl << endl;
00460    
00461    real_t pE = m_vertices[k-1].pE;
00462    real_t pL = m_vertices[0].pL;
00463    
00464    // path integrity check
00465    for(unsigned i = k; i--;) {
00466       PathVertex &x = m_vertices[i];
00467       
00468       // sanity check to ensure all values are within acceptable expected ranges
00469       ASSERT(x.GL >= 0);
00470       ASSERT(x.GE >= 0);
00471       
00472       ASSERT(x.alphaL >= SpectralSampleSet::black());
00473       ASSERT(x.alphaE >= SpectralSampleSet::black());
00474       
00475       ASSERT(x.fs >= SpectralSampleSet::black());
00476       
00477       ASSERT(x.pdfL >= 0);
00478       ASSERT(x.pdfE >= 0);
00479       
00480       ASSERT(x.pL >= 0);
00481       ASSERT(x.pE >= 0);
00482       
00483       // more thorough checks to ensure path is logically sound
00484       if (i > 0) {
00485          PathVertex &y = m_vertices[i - 1];
00486          
00487          ASSERT(y.GL     == x.GE);
00488          //ASSERT(y.alphaL >= x.alphaL - SpectralSampleSet::fill(0.001));
00489          //ASSERT(y.pL     >= x.pL - 0.001);
00490          
00491          // only the first light vertex is allowed to be an emitter; if a light
00492          // subpath strikes an emitter after the first vertex, it should act as
00493          // a normal surface w/ respect to reflectance, not as an emitter
00494          ASSERT(x.bsdf != x.pt->emitter);
00495          
00496          PathVertex &v = m_vertices[k - i - 1];
00497          pL *= v.pdfL * v.GL;
00498          
00499          ASSERT(EQ(pL, m_vertices[k - i].pL));
00500       } else {
00501          ASSERT(x.bsdf == x.pt->emitter && x.pt->emitter->isEmitter());
00502       }
00503       
00504       if (i < k - 1) {
00505          PathVertex &z = m_vertices[i + 1];
00506          
00507          ASSERT(z.GE     == x.GL);
00508          //ASSERT(z.alphaE >= x.alphaE - SpectralSampleSet::fill(0.001));
00509          //ASSERT(z.pE     >= x.pE - 0.001);
00510          
00511          // only the first eye vertex is allowed to be a sensor; if an eye 
00512          // subpath strikes a sensor after the first vertex, it should act as 
00513          // a normal surface w/ respect to reflectance, not as a sensor
00514          ASSERT(x.bsdf != x.pt->sensor);
00515          
00516          pE *= z.pdfE * z.GE;
00517          
00518          ASSERT(EQ(pE, x.pE));
00519       } else {
00520          ASSERT(x.bsdf == x.pt->sensor && x.pt->sensor->isSensor());
00521       }
00522    }
00523    
00524    if (s > 0 && t > 0 && (0 == invalid || 3 == invalid)) {
00525       for(unsigned i = k + 1; i--;) {
00526          const SpectralSampleSet &c = getContribution(k - i, i);
00527          
00528          if (invalid) {
00529             ASSERT(c == SpectralSampleSet::black());
00530          } else {
00531             //ASSERT(c.getSum() > 0);
00532          }
00533       }
00534    }
00535 #endif*/
00536    
00537    return (invalid != 3);
00538 }
00539 
00540 bool Path::_samplePathVertex(bool roulette, bool adjoint) {
00541    PathVertex &v = (adjoint ? front() : back());
00542    SurfacePoint *pt;
00543    real_t t;
00544    
00545    // sample direction from endpoint's BSDF
00546    const Vector3 &wo = v.event;
00547    if (wo == Vector3::zero())
00548       return false; // absorbed
00549    
00550    ASSERT(wo.isUnit());
00551    { // trace ray to determine new surfacepoint
00552       const Ray ray(v.pt->position, wo);
00553       
00554       pt = new SurfacePoint();
00555       t  = m_renderer->getScene()->getIntersection(ray, *pt);
00556       
00557       if (!pt->init(ray, t)) {
00558          safeDelete(pt);
00559          return false;
00560       }
00561    }
00562    
00563    // initialize new point
00564    if (adjoint) { // eye subpath
00565       SpectralSampleSet alphaE;
00566       real_t pE;
00567       
00568       if (!_initE(v, wo, t, pt, alphaE, pE, roulette)) {
00569          safeDelete(pt);
00570          return false;
00571       }
00572       
00573       m_vertices.push_front(
00574          PathVertex(pt, wo, v.GE, 1, SpectralSampleSet::identity(), alphaE, 1, pE)
00575       );
00576       
00577       PathVertex &z = m_vertices.front();
00578       if (z.bsdf == z.pt->sensor)
00579          z.bsdf = z.pt->bsdf;
00580    } else {       // light subpath
00581       SpectralSampleSet alphaL;
00582       real_t pL;
00583       
00584       if (!_initL(v, wo, t, pt, alphaL, pL, roulette)) {
00585          safeDelete(pt);
00586          return false;
00587       }
00588       
00589       m_vertices.push_back (
00590          PathVertex(pt, wo, 1, v.GL, alphaL, SpectralSampleSet::identity(), pL, 1)
00591       );
00592       
00593       PathVertex &y = m_vertices.back();
00594       if (y.bsdf == y.pt->emitter)
00595          y.bsdf = y.pt->bsdf;
00596    }
00597    
00598    return true;
00599 }
00600 
00601 bool Path::_initL(PathVertex &y, const Vector3 &wo, real_t t, SurfacePoint *pt, 
00602                   SpectralSampleSet &alphaL, real_t &pL, bool roulette) const
00603 {
00604    y.GL   = ABS(y.pt->normal.dot(wo) * pt->normal.dot(-wo)) / (t * t);
00605    
00606    y.bsdf->setWi(-wo);
00607    y.pdfE = y.bsdf->getPdf(Event(-y.wi, y.event));
00608    
00609    y.bsdf->setWi(y.wi);
00610    y.fs   = y.bsdf->getBSDF(wo);
00611    y.pdfL = y.bsdf->getPdf(Event(wo, y.event));
00612    
00613    if (y.pdfL == 0)
00614       return false;
00615    
00616    // russian roulette
00617    if (roulette || y.fs == SpectralSampleSet::black()) {
00618       const real_t q = MIN(0.95, y.fs[y.fs.getMaxSample()].value / y.pdfL);
00619       
00620       if (q <= Random::sample(0, 1)) {
00621          y.pdfL = 0;
00622          return false;
00623       }
00624       
00625       y.pdfL *= q;
00626    }
00627    
00628    // accumulate light subpath probability density and contribution
00629    pL     = (y.pdfL * y.GL) * y.pL;
00630    alphaL = (y.fs / y.pdfL) * y.alphaL;
00631    
00632    return true;
00633 }
00634 
00635 bool Path::_initE(PathVertex &z, const Vector3 &wo, real_t t, SurfacePoint *pt, 
00636                   SpectralSampleSet &alphaE, real_t &pE, bool roulette) const
00637 {
00638    z.GE   = ABS(z.pt->normal.dot(wo) * pt->normal.dot(-wo)) / (t * t);
00639    
00640    z.bsdf->setWi(-wo);
00641    z.pdfL = z.bsdf->getPdf(Event(-z.wi, z.event));
00642    z.fs   = z.bsdf->getBSDF(-z.wi);
00643    
00644    z.bsdf->setWi(z.wi);
00645    z.pdfE = z.bsdf->getPdf(Event(wo, z.event));
00646    
00647    if (z.pdfE == 0)
00648       return false;
00649    
00650    // russian roulette
00651    if (roulette || z.fs == SpectralSampleSet::black()) {
00652       const real_t q = MIN(0.95, z.fs[z.fs.getMaxSample()].value / z.pdfE);
00653       
00654       if (q <= Random::sample(0, 1)) {
00655          z.pdfE = 0;
00656          return false;
00657       }
00658       
00659       z.pdfE *= q;
00660    }
00661    
00662    // accumulate eye subpath probability density and contribution
00663    pE     = (z.pdfE * z.GE) * z.pE;
00664    alphaE = (z.fs / z.pdfE) * z.alphaE;
00665    
00666    return true;
00667 }
00668 
00669 std::string Path::toHeckbertNotation() const {
00670    std::string str = "";
00671    
00672    FOREACH(PathVertexListConstIter, m_vertices, iter) {
00673       if (iter->bsdf == iter->pt->emitter && iter->pt->emitter->isEmitter())
00674          str += "L";
00675       else if (iter->bsdf == iter->pt->sensor && iter->pt->sensor->isSensor())
00676          str += "E";
00677       else 
00678          str += (iter->pt->bsdf->isSpecular() ? "S" : "D");
00679    }
00680    
00681    return str;
00682 }
00683 
00684 std::ostream &operator<<(std::ostream &os, const Path &path) {
00685    os << "{ length = " << path.length();
00686    
00687    // print out each vertex in the given path
00688    for(unsigned i = 0; i < path.length(); ++i)
00689       os << ", " << path[i];
00690    
00691    os << " }";
00692    
00693    return os;
00694 }
00695 
00696 
00697 #if 0
00698 bool Path::_samplePathVertex(bool adjoint) {
00699    PathVertex &v = m_vertices[(adjoint ? 0 : length() - 1)];
00700    SurfacePoint *pt;
00701    real_t t;
00702    
00703    // sample direction from endpoint's BSDF
00704    const Event &e = v.bsdf->sample();
00705    const Vector3 &wo = e;
00706    
00707    { // trace ray to determine new samplepoint
00708       const Ray ray(v.pt->position, wo);
00709       
00710       pt = new SurfacePoint();
00711       t  = m_renderer->getScene()->getIntersection(ray, *pt);
00712       
00713       if (!pt->init(ray, t)) {
00714          safeDelete(pt);
00715          return false;
00716       }
00717    }
00718    
00719    { // initialize new point  
00720             const real_t cosWo = v.pt->normal.dot(wo);
00721       const real_t pPSA  = v.bsdf->getPdf(e) / ABS(cosWo);
00722       const real_t G     = ABS(cosWo * pt->normal.dot(-wo)) / (t * t);
00723       ASSERT(pPSA > 0);
00724       ASSERT(G    > 0);
00725       
00726       const SpectralSampleSet &fs    = v.bsdf->getBSDF(wo);
00727       const SpectralSampleSet &alpha = fs / pPSA;
00728       
00729       if (adjoint) {
00730          m_vertices.push_front(PathVertex(pt, alpha * v.alphaE, G, -wo));
00731       } else {
00732          m_vertices.push_back(PathVertex(pt, alpha * v.alphaL));
00733          v.G = G;
00734       }
00735       
00736       v.fs   = fs;
00737       v.pPSA = pPSA;
00738    }
00739    
00740    return true;
00741 }
00742 #endif
00743    
00744    
00745 #if 0
00746    ASSERT(actualS <= k && n <= k);
00747    
00748    bzero(pdfs, sizeof(real_t) * (n + 1));
00749    
00750    unsigned s = actualS;             // index of current light vertex
00751    unsigned t = k - n + actualS - 1; // index of current eye vertex
00752    real_t pL  = 1;
00753    real_t pE  = 1;
00754    
00755    if (actualS == 0) {
00756       ASSERT(t == k - n);
00757       pdfs[0] = !m_vertices[0].bsdf->isSpecular();
00758       
00759       pE = m_vertices[t].pE;
00760    } else if (actualS == n) {
00761       pdfs[s - 1] = !m_vertices[s - 1].bsdf->isSpecular();
00762       
00763       pL = m_vertices[s - 1].pL;
00764    } else {
00765       pdfs[s - 1] = !(m_vertices[s - 1].bsdf->isSpecular() || 
00766                       m_vertices[t].bsdf->isSpecular());
00767       
00768       pL = m_vertices[s - 1].pL;
00769       pE = m_vertices[t].pE;
00770    }
00771    
00772    for(unsigned i = actualS; i <= n && pdfs[i - 1] > 0; ++i, s = t++) {
00773       ASSERT(s < k && t <= k);
00774       
00775       const PathVertex &y = m_vertices[s];
00776       const real_t E      = (t < k ? m_vertices[t].pE : 1);
00777       
00778       if (pL == 0 || y.pE == 0 || y.bsdf->isSpecular())
00779          break;
00780       
00781       pdfs[i] = (pdfs[i - 1] * y.pL * E) / (pL * y.pE);
00782       pL      = y.pL;
00783    }
00784    
00785    s = actualS - 2;
00786    t = actualS - 1;
00787    
00788    for(; actualS > 0 && t > 0 && pdfs[t] > 0; t = s--) {
00789       ASSERT(t < k && s < k);
00790       
00791       const PathVertex &y = m_vertices[s];
00792       const PathVertex &z = m_vertices[t];
00793       
00794       if (z.pL == 0 || pE == 0 || y.bsdf->isSpecular() || z.bsdf->isSpecular())
00795          break;
00796       
00797       pdfs[s] = (pdfs[t] * y.pL * z.pE) / (z.pL * pE);
00798       pE      = y.pE;
00799    }
00800 }
00801 
00802 real_t Path::getPdf(unsigned s, unsigned t) const {
00803    // note s and t are exclusive; u and v are inclusive
00804    const unsigned k = length();
00805    const unsigned u = s - 1; // index of last light subpath vertex
00806    const unsigned v = k - t; // index of last eye   subpath vertex
00807    ASSERT(s + t <= k);
00808    
00809    real_t pL = 1, pE = 1;
00810    if (s > 0)
00811       pL = m_vertices[u].pL;
00812    else if (m_vertices[v].pt->shape->getSurfaceArea() == 0)
00813       pL = 0;
00814    
00815    if (t > 0)
00816       pE = m_vertices[v].pE;
00817    else if (m_vertices[u].pt->shape->getSurfaceArea() == 0)
00818       pE = 0;
00819    
00820    const real_t pdf = pL * pE;
00821    ASSERT(pdf >= 0);
00822    
00823    return pdf;
00824 }
00825 #endif
00826 
00827 #if 0
00828 void Path::getPdfs(unsigned k, unsigned s, real_t *pdfs) const {
00829    const unsigned n = length();
00830    ASSERT(s <= n && k <= n);
00831    s = MIN(s, k);
00832    
00833    bzero(pdfs, sizeof(real_t) * (k + 1));
00834    
00835    // pdfs[s] represents the probability density with which this path of length
00836    // k was actually generated; because we're combining pdfs with multiple 
00837    // importance sampling, we really only care about the relative probability 
00838    // densities (since each weight in multiple importance sampling is a ratio 
00839    // of densities -- their ratio is all that matters).  to make the 
00840    // computation (of all possible probability densities with which this 
00841    // k-length path could have been sampled) easier, we define p(s) to be one 
00842    // and compute all other probability densities relative to p(s) by using 
00843    // the quick / easy-to-compute ratios p(s+1)/p(s) and p(s+1)/p(s)
00844    pdfs[s] = 1;
00845    
00846    // p(i) is known; compute ratio p(i+1)/p(i) to determine p(s+1)...p(k)
00847    for(unsigned i = s; i < k; ++i) {
00848       real_t num = 1, den = 1;
00849       
00850       if (i == 0) {
00851          const PathVertex &v = m_vertices[P_INDEX(i, n, s, k)];
00852          
00853          num = v.pL;
00854          den = v.pdfE * v.GE;
00855       } else if (i < k - 1) {
00856          const PathVertex &u = m_vertices[P_INDEX(i - 1, n, s, k)];
00857          const PathVertex &w = m_vertices[P_INDEX(i + 1, n, s, k)];
00858          
00859          num = u.pdfL * u.GL;
00860          den = w.pdfE * w.GE;
00861       } else { // i == k - 1
00862          ASSERT(i == k - 1);
00863          
00864          const PathVertex &u = m_vertices[P_INDEX(i - 1, n, s, k)];
00865          const PathVertex &v = m_vertices[P_INDEX(i, n, s, k)];
00866          
00867          num = u.pdfL * u.GL;
00868          den = v.pE;
00869       }
00870       
00871       if (num == 0 || den == 0)
00872          break;
00873       
00874       ASSERT(i + 1 <= k);
00875       pdfs[i + 1] = pdfs[i] * (num / den);
00876    }
00877    
00878    // p(i) is known; compute ratio p(i-1)/p(i) to determine p(s-1)...p(0)
00879    for(unsigned i = s; i >= 1; --i) {
00880       real_t num = 1, den = 1;
00881       
00882       if (i == 1) {
00883          const PathVertex &u = m_vertices[P_INDEX(i - 1, n, s, k)];
00884          const PathVertex &v = m_vertices[P_INDEX(i, n, s, k)];
00885          
00886          num = v.pdfE * v.GE;
00887          den = u.pL;
00888       } else if (i < k) {
00889          const PathVertex &t = m_vertices[P_INDEX(i - 2, n, s, k)];
00890          const PathVertex &v = m_vertices[P_INDEX(i, n, s, k)];
00891          
00892          num = v.pdfE * v.GE;
00893          den = t.pdfL * t.GL;
00894       } else { // i == k
00895          ASSERT(i == k);
00896          
00897          const PathVertex &t = m_vertices[P_INDEX(i - 2, n, s, k)];
00898          const PathVertex &u = m_vertices[P_INDEX(i - 1, n, s, k)];
00899          
00900          num = u.pE;
00901          den = t.pdfL * t.GL;
00902       }
00903       
00904       if (num == 0 || den == 0)
00905          break;
00906       
00907       ASSERT(i - 1 <= k);
00908       pdfs[i - 1] = pdfs[i] * (num / den);
00909    }
00910    
00911    unsigned oldIndex = n;
00912    
00913    // handle specular vertices
00914    for(unsigned i = k; i--;) {
00915       const unsigned index = P_INDEX(i, n, s, k);
00916       
00917       ASSERT(index < oldIndex);
00918       oldIndex = index;
00919       
00920       // any path with a connecting edge containing a specular vertex 
00921       // automatically has its contribution set to zero because these paths 
00922       // are impossible to deal with numerically (dirac BSDFs and pdfs).
00923       // instead of counting their contribution, we will instead account for 
00924       // them using one of the other k-1 sampling techniques (in which 
00925       // this specular vertex is not part of the connecting edge)
00926       if (m_vertices[index].bsdf->isSpecular())
00927          pdfs[i] = pdfs[i + 1] = 0;
00928    }
00929 }
00930 #endif
00931 

Generated on 28 Feb 2009 for Milton by doxygen 1.5.6