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 &params, 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