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
1.5.6