SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLPathtracer.cpp
Go to the documentation of this file.
1 /**
2  * \file SLPathtracer.cpp
3  * \authors Thomas Schneiter
4  * \date July 2014
5  * \authors Marcus Hudritsch
6  * \copyright http://opensource.org/licenses/GPL-3.0
7  * \remarks Please use clangformat to format the code. See more code style on
8  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
9  */
10 
11 #include <algorithm>
12 
13 #include <SLCamera.h>
14 #include <SLLightRect.h>
15 #include <SLPathtracer.h>
16 #include <SLSceneView.h>
17 #include <GlobalTimer.h>
18 #include <Profiler.h>
19 
20 extern SLfloat rnd01();
21 
22 //-----------------------------------------------------------------------------
24 {
25  name("PathTracer");
26  _calcDirect = true;
27  _calcIndirect = true;
28  gamma(2.2f);
29 }
30 //-----------------------------------------------------------------------------
31 /*!
32 Main render function. The Path Tracing algorithm starts from here
33 */
35 {
36  _sv = sv;
37  _state = rtBusy; // From here we state the PT as busy
38  _renderSec = 0.0f; // reset time
39  _progressPC = 0; // % rendered
40 
41  initStats(0); // init statistics
42  prepareImage();
43 
44  // Set second image for render update to the same size
45  while (_images.size() > 1)
46  {
47  delete _images[_images.size() - 1];
48  _images.pop_back();
49  }
50  _images.push_back(new CVImage(_sv->viewportW(),
51  _sv->viewportH(),
52  PF_rgb,
53  "Pathtracer"));
54  // Measure time
55  double t1 = GlobalTimer::timeS();
56 
57  // Lambda function for async slice rendering
58  renderSlicesPTAsync = [this](bool isMainThread, SLint curSample, SLuint threadNum)
59  {
60  SLPathtracer::renderSlices(isMainThread, curSample, threadNum);
61  };
62 
63  // Do multi-threading only in release config
64  SL_LOG("\n\nRendering with %d samples", _aaSamples);
65  SL_LOG("\nCurrent Sample: ");
66  for (int currentSample = 1; currentSample <= _aaSamples; currentSample++)
67  {
68  vector<thread> threads; // vector for additional threads
69  _nextLine = 0;
70 
71  // Start additional threads on the renderSlices function
72  for (SLuint t = 0; t < Utils::maxThreads() - 1; t++)
73  threads.emplace_back(renderSlicesPTAsync, false, currentSample, t);
74 
75  // Do the same work in the main thread
76  renderSlicesPTAsync(true, currentSample, 0);
77 
78  for (auto& thread : threads)
79  thread.join();
80 
81  _progressPC = (SLint)((SLfloat)currentSample / (SLfloat)_aaSamples * 100.0f);
82  }
83 
85  _raysPerMS.set((float)SLRay::totalNumRays() / _renderSec / 1000.0f);
86  _progressPC = 100;
87 
88  SL_LOG("\nTime to render image: %6.3fsec", _renderSec);
89 
91  return true;
92 }
93 //-----------------------------------------------------------------------------
94 /*!
95 Renders a slice of 4px width.
96 */
97 void SLPathtracer::renderSlices(const bool isMainThread,
98  SLint currentSample,
99  SLuint threadNum)
100 {
101  if (!isMainThread)
102  {
103  PROFILE_THREAD(string("PT-Worker-") + std::to_string(threadNum));
104  }
105 
107 
108  // Time points
109  double t1 = 0;
110 
111  while (_nextLine < (SLint)_images[0]->width())
112  {
113  // The next section must be protected
114  // Making _nextLine an atomic was not sufficient.
115  _mutex.lock();
116  SLint minX = _nextLine;
117  _nextLine += 4;
118  _mutex.unlock();
119 
120  for (SLint x = minX; x < minX + 4; ++x)
121  {
122  for (SLuint y = 0; y < _images[0]->height(); ++y)
123  {
124  SLCol4f color(SLCol4f::BLACK);
125 
126  // calculate direction for primary ray - scatter with random variables for anti aliasing
127  SLRay primaryRay;
128  setPrimaryRay((SLfloat)((SLfloat)x - rnd01() + 0.5f),
129  (SLfloat)((SLfloat)y - rnd01() + 0.5f),
130  &primaryRay);
131 
132  ///////////////////////////////////
133  color += trace(&primaryRay, false);
134  ///////////////////////////////////
135 
136  // weight old and new color for continuous rendering
137  SLCol4f oldColor;
138  if (currentSample > 1)
139  {
140  CVVec4f c4f = _images[1]->getPixeli(x, (SLint)y);
141  oldColor.set(c4f[0], c4f[1], c4f[2], c4f[3]);
142 
143  // weight old color (examp. 3/4, 4/5, 5/6)
144  oldColor /= (SLfloat)currentSample;
145  oldColor *= (SLfloat)(currentSample - 1);
146 
147  // weight new color (examp. 1/4, 1/5, 1/6)
148  color /= (SLfloat)currentSample;
149 
150  // bring them together (examp. 4/4, 5/5, 6/6)
151  color += oldColor;
152  }
153 
154  color.clampMinMax(0.0f, 1.0f);
155 
156  // save image without gamma
157  _images[1]->setPixeliRGB(x,
158  (SLint)y,
159  CVVec4f(color.r,
160  color.g,
161  color.b,
162  color.a));
163 
165 
166  // image to render
167  _images[0]->setPixeliRGB(x,
168  (SLint)y,
169  CVVec4f(color.r,
170  color.g,
171  color.b,
172  color.a));
173  }
174 
175  // update image after 500 ms
176  if (_sv->onWndUpdate && isMainThread)
177  {
178  if (GlobalTimer::timeS() - t1 > 0.5f)
179  {
181  _sv->onWndUpdate(); // update window
182  t1 = GlobalTimer::timeS();
183  }
184  }
185  }
186  }
187 }
188 //-----------------------------------------------------------------------------
189 /*!
190 Recursively traces ray in scene.
191 */
193 {
194  SLCol4f finalColor(ray->backgroundColor);
195 
196  // Participating Media init
197  SLfloat absorption = 1.0f; // used to calculate absorption along the ray
198  SLfloat scaleBy = 1.0f; // used to scale surface reflectance at the end of random walk
199 
200  // Intersect scene
201  SLNode* root = _sv->s()->root3D();
202  if (root) root->hitRec(ray);
203 
204  // end of recursion - no object hit OR max depth reached
205  if (ray->length >= FLT_MAX || ray->depth > maxDepth())
206  return SLCol4f::BLACK;
207 
208  // hit material
209  SLMaterial* mat = ray->hitMesh->mat();
210  ray->hitMesh->preShade(ray);
211 
212  // set object color
213  SLCol4f objectColor = SLCol4f::BLACK;
214  if (ray->hitMatIsDiffuse())
215  objectColor = mat->diffuse();
216  else if (ray->hitMatIsReflective())
217  objectColor = mat->specular();
218  else if (ray->hitMatIsTransparent())
219  objectColor = mat->transmissive();
220 
221  // set object emission
222  SLCol4f objectEmission = mat->emissive();
223  SLfloat maxEmission = objectEmission.maxXYZ();
224 
225  // stop recursion if light source is hit
226  if (maxEmission > 0)
227  {
228  if (ray->depth == 1)
229  return mat->emissive() * absorption;
230  else
231  return mat->emissive() * absorption * em;
232  }
233 
234  // add absorption to base color from Participating Media
235  objectColor = objectColor * absorption;
236 
237  // diffuse reflection
238  if (ray->hitMatIsDiffuse())
239  {
240  // Add component wise the texture color
241  if (mat->numTextures() > 0)
242  {
243  objectColor &= ray->hitTexColor;
244  }
245 
246  if (_calcDirect)
247  finalColor += shade(ray, &objectColor) * scaleBy;
248 
249  if (_calcIndirect)
250  {
251  SLRay scatter;
252  ray->diffuseMC(&scatter);
253 
254  // material emission, material diffuse and recursive indirect illumination
255  finalColor += (trace(&scatter, false) & objectColor) * scaleBy;
256  }
257  }
258  else if (ray->hitMatIsReflective())
259  {
260  // scatter toward perfect specular direction
261  SLRay reflected;
262  ray->reflect(&reflected);
263 
264  // scatter around perfect reflected direction only if material not perfect
265  if (mat->shininess() < SLMaterial::PERFECT)
266  {
267  // rotation matrix for glossy
268  SLMat3f rotMat;
269  SLVec3f rotAxis((SLVec3f(0.0f, 0.0f, 1.0f) ^ reflected.dir).normalize());
270  SLfloat rotAngle = acos(reflected.dir.z);
271  rotMat.rotation(rotAngle * 180.0f * Utils::ONEOVERPI, rotAxis);
272  ray->reflectMC(&reflected, rotMat);
273  }
274 
275  // shininess contribution * recursive indirect illumination and matrial base color
276  finalColor += ((mat->shininess() + 2.0f) / (mat->shininess() + 1.0f) *
277  (trace(&reflected, true) & objectColor)) *
278  scaleBy;
279  }
280  else if (ray->hitMatIsTransparent())
281  {
282  // scatter toward perfect transmissive direction
283  SLRay refracted;
284  ray->refract(&refracted);
285 
286  // init Schlick's approximation
287  SLVec3f rayDir = ray->dir;
288  rayDir.normalize();
289  SLVec3f refrDir = refracted.dir;
290  refrDir.normalize();
291  SLfloat n, nt;
292  SLVec3f hitNormal = ray->hitNormal;
293  hitNormal.normalize();
294 
295  // ray from outside in
296  if (ray->isOutside)
297  {
298  n = 1.0f;
299  nt = mat->kn();
300  }
301  else // ray from inside out
302  {
303  n = mat->kn();
304  nt = 1.0f;
305  }
306 
307  // calculate Schlick's approx.
308  SLfloat nbig, nsmall;
309  nbig = n > nt ? n : nt;
310  nsmall = n < nt ? n : nt;
311  SLfloat R0 = ((nbig - nsmall) / (nbig + nsmall));
312  R0 = R0 * R0;
313  SLbool into = (rayDir * hitNormal) < 0;
314  SLfloat c = 1.0f - (into ? (-rayDir * hitNormal) : (refrDir * hitNormal));
315  SLfloat schlick = R0 + (1 - R0) * c * c * c * c * c;
316 
317  SLfloat P = 0.25f + 0.5f * schlick; // probability of reflectance
318  SLfloat reflectionProbability = schlick / P;
319  SLfloat refractionProbability = (1.0f - schlick) / (1.0f - P);
320 
321  // scatter around perfect transmissive direction only if material not perfect
322  if (mat->translucency() < SLMaterial::PERFECT)
323  {
324  // rotation matrix for translucency
325  SLMat3f rotMat;
326  SLVec3f rotAxis((SLVec3f(0.0f, 0.0f, 1.0f) ^ refracted.dir).normalize());
327  SLfloat rotAngle = acos(refracted.dir.z);
328  rotMat.rotation((SLfloat)(rotAngle * 180.0f * Utils::ONEOVERPI), rotAxis);
329  ray->refractMC(&refracted, rotMat);
330  }
331 
332  // probability of reflection
333  if (rnd01() > (0.25f + 0.5f * schlick))
334  // scatter toward transmissive direction
335  finalColor += ((mat->translucency() + 2.0f) /
336  (mat->translucency() + 1.0f) *
337  (trace(&refracted, true) & objectColor) *
338  refractionProbability) *
339  scaleBy;
340  else
341  {
342  // scatter toward perfect specular direction
343  SLRay scattered;
344  ray->reflect(&scattered);
345 
346  // shininess contribution * recursive indirect illumination and material basecolor
347  finalColor += ((mat->shininess() + 2.0f) /
348  (mat->shininess() + 1.0f) *
349  (trace(&scattered, true) & objectColor) *
350  reflectionProbability) *
351  scaleBy;
352  }
353  }
354 
355  return finalColor;
356 }
357 //-----------------------------------------------------------------------------
358 /*!
359 Calculates direct illumination for intersection point of ray
360 */
362 {
363  SLCol4f color = SLCol4f::BLACK;
364  SLCol4f diffuseColor = SLCol4f::BLACK;
365  SLVec3f L, N;
366  SLfloat lightDist, LdN, df, spotEffect, lighted;
367 
368  // loop over light sources in scene
369  for (auto* light : _sv->s()->lights())
370  {
371  if (light && light->isOn())
372  {
373  N.set(ray->hitNormal);
374  L.sub(light->positionWS().vec3(), ray->hitPoint);
375  lightDist = L.length();
376  L /= lightDist;
377  LdN = L.dot(N);
378 
379  // check shadow ray if hit point is towards the light
380  lighted = (SLfloat)((LdN > 0) ? light->shadowTestMC(ray,
381  L,
382  lightDist,
383  _sv->s()->root3D())
384  : 0);
385 
386  // calculate spot effect if light is a spotlight
387  if (lighted > 0.0f && light->spotCutOffDEG() < 180.0f)
388  {
389  SLfloat LdS = std::max(-L.dot(light->spotDirWS()), 0.0f);
390 
391  // check if point is in spot cone
392  if (LdS > light->spotCosCut())
393  {
394  spotEffect = pow(LdS, (SLfloat)light->spotExponent());
395  }
396  else
397  {
398  lighted = 0.0f;
399  spotEffect = 0.0f;
400  }
401  }
402  else
403  spotEffect = 1.0f;
404 
405  if (lighted > 0.0f)
406  {
407  df = std::max(LdN, 0.0f); // diffuse factor
408 
409  // material color * light emission * LdN * brdf(1/pi) * lighted(for soft shadows)
410  diffuseColor = (*mat & (light->diffuse() * df) * Utils::ONEOVERPI * lighted);
411  }
412 
413  color += light->attenuation(lightDist) * spotEffect * diffuseColor;
414  }
415  }
416 
417  return color;
418 }
419 //-----------------------------------------------------------------------------
420 //! Saves the current PT image as PNG image
422 {
423  static SLint no = 0;
424  SLchar filename[255];
425  snprintf(filename,
426  sizeof(filename),
427  "Pathtraced_%d_%d.png",
428  _aaSamples,
429  no++);
430  _images[0]->savePNG(filename);
431 }
432 //-----------------------------------------------------------------------------
@ PF_rgb
Definition: CVImage.h:36
cv::Vec4f CVVec4f
Definition: CVTypedefs.h:54
#define PROFILE_FUNCTION()
Definition: Instrumentor.h:41
#define PROFILE_THREAD(name)
Definition: Profiler.h:38
float SLfloat
Definition: SL.h:173
#define SL_LOG(...)
Definition: SL.h:233
unsigned int SLuint
Definition: SL.h:171
char SLchar
Definition: SL.h:162
bool SLbool
Definition: SL.h:175
int SLint
Definition: SL.h:170
SLSceneView * sv
Definition: SLGLImGui.h:28
SLfloat rnd01()
Definition: SLRay.cpp:39
@ rtBusy
Definition: SLRaytracer.h:30
@ rtFinished
Definition: SLRaytracer.h:31
SLVec3< SLfloat > SLVec3f
Definition: SLVec3.h:318
OpenCV image class with the same interface as the former SLImage class.
Definition: CVImage.h:64
static float timeS()
Definition: GlobalTimer.cpp:20
SLuint width()
Definition: SLGLTexture.h:218
CVVImage _images
Vector of CVImage pointers.
Definition: SLGLTexture.h:302
std::mutex _mutex
Mutex to protect parallel access (used in ray tracing)
Definition: SLGLTexture.h:324
void rotation(const T angleDEG, const SLVec3< T > &axis)
Sets the rotation components
Definition: SLMat3.h:392
Defines a standard CG material with textures and a shader program.
Definition: SLMaterial.h:56
void translucency(SLfloat transl)
Definition: SLMaterial.h:176
static SLfloat PERFECT
PM: shininess/translucency limit.
Definition: SLMaterial.h:238
void specular(const SLCol4f &spec)
Definition: SLMaterial.h:173
void diffuse(const SLCol4f &diff)
Definition: SLMaterial.h:171
SLuint numTextures()
Definition: SLMaterial.h:226
void shininess(SLfloat shin)
Definition: SLMaterial.h:177
void transmissive(const SLCol4f &transm)
Definition: SLMaterial.h:175
void emissive(const SLCol4f &emis)
Definition: SLMaterial.h:174
void kn(SLfloat kn)
Definition: SLMaterial.h:199
SLMaterial * mat() const
Definition: SLMesh.h:177
virtual void preShade(SLRay *ray)
Definition: SLMesh.cpp:1470
SLNode represents a node in a hierarchical scene graph.
Definition: SLNode.h:147
virtual bool hitRec(SLRay *ray)
Definition: SLNode.cpp:508
const SLstring & name() const
Definition: SLObject.h:38
SLbool _calcIndirect
flag to calculate indirect illumination
Definition: SLPathtracer.h:44
SLCol4f shade(SLRay *ray, SLCol4f *mat)
SLbool render(SLSceneView *sv)
SLbool _calcDirect
flag to calculate direct illumination
Definition: SLPathtracer.h:43
void renderSlices(bool isMainThread, SLint currentSample, SLuint threadNum)
void saveImage()
Saves the current PT image as PNG image.
function< void(bool, int, SLuint)> renderSlicesPTAsync
Definition: SLPathtracer.h:41
SLCol4f trace(SLRay *ray, SLbool em)
Ray class with ray and intersection properties.
Definition: SLRay.h:40
SLbool hitMatIsTransparent() const
Returns true if the hit material transmission color is not black.
Definition: SLRay.h:205
bool reflectMC(SLRay *reflected, const SLMat3f &rotMat) const
Definition: SLRay.cpp:332
SLCol4f backgroundColor
Background color at pixel x,y.
Definition: SLRay.h:100
SLMesh * hitMesh
Points to the intersected mesh.
Definition: SLRay.h:106
SLbool isOutside
Flag if ray is inside of a material.
Definition: SLRay.h:95
SLint depth
Recursion depth for ray tracing.
Definition: SLRay.h:78
SLVec3f dir
Direction vector of ray in WS.
Definition: SLRay.h:76
void refract(SLRay *refracted)
Definition: SLRay.cpp:197
SLbool hitMatIsReflective() const
Returns true if the hit material specular color is not black.
Definition: SLRay.h:194
SLfloat length
length from origin to an intersection
Definition: SLRay.h:77
SLVec3f hitPoint
Point of intersection.
Definition: SLRay.h:110
void diffuseMC(SLRay *scattered) const
Definition: SLRay.cpp:429
static SLuint totalNumRays()
Total NO. of rays shot during RT.
Definition: SLRay.h:84
void reflect(SLRay *reflected) const
Definition: SLRay.cpp:156
SLCol4f hitTexColor
Color at intersection for texture or color attributes.
Definition: SLRay.h:112
void refractMC(SLRay *refracted, const SLMat3f &rotMat) const
Definition: SLRay.cpp:384
SLVec3f hitNormal
Surface normal at intersection point.
Definition: SLRay.h:111
SLbool hitMatIsDiffuse() const
Returns true if the hit material diffuse color is not black.
Definition: SLRay.h:216
SLint maxDepth() const
Definition: SLRaytracer.h:114
SLfloat _oneOverGamma
one over gamma correction value
Definition: SLRaytracer.h:157
virtual void prepareImage()
SLSceneView * _sv
Parent sceneview.
Definition: SLRaytracer.h:138
SLfloat gamma() const
Definition: SLRaytracer.h:123
SLint _nextLine
next line index to render RT in a thread
Definition: SLRaytracer.h:154
SLRTState _state
RT state;.
Definition: SLRaytracer.h:139
SLfloat _renderSec
Rendering time in seconds.
Definition: SLRaytracer.h:147
SLint _aaSamples
SQRT of uneven num. of AA samples.
Definition: SLRaytracer.h:161
void renderUIBeforeUpdate()
Must be called before an inbetween frame updateRec.
void setPrimaryRay(SLfloat x, SLfloat y, SLRay *primaryRay)
Set the parameters of a primary ray for a pixel position at x, y.
virtual void initStats(SLint depth)
SLint _progressPC
progress in %
Definition: SLRaytracer.h:146
AvgFloat _raysPerMS
Averaged rays per ms.
Definition: SLRaytracer.h:148
SLVLight & lights()
Definition: SLScene.h:107
void root3D(SLNode *root3D)
Definition: SLScene.h:78
SceneView class represents a dynamic real time 3D view onto the scene.
Definition: SLSceneView.h:69
SLint viewportH() const
Definition: SLSceneView.h:180
cbOnWndUpdate onWndUpdate
C-Callback for app for intermediate window repaint.
Definition: SLSceneView.h:141
SLint viewportW() const
Definition: SLSceneView.h:179
SLScene * s()
Definition: SLSceneView.h:167
SLVec3 & normalize()
Definition: SLVec3.h:124
T length() const
Definition: SLVec3.h:122
void set(const T X, const T Y, const T Z)
Definition: SLVec3.h:59
T dot(const SLVec3 &v) const
Definition: SLVec3.h:117
void sub(const SLVec3 &a, const SLVec3 &b)
Definition: SLVec3.h:113
T z
Definition: SLVec3.h:43
static SLVec4 BLACK
Definition: SLVec4.h:213
T g
Definition: SLVec4.h:33
void set(const T X, const T Y, const T Z, const T W=1)
Definition: SLVec4.h:49
T b
Definition: SLVec4.h:33
T maxXYZ()
Definition: SLVec4.h:141
void gammaCorrect(T oneOverGamma)
Gamma correction.
Definition: SLVec4.h:163
T a
Definition: SLVec4.h:33
T r
Definition: SLVec4.h:33
void clampMinMax(const T min, const T max)
Definition: SLVec4.h:119
void set(T value)
Sets the current value in the value array and builds the average.
Definition: Averaged.h:53
static const float ONEOVERPI
Definition: Utils.h:241
unsigned int maxThreads()
Returns in release config the max. NO. of threads otherwise 1.
Definition: Utils.cpp:1191