1 /****************************************************************************
2 * photonintegr.cc: integrator for photon mapping and final gather
3 * This is part of the yafaray package
4 * Copyright (C) 2006 Mathias Wein (Lynx)
5 * Copyright (C) 2009 Rodrigo Placencia (DarkTide)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include <integrators/photonintegr.h>
23 #include <core_api/logging.h>
24 #include <core_api/session.h>
25 #include <core_api/volume.h>
26 #include <core_api/params.h>
27 #include <core_api/scene.h>
28
29 __BEGIN_YAFRAY
30
preGatherWorker(preGatherData_t * gdata,float dsRad,int nSearch)31 void photonIntegrator_t::preGatherWorker(preGatherData_t * gdata, float dsRad, int nSearch)
32 {
33 unsigned int start, end, total;
34 float dsRadius_2 = dsRad*dsRad;
35
36 gdata->mutx.lock();
37 start = gdata->fetched;
38 total = gdata->rad_points.size();
39 end = gdata->fetched = std::min(total, start + 32);
40 gdata->mutx.unlock();
41
42 foundPhoton_t *gathered = new foundPhoton_t[nSearch];
43
44 float radius = 0.f;
45 float iScale = 1.f / ((float)gdata->diffuseMap->nPaths() * M_PI);
46 float scale = 0.f;
47
48 while(start < total)
49 {
50 for(unsigned int n=start; n<end; ++n)
51 {
52 radius = dsRadius_2;//actually the square radius...
53 int nGathered = gdata->diffuseMap->gather(gdata->rad_points[n].pos, gathered, nSearch, radius);
54
55 vector3d_t rnorm = gdata->rad_points[n].normal;
56
57 color_t sum(0.0);
58
59 if(nGathered > 0)
60 {
61 scale = iScale / radius;
62
63 for(int i=0; i<nGathered; ++i)
64 {
65 vector3d_t pdir = gathered[i].photon->direction();
66
67 if( rnorm * pdir > 0.f ) sum += gdata->rad_points[n].refl * scale * gathered[i].photon->color();
68 else sum += gdata->rad_points[n].transm * scale * gathered[i].photon->color();
69 }
70 }
71
72 gdata->radianceVec[n] = photon_t(rnorm, gdata->rad_points[n].pos, sum);
73 }
74 gdata->mutx.lock();
75 start = gdata->fetched;
76 end = gdata->fetched = std::min(total, start + 32);
77 gdata->pbar->update(32);
78 gdata->mutx.unlock();
79 }
80 delete[] gathered;
81 }
82
photonIntegrator_t(unsigned int dPhotons,unsigned int cPhotons,bool transpShad,int shadowDepth,float dsRad,float cRad)83 photonIntegrator_t::photonIntegrator_t(unsigned int dPhotons, unsigned int cPhotons, bool transpShad, int shadowDepth, float dsRad, float cRad)
84 {
85 usePhotonCaustics = true;
86 usePhotonDiffuse = true;
87 type = SURFACE;
88 trShad = transpShad;
89 finalGather = true;
90 nDiffusePhotons = dPhotons;
91 nCausPhotons = cPhotons;
92 sDepth = shadowDepth;
93 dsRadius = dsRad;
94 causRadius = cRad;
95 rDepth = 6;
96 maxBounces = 5;
97 integratorName = "PhotonMap";
98 integratorShortName = "PM";
99 }
100
~photonIntegrator_t()101 photonIntegrator_t::~photonIntegrator_t()
102 {
103 // Empty
104 }
105
106
causticWorker(photonMap_t * causticMap,int threadID,const scene_t * scene,unsigned int nCausPhotons,const pdf1D_t * lightPowerD,int numCLights,const std::string & integratorName,const std::vector<light_t * > & tmplights,int causDepth,progressBar_t * pb,int pbStep,unsigned int & totalPhotonsShot,int maxBounces)107 void photonIntegrator_t::causticWorker(photonMap_t * causticMap, int threadID, const scene_t *scene, unsigned int nCausPhotons, const pdf1D_t *lightPowerD, int numCLights, const std::string &integratorName, const std::vector<light_t *> &tmplights, int causDepth, progressBar_t *pb, int pbStep, unsigned int &totalPhotonsShot, int maxBounces)
108 {
109 ray_t ray;
110 float lightNumPdf, lightPdf, s1, s2, s3, s4, s5, s6, s7, sL;
111 color_t pcol;
112
113 //shoot photons
114 bool done=false;
115 unsigned int curr=0;
116
117 surfacePoint_t sp;
118 renderState_t state;
119 unsigned char userdata[USER_DATA_SIZE+7];
120 state.userdata = (void *)( &userdata[7] - ( ((size_t)&userdata[7])&7 ) ); // pad userdata to 8 bytes
121 state.cam = scene->getCamera();
122
123 float fNumLights = (float)numCLights;
124 unsigned int nCausPhotons_thread = 1 + ( (nCausPhotons - 1) / scene->getNumThreadsPhotons() );
125
126 std::vector<photon_t> localCausticPhotons;
127 localCausticPhotons.clear();
128 localCausticPhotons.reserve(nCausPhotons_thread);
129
130 float invCaustPhotons = 1.f / (float)nCausPhotons;
131
132 while(!done)
133 {
134 unsigned int haltoncurr = curr + nCausPhotons_thread * threadID;
135
136 state.chromatic = true;
137 state.wavelength = scrHalton(5,haltoncurr);
138
139 s1 = RI_vdC(haltoncurr);
140 s2 = scrHalton(2, haltoncurr);
141 s3 = scrHalton(3, haltoncurr);
142 s4 = scrHalton(4, haltoncurr);
143
144 sL = float(haltoncurr) * invCaustPhotons;
145 int lightNum = lightPowerD->DSample(sL, &lightNumPdf);
146
147 if(lightNum >= numCLights)
148 {
149 causticMap->mutx.lock();
150 Y_ERROR << integratorName << ": lightPDF sample error! " << sL << "/" << lightNum << yendl;
151 causticMap->mutx.unlock();
152 return;
153 }
154
155 pcol = tmplights[lightNum]->emitPhoton(s1, s2, s3, s4, ray, lightPdf);
156 ray.tmin = scene->rayMinDist;
157 ray.tmax = -1.0;
158 pcol *= fNumLights*lightPdf/lightNumPdf; //remember that lightPdf is the inverse of th pdf, hence *=...
159 if(pcol.isBlack())
160 {
161 ++curr;
162 done = (curr >= nCausPhotons_thread);
163 continue;
164 }
165 int nBounces=0;
166 bool causticPhoton = false;
167 bool directPhoton = true;
168 const material_t *material = nullptr;
169 BSDF_t bsdfs;
170
171 while( scene->intersect(ray, sp) )
172 {
173 if(std::isnan(pcol.R) || std::isnan(pcol.G) || std::isnan(pcol.B))
174 {
175 causticMap->mutx.lock();
176 Y_WARNING << integratorName << ": NaN on photon color for light" << lightNum + 1 << "." << yendl;
177 causticMap->mutx.unlock();
178 continue;
179 }
180
181 color_t transm(1.f);
182 color_t vcol(0.f);
183 const volumeHandler_t* vol = nullptr;
184
185 if(material)
186 {
187 if((bsdfs&BSDF_VOLUMETRIC) && (vol=material->getVolumeHandler(sp.Ng * -ray.dir < 0)))
188 {
189 if(vol->transmittance(state, ray, vcol)) transm = vcol;
190 }
191 }
192
193 vector3d_t wi = -ray.dir, wo;
194 material = sp.material;
195 material->initBSDF(state, sp, bsdfs);
196
197 if(bsdfs & BSDF_DIFFUSE)
198 {
199 if(causticPhoton)
200 {
201 photon_t np(wi, sp.P, pcol);
202 localCausticPhotons.push_back(np);
203 }
204 }
205
206 // need to break in the middle otherwise we scatter the photon and then discard it => redundant
207 if(nBounces == maxBounces) break;
208 // scatter photon
209 int d5 = 3*nBounces + 5;
210
211 s5 = scrHalton(d5, haltoncurr);
212 s6 = scrHalton(d5+1, haltoncurr);
213 s7 = scrHalton(d5+2, haltoncurr);
214
215 pSample_t sample(s5, s6, s7, BSDF_ALL, pcol, transm);
216
217 bool scattered = material->scatterPhoton(state, sp, wi, wo, sample);
218 if(!scattered) break; //photon was absorped.
219
220 pcol = sample.color;
221
222 causticPhoton = ((sample.sampledFlags & (BSDF_GLOSSY | BSDF_SPECULAR | BSDF_DISPERSIVE)) && directPhoton) ||
223 ((sample.sampledFlags & (BSDF_GLOSSY | BSDF_SPECULAR | BSDF_FILTER | BSDF_DISPERSIVE)) && causticPhoton);
224 directPhoton = (sample.sampledFlags & BSDF_FILTER) && directPhoton;
225
226 if(state.chromatic && (sample.sampledFlags & BSDF_DISPERSIVE))
227 {
228 state.chromatic=false;
229 color_t wl_col;
230 wl2rgb(state.wavelength, wl_col);
231 pcol *= wl_col;
232 }
233
234 ray.from = sp.P;
235 ray.dir = wo;
236 ray.tmin = scene->rayMinDist;
237 ray.tmax = -1.0;
238 ++nBounces;
239 }
240 ++curr;
241 if(curr % pbStep == 0)
242 {
243 pb->mutx.lock();
244 pb->update();
245 pb->mutx.unlock();
246 if(scene->getSignals() & Y_SIG_ABORT) { return; }
247 }
248 done = (curr >= nCausPhotons_thread);
249 }
250 causticMap->mutx.lock();
251 causticMap->appendVector(localCausticPhotons, curr);
252 totalPhotonsShot += curr;
253 causticMap->mutx.unlock();
254 }
255
diffuseWorker(photonMap_t * diffuseMap,int threadID,const scene_t * scene,unsigned int nDiffusePhotons,const pdf1D_t * lightPowerD,int numDLights,const std::string & integratorName,const std::vector<light_t * > & tmplights,progressBar_t * pb,int pbStep,unsigned int & totalPhotonsShot,int maxBounces,bool finalGather,preGatherData_t & pgdat)256 void photonIntegrator_t::diffuseWorker(photonMap_t * diffuseMap, int threadID, const scene_t *scene, unsigned int nDiffusePhotons, const pdf1D_t *lightPowerD, int numDLights, const std::string &integratorName, const std::vector<light_t *> &tmplights, progressBar_t *pb, int pbStep, unsigned int &totalPhotonsShot, int maxBounces, bool finalGather, preGatherData_t &pgdat)
257 {
258 ray_t ray;
259 float lightNumPdf, lightPdf, s1, s2, s3, s4, s5, s6, s7, sL;
260 color_t pcol;
261
262 //shoot photons
263 bool done=false;
264 unsigned int curr=0;
265
266 surfacePoint_t sp;
267 renderState_t state;
268 unsigned char userdata[USER_DATA_SIZE+7];
269 state.userdata = (void *)( &userdata[7] - ( ((size_t)&userdata[7])&7 ) ); // pad userdata to 8 bytes
270 state.cam = scene->getCamera();
271
272 float fNumLights = (float)numDLights;
273
274 unsigned int nDiffusePhotons_thread = 1 + ( (nDiffusePhotons - 1) / scene->getNumThreadsPhotons() );
275
276 std::vector<photon_t> localDiffusePhotons;
277 std::vector<radData_t> localRadPoints;
278
279 localDiffusePhotons.clear();
280 localDiffusePhotons.reserve(nDiffusePhotons_thread);
281 localRadPoints.clear();
282
283 float invDiffPhotons = 1.f / (float)nDiffusePhotons;
284
285 while(!done)
286 {
287 unsigned int haltoncurr = curr + nDiffusePhotons_thread * threadID;
288
289 s1 = RI_vdC(haltoncurr);
290 s2 = scrHalton(2, haltoncurr);
291 s3 = scrHalton(3, haltoncurr);
292 s4 = scrHalton(4, haltoncurr);
293
294 sL = float(haltoncurr) * invDiffPhotons;
295 int lightNum = lightPowerD->DSample(sL, &lightNumPdf);
296 if(lightNum >= numDLights)
297 {
298 diffuseMap->mutx.lock();
299 Y_ERROR << integratorName << ": lightPDF sample error! " << sL << "/" << lightNum << yendl;
300 diffuseMap->mutx.unlock();
301 return;
302 }
303
304 pcol = tmplights[lightNum]->emitPhoton(s1, s2, s3, s4, ray, lightPdf);
305 ray.tmin = scene->rayMinDist;
306 ray.tmax = -1.0;
307 pcol *= fNumLights*lightPdf/lightNumPdf; //remember that lightPdf is the inverse of th pdf, hence *=...
308
309 if(pcol.isBlack())
310 {
311 ++curr;
312 done = (curr >= nDiffusePhotons_thread);
313 continue;
314 }
315
316 int nBounces=0;
317 bool causticPhoton = false;
318 bool directPhoton = true;
319 const material_t *material = nullptr;
320 BSDF_t bsdfs;
321
322 while( scene->intersect(ray, sp) )
323 {
324 if(std::isnan(pcol.R) || std::isnan(pcol.G) || std::isnan(pcol.B))
325 {
326 diffuseMap->mutx.lock();
327 Y_WARNING << integratorName << ": NaN on photon color for light" << lightNum + 1 << "." << yendl;
328 diffuseMap->mutx.unlock();
329 continue;
330 }
331
332 color_t transm(1.f);
333 color_t vcol(0.f);
334 const volumeHandler_t* vol = nullptr;
335
336 if(material)
337 {
338 if((bsdfs&BSDF_VOLUMETRIC) && (vol=material->getVolumeHandler(sp.Ng * -ray.dir < 0)))
339 {
340 if(vol->transmittance(state, ray, vcol)) transm = vcol;
341 }
342 }
343
344 vector3d_t wi = -ray.dir, wo;
345 material = sp.material;
346 material->initBSDF(state, sp, bsdfs);
347
348 if(bsdfs & (BSDF_DIFFUSE))
349 {
350 //deposit photon on surface
351 if(!causticPhoton)
352 {
353 photon_t np(wi, sp.P, pcol);
354 localDiffusePhotons.push_back(np);
355 }
356 // create entry for radiance photon:
357 // don't forget to choose subset only, face normal forward; geometric vs. smooth normal?
358 if(finalGather && ourRandom() < 0.125 && !causticPhoton )
359 {
360 vector3d_t N = FACE_FORWARD(sp.Ng, sp.N, wi);
361 radData_t rd(sp.P, N);
362 rd.refl = material->getReflectivity(state, sp, BSDF_DIFFUSE | BSDF_GLOSSY | BSDF_REFLECT);
363 rd.transm = material->getReflectivity(state, sp, BSDF_DIFFUSE | BSDF_GLOSSY | BSDF_TRANSMIT);
364 localRadPoints.push_back(rd);
365 }
366 }
367 // need to break in the middle otherwise we scatter the photon and then discard it => redundant
368 if(nBounces == maxBounces) break;
369 // scatter photon
370 int d5 = 3*nBounces + 5;
371
372 s5 = scrHalton(d5, haltoncurr);
373 s6 = scrHalton(d5+1, haltoncurr);
374 s7 = scrHalton(d5+2, haltoncurr);
375
376 pSample_t sample(s5, s6, s7, BSDF_ALL, pcol, transm);
377
378 bool scattered = material->scatterPhoton(state, sp, wi, wo, sample);
379 if(!scattered) break; //photon was absorped.
380
381 pcol = sample.color;
382
383 causticPhoton = ((sample.sampledFlags & (BSDF_GLOSSY | BSDF_SPECULAR | BSDF_DISPERSIVE)) && directPhoton) ||
384 ((sample.sampledFlags & (BSDF_GLOSSY | BSDF_SPECULAR | BSDF_FILTER | BSDF_DISPERSIVE)) && causticPhoton);
385 directPhoton = (sample.sampledFlags & BSDF_FILTER) && directPhoton;
386
387 ray.from = sp.P;
388 ray.dir = wo;
389 ray.tmin = scene->rayMinDist;
390 ray.tmax = -1.0;
391 ++nBounces;
392 }
393 ++curr;
394 if(curr % pbStep == 0)
395 {
396 pb->mutx.lock();
397 pb->update();
398 pb->mutx.unlock();
399 if(scene->getSignals() & Y_SIG_ABORT) { return; }
400 }
401 done = (curr >= nDiffusePhotons_thread);
402 }
403 diffuseMap->mutx.lock();
404 diffuseMap->appendVector(localDiffusePhotons, curr);
405 totalPhotonsShot += curr;
406 diffuseMap->mutx.unlock();
407
408 pgdat.mutx.lock();
409 pgdat.rad_points.insert(std::end(pgdat.rad_points), std::begin(localRadPoints), std::end(localRadPoints));
410 pgdat.mutx.unlock();
411 }
412
photonMapKdTreeWorker(photonMap_t * photonMap)413 void photonIntegrator_t::photonMapKdTreeWorker(photonMap_t * photonMap)
414 {
415 photonMap->updateTree();
416 }
417
preprocess()418 bool photonIntegrator_t::preprocess()
419 {
420 progressBar_t *pb;
421 if(intpb) pb = intpb;
422 else pb = new ConsoleProgressBar_t(80);
423
424 lookupRad = 4*dsRadius*dsRadius;
425
426 std::stringstream set;
427 gTimer.addEvent("prepass");
428 gTimer.start("prepass");
429
430 Y_INFO << integratorName << ": Starting preprocess..." << yendl;
431
432 set << "Photon Mapping ";
433
434 if(trShad)
435 {
436 set << "ShadowDepth=" << sDepth << " ";
437 }
438 set << "RayDepth=" << rDepth << " ";
439
440 background = scene->getBackground();
441 lights = scene->lights;
442 std::vector<light_t*> tmplights;
443
444 if(usePhotonCaustics)
445 {
446 set << "\nCaustic photons=" << nCausPhotons << " search=" << nCausSearch <<" radius=" << causRadius << " depth=" << causDepth << " ";
447 }
448
449 if(usePhotonDiffuse)
450 {
451 set << "\nDiffuse photons=" << nDiffusePhotons << " search=" << nDiffuseSearch <<" radius=" << dsRadius << " ";
452 }
453
454 if(finalGather)
455 {
456 set << " FG paths=" << nPaths << " bounces=" << gatherBounces << " ";
457 }
458
459 if(photonMapProcessing == PHOTONS_LOAD)
460 {
461 bool causticMapFailedLoad = false;
462 bool diffuseMapFailedLoad = false;
463 bool fgRadianceMapFailedLoad = false;
464
465 if(usePhotonCaustics)
466 {
467 pb->setTag("Loading caustic photon map from file...");
468 const std::string filename = session.getPathImageOutput() + "_caustic.photonmap";
469 Y_INFO << integratorName << ": Loading caustic photon map from: " << filename << ". If it does not match the scene you could have crashes and/or incorrect renders, USE WITH CARE!" << yendl;
470 if(session.causticMap->load(filename)) Y_VERBOSE << integratorName << ": Caustic map loaded." << yendl;
471 else causticMapFailedLoad = true;
472 }
473
474 if(usePhotonDiffuse)
475 {
476 pb->setTag("Loading diffuse photon map from file...");
477 const std::string filename = session.getPathImageOutput() + "_diffuse.photonmap";
478 Y_INFO << integratorName << ": Loading diffuse photon map from: " << filename << ". If it does not match the scene you could have crashes and/or incorrect renders, USE WITH CARE!" << yendl;
479 if(session.diffuseMap->load(filename)) Y_VERBOSE << integratorName << ": Diffuse map loaded." << yendl;
480 else diffuseMapFailedLoad = true;
481 }
482
483 if(usePhotonDiffuse && finalGather)
484 {
485 pb->setTag("Loading FG radiance photon map from file...");
486 const std::string filename = session.getPathImageOutput() + "_fg_radiance.photonmap";
487 Y_INFO << integratorName << ": Loading FG radiance photon map from: " << filename << ". If it does not match the scene you could have crashes and/or incorrect renders, USE WITH CARE!" << yendl;
488 if(session.radianceMap->load(filename)) Y_VERBOSE << integratorName << ": FG radiance map loaded." << yendl;
489 else fgRadianceMapFailedLoad = true;
490 }
491
492 if(causticMapFailedLoad || diffuseMapFailedLoad || fgRadianceMapFailedLoad)
493 {
494 photonMapProcessing = PHOTONS_GENERATE_AND_SAVE;
495 Y_WARNING << integratorName << ": photon maps loading failed, changing to Generate and Save mode." << yendl;
496 }
497 }
498
499 if(photonMapProcessing == PHOTONS_REUSE)
500 {
501 if(usePhotonCaustics)
502 {
503 Y_INFO << integratorName << ": Reusing caustics photon map from memory. If it does not match the scene you could have crashes and/or incorrect renders, USE WITH CARE!" << yendl;
504 if(session.causticMap->nPhotons() == 0)
505 {
506 Y_WARNING << integratorName << ": Caustic photon map enabled but empty, cannot be reused: changing to Generate mode." << yendl;
507 photonMapProcessing = PHOTONS_GENERATE_ONLY;
508 }
509 }
510
511 if(usePhotonDiffuse)
512 {
513 Y_INFO << integratorName << ": Reusing diffuse photon map from memory. If it does not match the scene you could have crashes and/or incorrect renders, USE WITH CARE!" << yendl;
514 if(session.diffuseMap->nPhotons() == 0)
515 {
516 Y_WARNING << integratorName << ": Diffuse photon map enabled but empty, cannot be reused: changing to Generate mode." << yendl;
517 photonMapProcessing = PHOTONS_GENERATE_ONLY;
518 }
519 }
520
521 if(finalGather)
522 {
523 Y_INFO << integratorName << ": Reusing FG radiance photon map from memory. If it does not match the scene you could have crashes and/or incorrect renders, USE WITH CARE!" << yendl;
524 if(session.radianceMap->nPhotons() == 0)
525 {
526 Y_WARNING << integratorName << ": FG radiance photon map enabled but empty, cannot be reused: changing to Generate mode." << yendl;
527 photonMapProcessing = PHOTONS_GENERATE_ONLY;
528 }
529 }
530 }
531
532 if(photonMapProcessing == PHOTONS_LOAD)
533 {
534 set << " (loading photon maps from file)";
535 }
536 else if(photonMapProcessing == PHOTONS_REUSE)
537 {
538 set << " (reusing photon maps from memory)";
539 }
540 else if(photonMapProcessing == PHOTONS_GENERATE_AND_SAVE) set << " (saving photon maps to file)";
541
542 if(photonMapProcessing == PHOTONS_LOAD || photonMapProcessing == PHOTONS_REUSE)
543 {
544 gTimer.stop("prepass");
545 Y_INFO << integratorName << ": Photonmap building time: " << std::fixed << std::setprecision(1) << gTimer.getTime("prepass") << "s" << yendl;
546
547 set << " [" << std::fixed << std::setprecision(1) << gTimer.getTime("prepass") << "s" << "]";
548
549 yafLog.appendRenderSettings(set.str());
550
551 for (std::string line; std::getline(set, line, '\n');) Y_VERBOSE << line << yendl;
552
553 return true;
554 }
555
556 session.diffuseMap->clear();
557 session.diffuseMap->setNumPaths(0);
558 session.diffuseMap->reserveMemory(nDiffusePhotons);
559 session.diffuseMap->setNumThreadsPKDtree(scene->getNumThreadsPhotons());
560
561 session.causticMap->clear();
562 session.causticMap->setNumPaths(0);
563 session.causticMap->reserveMemory(nCausPhotons);
564 session.causticMap->setNumThreadsPKDtree(scene->getNumThreadsPhotons());
565
566 session.radianceMap->clear();
567 session.radianceMap->setNumPaths(0);
568 session.radianceMap->setNumThreadsPKDtree(scene->getNumThreadsPhotons());
569
570 ray_t ray;
571 float lightNumPdf, lightPdf;
572 int numCLights = 0;
573 int numDLights = 0;
574 float fNumLights = 0.f;
575 float *energies = nullptr;
576 color_t pcol;
577
578 //shoot photons
579 unsigned int curr=0;
580 // for radiance map:
581 preGatherData_t pgdat(session.diffuseMap);
582
583 surfacePoint_t sp;
584 renderState_t state;
585 unsigned char userdata[USER_DATA_SIZE+7];
586 state.userdata = (void *)( &userdata[7] - ( ((size_t)&userdata[7])&7 ) ); // pad userdata to 8 bytes
587 state.cam = scene->getCamera();
588 int pbStep;
589
590 tmplights.clear();
591
592 for(int i=0;i<(int)lights.size();++i)
593 {
594 if(lights[i]->shootsDiffuseP())
595 {
596 numDLights++;
597 tmplights.push_back(lights[i]);
598 }
599 }
600
601 if(numDLights == 0)
602 {
603 Y_WARNING << integratorName << ": No lights found that can shoot diffuse photons, disabling Diffuse photon processing" << yendl;
604 enableDiffuse(false);
605 }
606
607 if( usePhotonDiffuse )
608 {
609 fNumLights = (float)numDLights;
610 energies = new float[numDLights];
611
612 for(int i=0;i<numDLights;++i) energies[i] = tmplights[i]->totalEnergy().energy();
613
614 lightPowerD = new pdf1D_t(energies, numDLights);
615
616 Y_VERBOSE << integratorName << ": Light(s) photon color testing for diffuse map:" << yendl;
617 for(int i=0;i<numDLights;++i)
618 {
619 pcol = tmplights[i]->emitPhoton(.5, .5, .5, .5, ray, lightPdf);
620 lightNumPdf = lightPowerD->func[i] * lightPowerD->invIntegral;
621 pcol *= fNumLights*lightPdf/lightNumPdf; //remember that lightPdf is the inverse of the pdf, hence *=...
622 Y_VERBOSE << integratorName << ": Light [" << i+1 << "] Photon col:" << pcol << " | lnpdf: " << lightNumPdf << yendl;
623 }
624
625 delete[] energies;
626
627 //shoot photons
628 curr=0;
629
630 Y_INFO << integratorName << ": Building diffuse photon map..." << yendl;
631
632 pb->init(128);
633 pbStep = std::max(1U, nDiffusePhotons / 128);
634 pb->setTag("Building diffuse photon map...");
635 //Pregather diffuse photons
636
637 int nThreads = scene->getNumThreadsPhotons();
638
639 nDiffusePhotons = std::max((unsigned int) nThreads, (nDiffusePhotons / nThreads) * nThreads); //rounding the number of diffuse photons so it's a number divisible by the number of threads (distribute uniformly among the threads). At least 1 photon per thread
640
641 Y_PARAMS << integratorName << ": Shooting "<<nDiffusePhotons<<" photons across " << nThreads << " threads (" << (nDiffusePhotons / nThreads) << " photons/thread)"<< yendl;
642
643 if(nThreads >= 2)
644 {
645 std::vector<std::thread> threads;
646 for(int i=0; i<nThreads; ++i) threads.push_back(std::thread(&photonIntegrator_t::diffuseWorker, this, session.diffuseMap, i, scene, nDiffusePhotons, lightPowerD, numDLights, std::ref(integratorName), tmplights, pb, pbStep, std::ref(curr), maxBounces, finalGather, std::ref(pgdat)));
647 for(auto& t : threads) t.join();
648 }
649 else
650 {
651 bool done=false;
652
653 float invDiffPhotons = 1.f / (float)nDiffusePhotons;
654 float s1, s2, s3, s4, s5, s6, s7, sL;
655
656 while(!done)
657 {
658 if(scene->getSignals() & Y_SIG_ABORT) { pb->done(); if(!intpb) delete pb; return false; }
659
660 s1 = RI_vdC(curr);
661 s2 = scrHalton(2, curr);
662 s3 = scrHalton(3, curr);
663 s4 = scrHalton(4, curr);
664
665 sL = float(curr) * invDiffPhotons;
666 int lightNum = lightPowerD->DSample(sL, &lightNumPdf);
667 if(lightNum >= numDLights)
668 {
669 Y_ERROR << integratorName << ": lightPDF sample error! " << sL << "/" << lightNum << "... stopping now." << yendl;
670 delete lightPowerD;
671 return false;
672 }
673
674 pcol = tmplights[lightNum]->emitPhoton(s1, s2, s3, s4, ray, lightPdf);
675 ray.tmin = scene->rayMinDist;
676 ray.tmax = -1.0;
677 pcol *= fNumLights*lightPdf/lightNumPdf; //remember that lightPdf is the inverse of th pdf, hence *=...
678
679 if(pcol.isBlack())
680 {
681 ++curr;
682 done = (curr >= nDiffusePhotons);
683 continue;
684 }
685
686 int nBounces=0;
687 bool causticPhoton = false;
688 bool directPhoton = true;
689 const material_t *material = nullptr;
690 BSDF_t bsdfs;
691
692 while( scene->intersect(ray, sp) )
693 {
694 if(std::isnan(pcol.R) || std::isnan(pcol.G) || std::isnan(pcol.B))
695 {
696 Y_WARNING << integratorName << ": NaN on photon color for light" << lightNum + 1 << "." << yendl;
697 continue;
698 }
699
700 color_t transm(1.f);
701 color_t vcol(0.f);
702 const volumeHandler_t* vol = nullptr;
703
704 if(material)
705 {
706 if((bsdfs&BSDF_VOLUMETRIC) && (vol=material->getVolumeHandler(sp.Ng * -ray.dir < 0)))
707 {
708 if(vol->transmittance(state, ray, vcol)) transm = vcol;
709 }
710 }
711
712 vector3d_t wi = -ray.dir, wo;
713 material = sp.material;
714 material->initBSDF(state, sp, bsdfs);
715
716 if(bsdfs & (BSDF_DIFFUSE))
717 {
718 //deposit photon on surface
719 if(!causticPhoton)
720 {
721 photon_t np(wi, sp.P, pcol);
722 session.diffuseMap->pushPhoton(np);
723 session.diffuseMap->setNumPaths(curr);
724 }
725 // create entry for radiance photon:
726 // don't forget to choose subset only, face normal forward; geometric vs. smooth normal?
727 if(finalGather && ourRandom() < 0.125 && !causticPhoton )
728 {
729 vector3d_t N = FACE_FORWARD(sp.Ng, sp.N, wi);
730 radData_t rd(sp.P, N);
731 rd.refl = material->getReflectivity(state, sp, BSDF_DIFFUSE | BSDF_GLOSSY | BSDF_REFLECT);
732 rd.transm = material->getReflectivity(state, sp, BSDF_DIFFUSE | BSDF_GLOSSY | BSDF_TRANSMIT);
733 pgdat.rad_points.push_back(rd);
734 }
735 }
736 // need to break in the middle otherwise we scatter the photon and then discard it => redundant
737 if(nBounces == maxBounces) break;
738 // scatter photon
739 int d5 = 3*nBounces + 5;
740
741 s5 = scrHalton(d5, curr);
742 s6 = scrHalton(d5+1, curr);
743 s7 = scrHalton(d5+2, curr);
744
745 pSample_t sample(s5, s6, s7, BSDF_ALL, pcol, transm);
746
747 bool scattered = material->scatterPhoton(state, sp, wi, wo, sample);
748 if(!scattered) break; //photon was absorped.
749
750 pcol = sample.color;
751
752 causticPhoton = ((sample.sampledFlags & (BSDF_GLOSSY | BSDF_SPECULAR | BSDF_DISPERSIVE)) && directPhoton) ||
753 ((sample.sampledFlags & (BSDF_GLOSSY | BSDF_SPECULAR | BSDF_FILTER | BSDF_DISPERSIVE)) && causticPhoton);
754 directPhoton = (sample.sampledFlags & BSDF_FILTER) && directPhoton;
755
756 ray.from = sp.P;
757 ray.dir = wo;
758 ray.tmin = scene->rayMinDist;
759 ray.tmax = -1.0;
760 ++nBounces;
761 }
762 ++curr;
763 if(curr % pbStep == 0) pb->update();
764 done = (curr >= nDiffusePhotons);
765 }
766 }
767
768 pb->done();
769 pb->setTag("Diffuse photon map built.");
770 Y_VERBOSE << integratorName << ": Diffuse photon map built." << yendl;
771 Y_INFO << integratorName << ": Shot "<<curr<<" photons from " << numDLights << " light(s)" << yendl;
772
773 delete lightPowerD;
774
775 tmplights.clear();
776
777 if(session.diffuseMap->nPhotons() < 50)
778 {
779 Y_ERROR << integratorName << ": Too few diffuse photons, stopping now." << yendl;
780 return false;
781 }
782
783 Y_VERBOSE << integratorName << ": Stored diffuse photons: " << session.diffuseMap->nPhotons() << yendl;
784 }
785 else
786 {
787 Y_INFO << integratorName << ": Diffuse photon mapping disabled, skipping..." << yendl;
788 }
789
790 std::thread * diffuseMapBuildKdTree_thread = nullptr;
791
792 if( usePhotonDiffuse && session.diffuseMap->nPhotons() > 0 && scene->getNumThreadsPhotons() >= 2)
793 {
794 Y_INFO << integratorName << ": Building diffuse photons kd-tree:" << yendl;
795 pb->setTag("Building diffuse photons kd-tree...");
796
797 diffuseMapBuildKdTree_thread = new std::thread(&photonIntegrator_t::photonMapKdTreeWorker, this, session.diffuseMap);
798 }
799 else
800
801 if( usePhotonDiffuse && session.diffuseMap->nPhotons() > 0)
802 {
803 Y_INFO << integratorName << ": Building diffuse photons kd-tree:" << yendl;
804 pb->setTag("Building diffuse photons kd-tree...");
805 session.diffuseMap->updateTree();
806 Y_VERBOSE << integratorName << ": Done." << yendl;
807 }
808
809 for(int i=0;i<(int)lights.size();++i)
810 {
811 if(lights[i]->shootsCausticP())
812 {
813 numCLights++;
814 tmplights.push_back(lights[i]);
815 }
816 }
817
818 if(numCLights == 0)
819 {
820 Y_WARNING << integratorName << ": No lights found that can shoot caustic photons, disabling Caustic photon processing" << yendl;
821 enableCaustics(false);
822 }
823
824 if( usePhotonCaustics )
825 {
826 curr=0;
827
828 fNumLights = (float)numCLights;
829 energies = new float[numCLights];
830
831 for(int i=0;i<numCLights;++i) energies[i] = tmplights[i]->totalEnergy().energy();
832
833 lightPowerD = new pdf1D_t(energies, numCLights);
834
835 Y_VERBOSE << integratorName << ": Light(s) photon color testing for caustics map:" << yendl;
836 for(int i=0;i<numCLights;++i)
837 {
838 pcol = tmplights[i]->emitPhoton(.5, .5, .5, .5, ray, lightPdf);
839 lightNumPdf = lightPowerD->func[i] * lightPowerD->invIntegral;
840 pcol *= fNumLights*lightPdf/lightNumPdf; //remember that lightPdf is the inverse of the pdf, hence *=...
841 Y_VERBOSE << integratorName << ": Light [" << i+1 << "] Photon col:" << pcol << " | lnpdf: " << lightNumPdf << yendl;
842 }
843
844 delete[] energies;
845
846 Y_INFO << integratorName << ": Building caustics photon map..." << yendl;
847 pb->init(128);
848 pbStep = std::max(1U, nCausPhotons / 128);
849 pb->setTag("Building caustics photon map...");
850 //Pregather caustic photons
851
852 int nThreads = scene->getNumThreadsPhotons();
853
854 nCausPhotons = std::max((unsigned int) nThreads, (nCausPhotons / nThreads) * nThreads); //rounding the number of diffuse photons so it's a number divisible by the number of threads (distribute uniformly among the threads). At least 1 photon per thread
855
856 Y_PARAMS << integratorName << ": Shooting "<<nCausPhotons<<" photons across " << nThreads << " threads (" << (nCausPhotons / nThreads) << " photons/thread)"<< yendl;
857
858
859 if(nThreads >= 2)
860 {
861 std::vector<std::thread> threads;
862 for(int i=0; i<nThreads; ++i) threads.push_back(std::thread(&photonIntegrator_t::causticWorker, this, session.causticMap, i, scene, nCausPhotons, lightPowerD, numCLights, std::ref(integratorName), tmplights, causDepth, pb, pbStep, std::ref(curr), maxBounces));
863 for(auto& t : threads) t.join();
864 }
865 else
866 {
867 bool done=false;
868 float invCaustPhotons = 1.f / (float)nCausPhotons;
869 float s1, s2, s3, s4, s5, s6, s7, sL;
870
871 while(!done)
872 {
873 if(scene->getSignals() & Y_SIG_ABORT) { pb->done(); if(!intpb) delete pb; return false; }
874 state.chromatic = true;
875 state.wavelength = scrHalton(5,curr);
876
877 s1 = RI_vdC(curr);
878 s2 = scrHalton(2, curr);
879 s3 = scrHalton(3, curr);
880 s4 = scrHalton(4, curr);
881
882 sL = float(curr) * invCaustPhotons;
883 int lightNum = lightPowerD->DSample(sL, &lightNumPdf);
884
885 if(lightNum >= numCLights)
886 {
887 Y_ERROR << integratorName << ": lightPDF sample error! "<<sL<<"/"<<lightNum<<"... stopping now." << yendl;
888 delete lightPowerD;
889 return false;
890 }
891
892 pcol = tmplights[lightNum]->emitPhoton(s1, s2, s3, s4, ray, lightPdf);
893 ray.tmin = scene->rayMinDist;
894 ray.tmax = -1.0;
895 pcol *= fNumLights*lightPdf/lightNumPdf; //remember that lightPdf is the inverse of th pdf, hence *=...
896 if(pcol.isBlack())
897 {
898 ++curr;
899 done = (curr >= nCausPhotons);
900 continue;
901 }
902 int nBounces=0;
903 bool causticPhoton = false;
904 bool directPhoton = true;
905 const material_t *material = nullptr;
906 BSDF_t bsdfs;
907
908 while( scene->intersect(ray, sp) )
909 {
910 if(std::isnan(pcol.R) || std::isnan(pcol.G) || std::isnan(pcol.B))
911 {
912 Y_WARNING << integratorName << ": NaN on photon color for light" << lightNum + 1 << "." << yendl;
913 continue;
914 }
915
916 color_t transm(1.f);
917 color_t vcol(0.f);
918 const volumeHandler_t* vol = nullptr;
919
920 if(material)
921 {
922 if((bsdfs&BSDF_VOLUMETRIC) && (vol=material->getVolumeHandler(sp.Ng * -ray.dir < 0)))
923 {
924 if(vol->transmittance(state, ray, vcol)) transm = vcol;
925 }
926 }
927
928 vector3d_t wi = -ray.dir, wo;
929 material = sp.material;
930 material->initBSDF(state, sp, bsdfs);
931
932 if(bsdfs & BSDF_DIFFUSE)
933 {
934 if(causticPhoton)
935 {
936 photon_t np(wi, sp.P, pcol);
937 session.causticMap->pushPhoton(np);
938 session.causticMap->setNumPaths(curr);
939 }
940 }
941
942 // need to break in the middle otherwise we scatter the photon and then discard it => redundant
943 if(nBounces == maxBounces) break;
944 // scatter photon
945 int d5 = 3*nBounces + 5;
946
947 s5 = scrHalton(d5, curr);
948 s6 = scrHalton(d5+1, curr);
949 s7 = scrHalton(d5+2, curr);
950
951 pSample_t sample(s5, s6, s7, BSDF_ALL, pcol, transm);
952
953 bool scattered = material->scatterPhoton(state, sp, wi, wo, sample);
954 if(!scattered) break; //photon was absorped.
955
956 pcol = sample.color;
957
958 causticPhoton = ((sample.sampledFlags & (BSDF_GLOSSY | BSDF_SPECULAR | BSDF_DISPERSIVE)) && directPhoton) ||
959 ((sample.sampledFlags & (BSDF_GLOSSY | BSDF_SPECULAR | BSDF_FILTER | BSDF_DISPERSIVE)) && causticPhoton);
960 directPhoton = (sample.sampledFlags & BSDF_FILTER) && directPhoton;
961
962 if(state.chromatic && (sample.sampledFlags & BSDF_DISPERSIVE))
963 {
964 state.chromatic=false;
965 color_t wl_col;
966 wl2rgb(state.wavelength, wl_col);
967 pcol *= wl_col;
968 }
969
970 ray.from = sp.P;
971 ray.dir = wo;
972 ray.tmin = scene->rayMinDist;
973 ray.tmax = -1.0;
974 ++nBounces;
975 }
976 ++curr;
977 if(curr % pbStep == 0) pb->update();
978 done = (curr >= nCausPhotons);
979 }
980 }
981
982 pb->done();
983 pb->setTag("Caustics photon map built.");
984 delete lightPowerD;
985
986 Y_INFO << integratorName << ": Shot "<<curr<<" caustic photons from " << numCLights <<" light(s)." << yendl;
987 Y_VERBOSE << integratorName << ": Stored caustic photons: " << session.causticMap->nPhotons() << yendl;
988 }
989 else
990 {
991 Y_INFO << integratorName << ": Caustics photon mapping disabled, skipping..." << yendl;
992 }
993
994 tmplights.clear();
995
996 std::thread * causticMapBuildKdTree_thread = nullptr;
997
998 if(usePhotonCaustics && session.causticMap->nPhotons() > 0 && scene->getNumThreadsPhotons() >= 2)
999 {
1000 Y_INFO << integratorName << ": Building caustic photons kd-tree:" << yendl;
1001 pb->setTag("Building caustic photons kd-tree...");
1002
1003 causticMapBuildKdTree_thread = new std::thread(&photonIntegrator_t::photonMapKdTreeWorker, this, session.causticMap);
1004 }
1005 else
1006 {
1007 if( usePhotonCaustics && session.causticMap->nPhotons() > 0)
1008 {
1009 Y_INFO << integratorName << ": Building caustic photons kd-tree:" << yendl;
1010 pb->setTag("Building caustic photons kd-tree...");
1011 session.causticMap->updateTree();
1012 Y_VERBOSE << integratorName << ": Done." << yendl;
1013 }
1014 }
1015
1016 if( usePhotonDiffuse && session.diffuseMap->nPhotons() > 0 && scene->getNumThreadsPhotons() >= 2 && diffuseMapBuildKdTree_thread)
1017 {
1018 diffuseMapBuildKdTree_thread->join();
1019 delete diffuseMapBuildKdTree_thread;
1020 diffuseMapBuildKdTree_thread = nullptr;
1021
1022 Y_VERBOSE << integratorName << ": Diffuse photon map: done." << yendl;
1023 }
1024
1025 if (!intpb) delete pb;
1026
1027 if(usePhotonDiffuse && finalGather) //create radiance map:
1028 {
1029 // == remove too close radiance points ==//
1030 kdtree::pointKdTree< radData_t > *rTree = new kdtree::pointKdTree< radData_t >(pgdat.rad_points, "FG Radiance Photon Map", scene->getNumThreadsPhotons());
1031 std::vector< radData_t > cleaned;
1032 for(unsigned int i=0; i<pgdat.rad_points.size(); ++i)
1033 {
1034 if(pgdat.rad_points[i].use)
1035 {
1036 cleaned.push_back(pgdat.rad_points[i]);
1037 eliminatePhoton_t elimProc(pgdat.rad_points[i].normal);
1038 float maxrad = 0.01f*dsRadius; // 10% of diffuse search radius
1039 rTree->lookup(pgdat.rad_points[i].pos, elimProc, maxrad);
1040 }
1041 }
1042 pgdat.rad_points.swap(cleaned);
1043 // ================ //
1044 int nThreads = scene->getNumThreads();
1045 pgdat.radianceVec.resize(pgdat.rad_points.size());
1046 if(intpb) pgdat.pbar = intpb;
1047 else pgdat.pbar = new ConsoleProgressBar_t(80);
1048 pgdat.pbar->init(pgdat.rad_points.size());
1049 pgdat.pbar->setTag("Pregathering radiance data for final gathering...");
1050
1051 std::vector<std::thread> threads;
1052 for(int i=0; i<nThreads; ++i) threads.push_back(std::thread(&photonIntegrator_t::preGatherWorker, this, &pgdat, dsRadius, nDiffuseSearch));
1053 for(auto& t : threads) t.join();
1054
1055 session.radianceMap->swapVector(pgdat.radianceVec);
1056 pgdat.pbar->done();
1057 pgdat.pbar->setTag("Pregathering radiance data done...");
1058 if(!intpb) delete pgdat.pbar;
1059 Y_VERBOSE << integratorName << ": Radiance tree built... Updating the tree..." << yendl;
1060 session.radianceMap->updateTree();
1061 Y_VERBOSE << integratorName << ": Done." << yendl;
1062
1063 delete rTree;
1064 rTree = nullptr;
1065 }
1066
1067 if(usePhotonCaustics && session.causticMap->nPhotons() > 0 && scene->getNumThreadsPhotons() >= 2 && causticMapBuildKdTree_thread)
1068 {
1069 causticMapBuildKdTree_thread->join();
1070 delete causticMapBuildKdTree_thread;
1071 causticMapBuildKdTree_thread = nullptr;
1072
1073 Y_VERBOSE << integratorName << ": Caustic photon map: done." << yendl;
1074 }
1075
1076 if(photonMapProcessing == PHOTONS_GENERATE_AND_SAVE)
1077 {
1078 if( usePhotonDiffuse )
1079 {
1080 pb->setTag("Saving diffuse photon map to file...");
1081 const std::string filename = session.getPathImageOutput() + "_diffuse.photonmap";
1082 Y_INFO << integratorName << ": Saving diffuse photon map to: " << filename << yendl;
1083 if(session.diffuseMap->save(filename)) Y_VERBOSE << integratorName << ": Diffuse map saved." << yendl;
1084 }
1085
1086 if( usePhotonCaustics )
1087 {
1088 pb->setTag("Saving caustic photon map to file...");
1089 const std::string filename = session.getPathImageOutput() + "_caustic.photonmap";
1090 Y_INFO << integratorName << ": Saving caustic photon map to: " << filename << yendl;
1091 if(session.causticMap->save(filename)) Y_VERBOSE << integratorName << ": Caustic map saved." << yendl;
1092 }
1093
1094 if( usePhotonDiffuse && finalGather )
1095 {
1096 pb->setTag("Saving FG radiance photon map to file...");
1097 const std::string filename = session.getPathImageOutput() + "_fg_radiance.photonmap";
1098 Y_INFO << integratorName << ": Saving FG radiance photon map to: " << filename << yendl;
1099 if(session.radianceMap->save(filename)) Y_VERBOSE << integratorName << ": FG radiance map saved." << yendl;
1100 }
1101 }
1102
1103 gTimer.stop("prepass");
1104 Y_INFO << integratorName << ": Photonmap building time: " << std::fixed << std::setprecision(1) << gTimer.getTime("prepass") << "s" << " (" << scene->getNumThreadsPhotons() << " thread(s))" << yendl;
1105
1106 set << "| photon maps: " << std::fixed << std::setprecision(1) << gTimer.getTime("prepass") << "s" << " [" << scene->getNumThreadsPhotons() << " thread(s)]";
1107
1108 yafLog.appendRenderSettings(set.str());
1109
1110 for (std::string line; std::getline(set, line, '\n');) Y_VERBOSE << line << yendl;
1111
1112 return true;
1113 }
1114
1115 // final gathering: this is basically a full path tracer only that it uses the radiance map only
1116 // at the path end. I.e. paths longer than 1 are only generated to overcome lack of local radiance detail.
1117 // precondition: initBSDF of current spot has been called!
finalGathering(renderState_t & state,const surfacePoint_t & sp,const vector3d_t & wo,colorPasses_t & colorPasses) const1118 color_t photonIntegrator_t::finalGathering(renderState_t &state, const surfacePoint_t &sp, const vector3d_t &wo, colorPasses_t &colorPasses) const
1119 {
1120 color_t pathCol(0.0);
1121 void *first_udat = state.userdata;
1122 unsigned char userdata[USER_DATA_SIZE+7];
1123 void *n_udat = (void *)( &userdata[7] - ( ((size_t)&userdata[7])&7 ) ); // pad userdata to 8 bytes
1124 const volumeHandler_t *vol;
1125 color_t vcol(0.f);
1126 float W = 0.f;
1127
1128 colorPasses_t tmpColorPasses(scene->getRenderPasses());
1129
1130 int nSampl = (int) ceilf(std::max(1, nPaths/state.rayDivision)*AA_indirect_sample_multiplier);
1131 for(int i=0; i<nSampl; ++i)
1132 {
1133 color_t throughput( 1.0 );
1134 float length=0;
1135 surfacePoint_t hit=sp;
1136 vector3d_t pwo = wo;
1137 ray_t pRay;
1138 BSDF_t matBSDFs;
1139 bool did_hit;
1140 const material_t *p_mat = sp.material;
1141 unsigned int offs = nPaths * state.pixelSample + state.samplingOffs + i; // some redundancy here...
1142 color_t lcol, scol;
1143 // "zero'th" FG bounce:
1144 float s1 = RI_vdC(offs);
1145 float s2 = scrHalton(2, offs);
1146 if(state.rayDivision > 1)
1147 {
1148 s1 = addMod1(s1, state.dc1);
1149 s2 = addMod1(s2, state.dc2);
1150 }
1151
1152 sample_t s(s1, s2, BSDF_DIFFUSE|BSDF_REFLECT|BSDF_TRANSMIT); // glossy/dispersion/specular done via recursive raytracing
1153 scol = p_mat->sample(state, hit, pwo, pRay.dir, s, W);
1154
1155 scol *= W;
1156 if(scol.isBlack()) continue;
1157
1158 pRay.tmin = scene->rayMinDist;
1159 pRay.tmax = -1.0;
1160 pRay.from = hit.P;
1161 throughput = scol;
1162
1163 if( !(did_hit = scene->intersect(pRay, hit)) ) continue; //hit background
1164
1165 p_mat = hit.material;
1166 length = pRay.tmax;
1167 state.userdata = n_udat;
1168 matBSDFs = p_mat->getFlags();
1169 bool has_spec = matBSDFs & BSDF_SPECULAR;
1170 bool caustic = false;
1171 bool close = length < gatherDist;
1172 bool do_bounce = close || has_spec;
1173 // further bounces construct a path just as with path tracing:
1174 for(int depth=0; depth<gatherBounces && do_bounce; ++depth)
1175 {
1176 int d4 = 4*depth;
1177 pwo = -pRay.dir;
1178 p_mat->initBSDF(state, hit, matBSDFs);
1179
1180 if((matBSDFs & BSDF_VOLUMETRIC) && (vol=p_mat->getVolumeHandler(hit.N * pwo < 0)))
1181 {
1182 if(vol->transmittance(state, pRay, vcol)) throughput *= vcol;
1183 }
1184
1185 if(matBSDFs & (BSDF_DIFFUSE))
1186 {
1187 if(close)
1188 {
1189 lcol = estimateOneDirectLight(state, hit, pwo, offs, tmpColorPasses);
1190 }
1191 else if(caustic)
1192 {
1193 vector3d_t sf = FACE_FORWARD(hit.Ng, hit.N, pwo);
1194 const photon_t *nearest = session.radianceMap->findNearest(hit.P, sf, lookupRad);
1195 if(nearest) lcol = nearest->color();
1196 }
1197
1198 if(close || caustic)
1199 {
1200 if(matBSDFs & BSDF_EMIT) lcol += p_mat->emit(state, hit, pwo);
1201 pathCol += lcol*throughput;
1202 }
1203 }
1204
1205 s1 = scrHalton(d4+3, offs);
1206 s2 = scrHalton(d4+4, offs);
1207
1208 if(state.rayDivision > 1)
1209 {
1210 s1 = addMod1(s1, state.dc1);
1211 s2 = addMod1(s2, state.dc2);
1212 }
1213
1214 sample_t sb(s1, s2, (close) ? BSDF_ALL : BSDF_ALL_SPECULAR | BSDF_FILTER);
1215 scol = p_mat->sample(state, hit, pwo, pRay.dir, sb, W);
1216
1217 if( sb.pdf <= 1.0e-6f)
1218 {
1219 did_hit=false;
1220 break;
1221 }
1222
1223 scol *= W;
1224
1225 pRay.tmin = scene->rayMinDist;
1226 pRay.tmax = -1.0;
1227 pRay.from = hit.P;
1228 throughput *= scol;
1229 did_hit = scene->intersect(pRay, hit);
1230
1231 if(!did_hit) //hit background
1232 {
1233 if(caustic && background && background->hasIBL() && background->shootsCaustic())
1234 {
1235 pathCol += throughput * (*background)(pRay, state, true);
1236 }
1237 break;
1238 }
1239
1240 p_mat = hit.material;
1241 length += pRay.tmax;
1242 caustic = (caustic || !depth) && (sb.sampledFlags & (BSDF_SPECULAR | BSDF_FILTER));
1243 close = length < gatherDist;
1244 do_bounce = caustic || close;
1245 }
1246
1247 if(did_hit)
1248 {
1249 p_mat->initBSDF(state, hit, matBSDFs);
1250 if(matBSDFs & (BSDF_DIFFUSE | BSDF_GLOSSY))
1251 {
1252 vector3d_t sf = FACE_FORWARD(hit.Ng, hit.N, -pRay.dir);
1253 const photon_t *nearest = session.radianceMap->findNearest(hit.P, sf, lookupRad);
1254 if(nearest) lcol = nearest->color();
1255 if(matBSDFs & BSDF_EMIT) lcol += p_mat->emit(state, hit, -pRay.dir);
1256 pathCol += lcol * throughput;
1257 }
1258 }
1259 state.userdata = first_udat;
1260 }
1261 return pathCol / (float)nSampl;
1262 }
1263
integrate(renderState_t & state,diffRay_t & ray,colorPasses_t & colorPasses,int additionalDepth) const1264 colorA_t photonIntegrator_t::integrate(renderState_t &state, diffRay_t &ray, colorPasses_t &colorPasses, int additionalDepth /*=0*/) const
1265 {
1266 static int _nMax=0;
1267 static int calls=0;
1268 ++calls;
1269 color_t col(0.0);
1270 float alpha;
1271 surfacePoint_t sp;
1272
1273 void *o_udat = state.userdata;
1274 bool oldIncludeLights = state.includeLights;
1275
1276 if(transpBackground) alpha=0.0;
1277 else alpha=1.0;
1278
1279 if(scene->intersect(ray, sp))
1280 {
1281 unsigned char userdata[USER_DATA_SIZE+7];
1282 state.userdata = (void *)( &userdata[7] - ( ((size_t)&userdata[7])&7 ) ); // pad userdata to 8 bytes
1283
1284 if(state.raylevel == 0)
1285 {
1286 state.chromatic = true;
1287 state.includeLights = true;
1288 }
1289 BSDF_t bsdfs;
1290 int additionalDepth = 0;
1291
1292 vector3d_t N_nobump = sp.N;
1293 vector3d_t wo = -ray.dir;
1294 const material_t *material = sp.material;
1295 material->initBSDF(state, sp, bsdfs);
1296
1297 if(additionalDepth < material->getAdditionalDepth()) additionalDepth = material->getAdditionalDepth();
1298
1299 col += colorPasses.probe_add(PASS_INT_EMIT, material->emit(state, sp, wo), state.raylevel == 0);
1300
1301 state.includeLights = false;
1302
1303 if(usePhotonDiffuse && finalGather)
1304 {
1305 if(showMap)
1306 {
1307 vector3d_t N = FACE_FORWARD(sp.Ng, sp.N, wo);
1308 const photon_t *nearest = session.radianceMap->findNearest(sp.P, N, lookupRad);
1309 if(nearest) col += nearest->color();
1310 }
1311 else
1312 {
1313 if(state.raylevel == 0 && colorPasses.enabled(PASS_INT_RADIANCE))
1314 {
1315 vector3d_t N = FACE_FORWARD(sp.Ng, sp.N, wo);
1316 const photon_t *nearest = session.radianceMap->findNearest(sp.P, N, lookupRad);
1317 if(nearest) colorPasses(PASS_INT_RADIANCE) = nearest->color();
1318 }
1319
1320 // contribution of light emitting surfaces
1321 if(bsdfs & BSDF_EMIT) col += colorPasses.probe_add(PASS_INT_EMIT, material->emit(state, sp, wo), state.raylevel == 0);
1322
1323 if(bsdfs & BSDF_DIFFUSE)
1324 {
1325 col += estimateAllDirectLight(state, sp, wo, colorPasses);;
1326
1327 if(AA_clamp_indirect>0.f)
1328 {
1329 color_t tmpCol = finalGathering(state, sp, wo, colorPasses);
1330 tmpCol.clampProportionalRGB(AA_clamp_indirect);
1331 col += colorPasses.probe_set(PASS_INT_DIFFUSE_INDIRECT, tmpCol, state.raylevel == 0);
1332 }
1333 else col += colorPasses.probe_set(PASS_INT_DIFFUSE_INDIRECT, finalGathering(state, sp, wo, colorPasses), state.raylevel == 0);
1334 }
1335 }
1336 }
1337 else
1338 {
1339 if(usePhotonDiffuse && showMap)
1340 {
1341 vector3d_t N = FACE_FORWARD(sp.Ng, sp.N, wo);
1342 const photon_t *nearest = session.diffuseMap->findNearest(sp.P, N, dsRadius);
1343 if(nearest) col += nearest->color();
1344 }
1345 else
1346 {
1347 if(usePhotonDiffuse && state.raylevel == 0 && colorPasses.enabled(PASS_INT_RADIANCE))
1348 {
1349 vector3d_t N = FACE_FORWARD(sp.Ng, sp.N, wo);
1350 const photon_t *nearest = session.radianceMap->findNearest(sp.P, N, lookupRad);
1351 if(nearest) colorPasses(PASS_INT_RADIANCE) = nearest->color();
1352 }
1353
1354 if(bsdfs & BSDF_EMIT) col += colorPasses.probe_add(PASS_INT_EMIT, material->emit(state, sp, wo), state.raylevel == 0);
1355
1356 if(bsdfs & BSDF_DIFFUSE)
1357 {
1358 col += estimateAllDirectLight(state, sp, wo, colorPasses);
1359 }
1360
1361 foundPhoton_t *gathered = (foundPhoton_t *)alloca(nDiffuseSearch * sizeof(foundPhoton_t));
1362 float radius = dsRadius; //actually the square radius...
1363
1364 int nGathered=0;
1365
1366 if(usePhotonDiffuse && session.diffuseMap->nPhotons() > 0) nGathered = session.diffuseMap->gather(sp.P, gathered, nDiffuseSearch, radius);
1367 color_t sum(0.0);
1368 if(usePhotonDiffuse && nGathered > 0)
1369 {
1370 if(nGathered > _nMax) _nMax = nGathered;
1371
1372 float scale = 1.f / ( (float)session.diffuseMap->nPaths() * radius * M_PI);
1373 for(int i=0; i<nGathered; ++i)
1374 {
1375 vector3d_t pdir = gathered[i].photon->direction();
1376 color_t surfCol = material->eval(state, sp, wo, pdir, BSDF_DIFFUSE);
1377
1378 col += colorPasses.probe_add(PASS_INT_DIFFUSE_INDIRECT, surfCol * scale * gathered[i].photon->color(), state.raylevel == 0);
1379 }
1380 }
1381 }
1382 }
1383
1384 // add caustics
1385 if(usePhotonCaustics && bsdfs & BSDF_DIFFUSE)
1386 {
1387 if(AA_clamp_indirect>0.f)
1388 {
1389 color_t tmpCol = estimateCausticPhotons(state, sp, wo);
1390 tmpCol.clampProportionalRGB(AA_clamp_indirect);
1391 col += colorPasses.probe_set(PASS_INT_INDIRECT, tmpCol, state.raylevel == 0);
1392 }
1393 else col += colorPasses.probe_set(PASS_INT_INDIRECT, estimateCausticPhotons(state, sp, wo), state.raylevel == 0);
1394 }
1395
1396 recursiveRaytrace(state, ray, bsdfs, sp, wo, col, alpha, colorPasses, additionalDepth);
1397
1398 if(colorPasses.size() > 1 && state.raylevel == 0)
1399 {
1400 generateCommonRenderPasses(colorPasses, state, sp, ray);
1401
1402 if(colorPasses.enabled(PASS_INT_AO))
1403 {
1404 colorPasses(PASS_INT_AO) = sampleAmbientOcclusionPass(state, sp, wo);
1405 }
1406
1407 if(colorPasses.enabled(PASS_INT_AO_CLAY))
1408 {
1409 colorPasses(PASS_INT_AO_CLAY) = sampleAmbientOcclusionPassClay(state, sp, wo);
1410 }
1411 }
1412
1413 if(transpRefractedBackground)
1414 {
1415 float m_alpha = material->getAlpha(state, sp, wo);
1416 alpha = m_alpha + (1.f-m_alpha)*alpha;
1417 }
1418 else alpha = 1.0;
1419 }
1420 else //nothing hit, return background
1421 {
1422 if(background && !transpRefractedBackground)
1423 {
1424 col += colorPasses.probe_set(PASS_INT_ENV, (*background)(ray, state), state.raylevel == 0);
1425 }
1426 }
1427
1428 state.userdata = o_udat;
1429 state.includeLights = oldIncludeLights;
1430
1431 color_t colVolTransmittance = scene->volIntegrator->transmittance(state, ray);
1432 color_t colVolIntegration = scene->volIntegrator->integrate(state, ray, colorPasses);
1433
1434 if(transpBackground) alpha = std::max(alpha, 1.f-colVolTransmittance.R);
1435
1436 colorPasses.probe_set(PASS_INT_VOLUME_TRANSMITTANCE, colVolTransmittance);
1437 colorPasses.probe_set(PASS_INT_VOLUME_INTEGRATION, colVolIntegration);
1438
1439 col = (col * colVolTransmittance) + colVolIntegration;
1440
1441 return colorA_t(col, alpha);
1442 }
1443
factory(paraMap_t & params,renderEnvironment_t & render)1444 integrator_t* photonIntegrator_t::factory(paraMap_t ¶ms, renderEnvironment_t &render)
1445 {
1446 bool transpShad=false;
1447 bool finalGather=true;
1448 bool show_map=false;
1449 int shadowDepth=5;
1450 int raydepth=5;
1451 int numPhotons = 100000;
1452 int numCPhotons = 500000;
1453 int search = 50;
1454 int caustic_mix = 50;
1455 int bounces = 5;
1456 int fgPaths = 32;
1457 int fgBounces = 2;
1458 float dsRad=0.1;
1459 float cRad=0.01;
1460 float gatherDist=0.2;
1461 bool do_AO=false;
1462 int AO_samples = 32;
1463 double AO_dist = 1.0;
1464 color_t AO_col(1.f);
1465 bool bg_transp = false;
1466 bool bg_transp_refract = false;
1467 bool caustics = true;
1468 bool diffuse = true;
1469 std::string photon_maps_processing_str = "generate";
1470
1471 params.getParam("caustics", caustics);
1472 params.getParam("diffuse", diffuse);
1473
1474 params.getParam("transpShad", transpShad);
1475 params.getParam("shadowDepth", shadowDepth);
1476 params.getParam("raydepth", raydepth);
1477 params.getParam("photons", numPhotons);
1478 params.getParam("cPhotons", numCPhotons);
1479 params.getParam("diffuseRadius", dsRad);
1480 params.getParam("causticRadius", cRad);
1481 params.getParam("search", search);
1482 caustic_mix = search;
1483 params.getParam("caustic_mix", caustic_mix);
1484 params.getParam("bounces", bounces);
1485 params.getParam("finalGather", finalGather);
1486 params.getParam("fg_samples", fgPaths);
1487 params.getParam("fg_bounces", fgBounces);
1488 gatherDist = dsRad;
1489 params.getParam("fg_min_pathlen", gatherDist);
1490 params.getParam("show_map", show_map);
1491 params.getParam("bg_transp", bg_transp);
1492 params.getParam("bg_transp_refract", bg_transp_refract);
1493 params.getParam("do_AO", do_AO);
1494 params.getParam("AO_samples", AO_samples);
1495 params.getParam("AO_distance", AO_dist);
1496 params.getParam("AO_color", AO_col);
1497 params.getParam("photon_maps_processing", photon_maps_processing_str);
1498
1499 photonIntegrator_t* ite = new photonIntegrator_t(numPhotons, numCPhotons, transpShad, shadowDepth, dsRad, cRad);
1500
1501 ite->usePhotonCaustics = caustics;
1502 ite->usePhotonDiffuse = diffuse;
1503
1504 ite->rDepth = raydepth;
1505 ite->nDiffuseSearch = search;
1506 ite->nCausSearch = caustic_mix;
1507 ite->finalGather = finalGather;
1508 ite->maxBounces = bounces;
1509 ite->causDepth = bounces;
1510 ite->nPaths = fgPaths;
1511 ite->gatherBounces = fgBounces;
1512 ite->showMap = show_map;
1513 ite->gatherDist = gatherDist;
1514 // Background settings
1515 ite->transpBackground = bg_transp;
1516 ite->transpRefractedBackground = bg_transp_refract;
1517 // AO settings
1518 ite->useAmbientOcclusion = do_AO;
1519 ite->aoSamples = AO_samples;
1520 ite->aoDist = AO_dist;
1521 ite->aoCol = AO_col;
1522
1523 if(photon_maps_processing_str == "generate-save") ite->photonMapProcessing = PHOTONS_GENERATE_AND_SAVE;
1524 else if(photon_maps_processing_str == "load") ite->photonMapProcessing = PHOTONS_LOAD;
1525 else if(photon_maps_processing_str == "reuse-previous") ite->photonMapProcessing = PHOTONS_REUSE;
1526 else ite->photonMapProcessing = PHOTONS_GENERATE_ONLY;
1527
1528 return ite;
1529 }
1530
1531 extern "C"
1532 {
1533
registerPlugin(renderEnvironment_t & render)1534 YAFRAYPLUGIN_EXPORT void registerPlugin(renderEnvironment_t &render)
1535 {
1536 render.registerFactory("photonmapping", photonIntegrator_t::factory);
1537 }
1538
1539 }
1540
1541 __END_YAFRAY
1542