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
1.5.6