SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLRaytracer.cpp
Go to the documentation of this file.
1 /**
2  * \file SLRaytracer.cpp
3  * \date July 2014
4  * \authors Marcus Hudritsch
5  * \copyright http://opensource.org/licenses/GPL-3.0
6  * \remarks Please use clangformat to format the code. See more code style on
7  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
8 */
9 
10 #include <functional>
11 using namespace std::placeholders;
12 
13 #include <SLLightRect.h>
14 #include <SLRay.h>
15 #include <SLRaytracer.h>
16 #include <SLSceneView.h>
17 #include <SLSkybox.h>
18 #include <GlobalTimer.h>
19 #include <Profiler.h>
20 
21 //-----------------------------------------------------------------------------
23 {
24  name("myCoolRaytracer");
25 
26  _sv = nullptr;
27  _state = rtReady;
28  _doDistributed = true;
29  _doContinuous = false;
30  _doFresnel = true;
31  _maxDepth = 5;
32  _aaThreshold = 0.3f; // = 10% color difference
33  _aaSamples = 3;
34  _resolutionFactor = 0.5f;
35  gamma(1.0f);
36  _raysPerMS.init(60, 0.0f);
37 
38  // set texture properties
39  _min_filter = GL_NEAREST;
40  _mag_filter = GL_NEAREST;
41  _wrap_s = GL_CLAMP_TO_EDGE;
42  _wrap_t = GL_CLAMP_TO_EDGE;
43  _resizeToPow2 = false;
44 }
45 //-----------------------------------------------------------------------------
47 {
48  SL_LOG("Destructor : ~SLRaytracer");
49 }
50 //-----------------------------------------------------------------------------
51 /*!
52 This is the main rendering method for the classic ray tracing. It loops over all
53 lines and pixels and determines for each pixel a color with a partly global
54 illumination calculation.
55 */
57 {
59 
60  _sv = sv;
61  _state = rtBusy; // From here we state the RT as busy
62  _progressPC = 0; // % rendered
63  _renderSec = 0.0f; // reset time
64 
65  initStats(_maxDepth); // init statistics
66  prepareImage(); // Setup image & precalculations
67 
68  // Measure time
69  float t1 = GlobalTimer::timeS();
70  float tStart = t1;
71 
72  for (SLuint y = 0; y < _images[0]->height(); ++y)
73  {
74  for (SLuint x = 0; x < _images[0]->width(); ++x)
75  {
76  SLRay primaryRay(_sv);
77  setPrimaryRay((SLfloat)x, (SLfloat)y, &primaryRay);
78 
79  ///////////////////////////////////
80  SLCol4f color = trace(&primaryRay);
81  ///////////////////////////////////
82 
83  color.gammaCorrect(_oneOverGamma);
84 
85  _images[0]->setPixeliRGB((SLint)x,
86  (SLint)y,
87  CVVec4f(color.r,
88  color.g,
89  color.b,
90  color.a));
91 
95  }
96 
97  // Update image after 500 ms
98  double t2 = GlobalTimer::timeS();
99  if (t2 - t1 > 0.5)
100  {
101  _progressPC = (SLint)((SLfloat)y / (SLfloat)_images[0]->height() * 100);
102  renderUIBeforeUpdate();
103  _sv->onWndUpdate();
104  t1 = GlobalTimer::timeS();
105  }
106  }
107 
108  _renderSec = GlobalTimer::timeS() - tStart;
109  _raysPerMS.set((SLfloat)SLRay::totalNumRays() / _renderSec / 1000.0f);
110  _progressPC = 100;
111 
112  if (_doContinuous)
113  _state = rtReady;
114  else
115  {
116  _state = rtFinished;
117  printStats(_renderSec);
118  }
119  return true;
120 }
121 //-----------------------------------------------------------------------------
122 /*!
123 This is the main rendering method for parallel and distributed ray tracing.
124 */
126 {
128 
129  _sv = sv;
130  _state = rtBusy; // From here we state the RT as busy
131  _progressPC = 0; // % rendered
132  _renderSec = 0.0f; // reset time
133 
134  initStats(_maxDepth); // init statistics
135  prepareImage(); // Setup image & precalculations
136 
137  // Measure time
138  float t1 = GlobalTimer::timeS();
139 
140  // Lambda function for async AA pixel sampling
141  sampleAAPixelsAsync = [this](bool isMainThread, SLuint threadNum)
142  {
143  sampleAAPixels(isMainThread, threadNum);
144  };
145 
146  // Lambda function for async slice rendering
147  if (_cam->lensSamples()->samples() == 1)
148  {
149  renderSlicesAsync = [this](bool isMainThread, SLuint threadNum)
150  {
151  renderSlices(isMainThread, threadNum);
152  };
153  }
154  else
155  {
156  renderSlicesAsync = [this](bool isMainThread, SLuint threadNum)
157  {
158  renderSlicesMS(isMainThread, threadNum);
159  };
160  }
161 
162  // Do multi-threading only in release config
163  // Render image without anti-aliasing
164  vector<thread> threads1; // vector for additional threads
165  _nextLine = 0; // reset _nextLine=0 be for multithreading starts
166 
167  // Start additional threads on the renderSlices function
168  for (SLuint t = 1; t <= Utils::maxThreads() - 1; t++)
169  threads1.emplace_back(renderSlicesAsync, false, t);
170 
171  // Do the same work in the main thread
172  renderSlicesAsync(true, 0);
173 
174  // Wait for the other threads to finish
175  for (auto& thread : threads1)
176  thread.join();
177 
178  // Do anti-aliasing w. contrast compare in a 2nd. pass
179  if (_aaSamples > 1 && _cam->lensSamples()->samples() == 1)
180  {
181  PROFILE_SCOPE("AntiAliasing");
182 
183  getAAPixels(); // Fills in the AA pixels by contrast
184  vector<thread> threads2; // vector for additional threads
185  _nextLine = 0; // reset _nextLine=0 be for multithreading starts
186 
187  // Start additional threads on the sampleAAPixelFunction function
188  for (SLuint t = 1; t <= Utils::maxThreads() - 1; t++)
189  threads2.emplace_back(sampleAAPixelsAsync, false, t);
190 
191  // Do the same work in the main thread
192  sampleAAPixelsAsync(true, 0);
193 
194  // Wait for the other threads to finish
195  for (auto& thread : threads2)
196  thread.join();
197  }
198 
199  _renderSec = GlobalTimer::timeS() - t1;
200  _raysPerMS.set((float)SLRay::totalNumRays() / _renderSec / 1000.0f);
201  _progressPC = 100;
202 
203  if (_doContinuous)
204  _state = rtReady;
205  else
206  {
207  _state = rtFinished;
208  printStats(_renderSec);
209  }
210  return true;
211 }
212 //-----------------------------------------------------------------------------
213 /*!
214 Renders slices of 4 rows until the full width of the image is rendered. This
215 method can be called as a function by multiple threads.
216 The _nextLine index is used and incremented by every thread. So it should be
217 locked or an atomic index. I prefer not protecting it because it's faster.
218 If the increment is not done properly some pixels may get ray traced twice.
219 Only the main thread is allowed to call a repaint of the image.
220 */
221 void SLRaytracer::renderSlices(const bool isMainThread, SLuint threadNum)
222 {
223  if (!isMainThread)
224  {
225  PROFILE_THREAD(string("RT-Worker-") + std::to_string(threadNum));
226  }
227 
229 
230  // Time points
231  double t1 = 0;
232 
233  while (_nextLine < (SLint)_images[0]->height())
234  {
235  // The next section must be protected
236  // Making _nextLine an atomic was not sufficient.
237  _mutex.lock();
238  SLint minY = _nextLine;
239  _nextLine += 4;
240  _mutex.unlock();
241 
242  for (SLint y = minY; y < minY + 4; ++y)
243  {
244  for (SLuint x = 0; x < _images[0]->width(); ++x)
245  {
246  SLRay primaryRay(_sv);
247  setPrimaryRay((SLfloat)x, (SLfloat)y, &primaryRay);
248 
249  ///////////////////////////////////
250  SLCol4f color = trace(&primaryRay);
251  ///////////////////////////////////
252 
253  color.gammaCorrect(_oneOverGamma);
254 
255  //_mutex.lock();
256  _images[0]->setPixeliRGB((SLint)x,
257  (SLint)y,
258  CVVec4f(color.r,
259  color.g,
260  color.b,
261  color.a));
262  //_mutex.unlock();
263 
267  }
268 
269  // Update image after 500 ms
270  if (_sv->onWndUpdate && isMainThread && !_doContinuous)
271  {
272  if (GlobalTimer::timeS() - t1 > 0.5)
273  {
274  _progressPC = (SLint)((SLfloat)y /
275  (SLfloat)_images[0]->height() * 100);
276  if (_aaSamples > 0) _progressPC /= 2;
277  renderUIBeforeUpdate();
278  _sv->onWndUpdate();
279  t1 = GlobalTimer::timeS();
280  }
281  }
282  }
283  }
284 }
285 //-----------------------------------------------------------------------------
286 /*!
287 Renders slices of 4 rows multi-sampled until the full width of the image is
288 rendered. Every pixel is multi-sampled for depth of field lens sampling. This
289 method can be called as a function by multiple threads.
290 The _nextLine index is used and incremented by every thread. So it should be
291 locked or an atomic index. I prefer not protecting it because it's faster.
292 If the increment is not done properly some pixels may get ray traced twice.
293 Only the main thread is allowed to call a repaint of the image.
294 */
295 void SLRaytracer::renderSlicesMS(const bool isMainThread, SLuint threadNum)
296 {
297  if (!isMainThread)
298  {
299  PROFILE_THREAD(string("RT-Worker-") + std::to_string(threadNum));
300  }
301 
303 
304  // Time points
305  double t1 = 0;
306 
307  // lens sampling constants
308  SLVec3f lensRadiusX = _lr * (_cam->lensDiameter() * 0.5f);
309  SLVec3f lensRadiusY = _lu * (_cam->lensDiameter() * 0.5f);
310 
311  while (_nextLine < (SLint)_images[0]->width())
312  {
313  // The next section must be protected
314  // Making _nextLine an atomic was not sufficient.
315  _mutex.lock();
316  SLint minY = _nextLine;
317  _nextLine += 4;
318  _mutex.unlock();
319 
320  for (SLint y = minY; y < minY + 4; ++y)
321  {
322  for (SLuint x = 0; x < _images[0]->width(); ++x)
323  {
324  // focal point is single shot primary dir
325  SLVec3f primaryDir(_bl + _pxSize * ((SLfloat)x * _lr + (SLfloat)y * _lu));
326  SLVec3f FP = _eye + primaryDir;
327  SLCol4f color(SLCol4f::BLACK);
328 
329  // Loop over radius r and angle phi of lens
330  for (SLint iR = (SLint)_cam->lensSamples()->samplesX() - 1; iR >= 0; --iR)
331  {
332  for (SLint iPhi = (SLint)_cam->lensSamples()->samplesY() - 1; iPhi >= 0; --iPhi)
333  {
334  SLVec2f discPos(_cam->lensSamples()->point((SLuint)iR, (SLuint)iPhi));
335 
336  // calculate lens position out of disc position
337  SLVec3f lensPos(_eye + discPos.x * lensRadiusX + discPos.y * lensRadiusY);
338  SLVec3f lensToFP(FP - lensPos);
339  lensToFP.normalize();
340 
341  SLCol4f backColor;
342  if (_sv->s()->skybox())
343  backColor = _sv->s()->skybox()->colorAtDir(lensToFP);
344  else
345  backColor = _sv->camera()->background().colorAtPos((SLfloat)x,
346  (SLfloat)y,
347  (SLfloat)_images[0]->width(),
348  (SLfloat)_images[0]->height());
349 
350  SLRay primaryRay(lensPos, lensToFP, (SLfloat)x, (SLfloat)y, backColor, _sv);
351 
352  ////////////////////////////
353  color += trace(&primaryRay);
354  ////////////////////////////
355 
359  }
360  }
361  color /= (SLfloat)_cam->lensSamples()->samples();
362 
363  color.gammaCorrect(_oneOverGamma);
364 
365  //_mutex.lock();
366  _images[0]->setPixeliRGB((SLint)x, y, CVVec4f(color.r, color.g, color.b, color.a));
367  //_mutex.unlock();
368 
371  }
372 
373  if (_sv->onWndUpdate && isMainThread && !_doContinuous)
374  {
375  if (GlobalTimer::timeS() - t1 > 0.5)
376  {
377  renderUIBeforeUpdate();
378  _sv->onWndUpdate();
379  t1 = GlobalTimer::timeS();
380  }
381  }
382  }
383  }
384 }
385 //-----------------------------------------------------------------------------
386 /*!
387 This method is the classic recursive ray tracing method that checks the scene
388 for intersection. If the ray hits an object the local color is calculated and
389 if the material is reflective and/or transparent new rays are created and
390 passed to this trace method again. If no object got intersected the
391 background color is return.
392 */
394 {
395  SLCol4f color(ray->backgroundColor);
396 
397  // Intersect scene
398  SLNode* root = _sv->s()->root3D();
399  if (root) root->hitRec(ray);
400 
401  if (ray->length < FLT_MAX && ray->hitMesh && ray->hitMesh->primitive() == PT_triangles)
402  {
403  color = shade(ray);
404 
405  SLfloat kt = ray->hitMesh->mat()->kt();
406  SLfloat kr = ray->hitMesh->mat()->kr();
407 
408  if (ray->depth < SLRay::maxDepth && ray->contrib > SLRay::minContrib)
409  {
410  if (!_doFresnel)
411  {
412  // Do classic refraction and/or reflection
413  if (kt > 0.0f)
414  {
415  SLRay refracted(_sv);
416  ray->refract(&refracted);
417  color += kt * trace(&refracted);
418  }
419  if (kr > 0.0f)
420  {
421  SLRay reflected(_sv);
422  ray->reflect(&reflected);
423  color += kr * trace(&reflected);
424  }
425  }
426  else
427  {
428  // Mix refr. & refl. color w. Fresnel approximation
429  if (kt > 0.0f)
430  {
431  SLRay refracted(_sv), reflected(_sv);
432  ray->refract(&refracted);
433  ray->reflect(&reflected);
434  SLCol4f refrCol = trace(&refracted);
435  SLCol4f reflCol = trace(&reflected);
436 
437  // Apply Schlick's Fresnel approximation
438  SLfloat F0 = kr;
439  SLfloat theta = -(ray->dir * ray->hitNormal);
440  SLfloat F_theta = F0 + (1 - F0) * (SLfloat)pow(1 - theta, 5);
441  color += refrCol * (1 - F_theta) + reflCol * F_theta;
442  }
443  else
444  {
445  if (kr > 0.0f)
446  {
447  SLRay reflected(_sv);
448  ray->reflect(&reflected);
449  color += kr * trace(&reflected);
450  }
451  }
452  }
453  }
454  }
455 
456  if (_cam->fogIsOn())
457  color = fogBlend(ray->length, color);
458 
459  color.clampMinMax(0, 1);
460  return color;
461 }
462 //-----------------------------------------------------------------------------
463 //! Set the parameters of a primary ray for a pixel position at x, y.
465 {
466  primaryRay->x = x;
467  primaryRay->y = y;
468  primaryRay->sv = _sv;
469 
470  // calculate ray from eye to pixel (See also prepareImage())
471  if (_cam->projType() == P_monoOrthographic)
472  {
473  primaryRay->setDir(_la);
474  primaryRay->origin = _bl + _pxSize * ((SLfloat)x * _lr + (SLfloat)y * _lu);
475  }
476  else
477  {
478  SLVec3f primaryDir(_bl + _pxSize * ((SLfloat)x * _lr + (SLfloat)y * _lu));
479  primaryDir.normalize();
480  primaryRay->setDir(primaryDir);
481  primaryRay->origin = _eye;
482  }
483 
484  if (_sv->s()->skybox())
485  primaryRay->backgroundColor = _sv->s()->skybox()->colorAtDir(primaryRay->dir);
486  else
487  primaryRay->backgroundColor = _sv->camera()->background().colorAtPos(x,
488  y,
489  (SLfloat)_images[0]->width(),
490  (SLfloat)_images[0]->height());
492 }
493 //-----------------------------------------------------------------------------
494 /*!
495 This method calculates the local illumination at the rays intersection point.
496 It uses the Blinn-Phong local reflection model where the color is calculated as
497 follows:
498 color = material emission +
499  global ambient light scaled by the material's ambient color +
500  ambient, diffuse, and specular contributions from all lights,
501  properly attenuated
502 */
504 {
505  SLMaterial* mat = ray->hitMesh->mat();
506  SLVGLTexture& texture = mat->textures(TT_diffuse);
507  SLVec3f L, N, H;
508  SLfloat lightDist, LdotN, NdotH, df, sf, spotEffect, att, lighted;
509  SLCol4f ambi, diff, spec;
510  SLCol4f localSpec(0, 0, 0, 1);
511  SLScene* s = _sv->s();
512  SLCol4f localColor = mat->emissive() + (mat->ambient() & SLLight::globalAmbient);
513 
514  ray->hitMesh->preShade(ray);
515 
516  for (auto* light : s->lights())
517  {
518  if (light && light->isOn())
519  {
520  // calculate light vector L and distance to light
521  N.set(ray->hitNormal);
522 
523  // Distinguish between point and directional lights
524  SLVec4f lightPos = light->positionWS();
525 
526  // Check if directional light on last component w (0 = light is in infinity)
527  if (lightPos.w == 0.0f)
528  {
529  // directional light
530  L = -light->spotDirWS().normalized();
531  lightDist = FLT_MAX; // = infinity
532  }
533  else
534  {
535  // Point light
536  L.sub(lightPos.vec3(), ray->hitPoint);
537  lightDist = L.length();
538  L /= lightDist;
539  }
540 
541  // Cosine between L and N
542  LdotN = L.dot(N);
543 
544  // check shadow ray if hit point is towards the light
545  lighted = (LdotN > 0) ? light->shadowTest(ray, L, lightDist, s->root3D()) : 0;
546 
547  // calculate the ambient part
548  ambi = light->ambient() & mat->ambient() * ray->hitAO;
549 
550  // calculate spot effect if light is a spotlight
551  spec.set(0, 0, 0);
552  if (lighted > 0.0f && light->spotCutOffDEG() < 180.0f)
553  {
554  SLfloat LdS = std::max(-L.dot(light->spotDirWS()), 0.0f);
555 
556  // check if point is in spot cone
557  if (LdS > light->spotCosCut())
558  spotEffect = pow(LdS, (SLfloat)light->spotExponent());
559  else
560  {
561  lighted = 0.0f;
562  spotEffect = 0.0f;
563  }
564  }
565  else
566  spotEffect = 1.0f;
567 
568  // calculate local illumination only if point is not shaded
569  if (lighted > 0.0f)
570  {
571  H.sub(L, ray->dir); // half vector between light & eye
572  H.normalize();
573  df = std::max(LdotN, 0.0f); // diffuse factor
574  NdotH = std::max(N.dot(H), 0.0f);
575  sf = pow(NdotH, (SLfloat)mat->shininess()); // specular factor
576 
577  diff += lighted * df * light->diffuse() & mat->diffuse();
578  spec = lighted * sf * light->specular() & mat->specular();
579  }
580 
581  // apply attenuation and spot effect
582  att = light->attenuation(lightDist);
583  localColor += att * ambi;
584  localColor += att * spotEffect * diff;
585  localSpec += att * spotEffect * spec;
586  }
587  }
588 
589  if (!texture.empty() || !ray->hitMesh->C.empty())
590  {
591  localColor &= ray->hitTexColor; // component wise multiply
592  localColor += localSpec; // add afterwards the specular component
593  }
594  else
595  localColor += localSpec;
596 
597  localColor.clampMinMax(0, 1);
598  return localColor;
599 }
600 //-----------------------------------------------------------------------------
601 /*!
602 This method fills the pixels into the vector pix that need to be sub-sampled
603 because the contrast to its left and/or above neighbor is above a threshold.
604 */
606 {
607  SLCol4f color, colorLeft, colorUp; // pixel colors to be compared
608  SLVbool gotSampled;
609  gotSampled.resize(_images[0]->width()); // Flags if above pixel got sampled
610  SLbool isSubsampled; // Flag if pixel got sub-sampled
611 
612  // Nothing got sampled at beginning
613  for (SLuint x = 0; x < _images[0]->width(); ++x)
614  gotSampled[x] = false;
615 
616  // Loop through all pixels & add the pixel that have to be subsampled
617  _aaPixels.clear();
618  for (SLuint y = 0; y < _images[0]->height(); ++y)
619  {
620  for (SLuint x = 0; x < _images[0]->width(); ++x)
621  {
622  CVVec4f c4f = _images[0]->getPixeli((SLint)x, (SLint)y);
623  color.set(c4f[0], c4f[1], c4f[2], c4f[3]);
624 
625  isSubsampled = false;
626  if (x > 0)
627  {
628  CVVec4f colL = _images[0]->getPixeli((SLint)x - 1, (SLint)y);
629  colorLeft.set(colL[0], colL[1], colL[2], colL[3]);
630  if (color.diffRGB(colorLeft) > _aaThreshold)
631  {
632  if (!gotSampled[x - 1])
633  {
634  _aaPixels.push_back(SLRTAAPixel((SLushort)x - 1, (SLushort)y));
635  gotSampled[x - 1] = true;
636  }
637  _aaPixels.push_back(SLRTAAPixel((SLushort)x, (SLushort)y));
638  isSubsampled = true;
639  }
640  }
641  if (y > 0)
642  {
643  CVVec4f colU = _images[0]->getPixeli((SLint)x, (SLint)y - 1);
644  colorUp.set(colU[0], colU[1], colU[2], colU[3]);
645  if (color.diffRGB(colorUp) > _aaThreshold)
646  {
647  if (!gotSampled[x])
648  _aaPixels.push_back(SLRTAAPixel((SLushort)x, (SLushort)y - 1));
649  if (!isSubsampled)
650  {
651  _aaPixels.push_back(SLRTAAPixel((SLushort)x, (SLushort)y));
652  isSubsampled = true;
653  }
654  }
655  }
656  gotSampled[x] = isSubsampled;
657  }
658  }
659  SLRay::subsampledPixels = (SLuint)_aaPixels.size();
660 }
661 //-----------------------------------------------------------------------------
662 /*!
663 SLRaytracer::sampleAAPixels does the subsampling of the pixels that need to be
664 antialiased. See also getAAPixels. This routine can be called by multiple
665 threads.
666 The _nextLine index is used and incremented by every thread. So it should be
667 locked or an atomic index. I prefer not protecting it because it's faster.
668 If the increment is not done properly some pixels may get ray traced twice.
669 Only the main thread is allowed to call a repaint of the image.
670 */
671 void SLRaytracer::sampleAAPixels(const bool isMainThread, SLuint threadNum)
672 {
673  if (!isMainThread)
674  {
675  PROFILE_THREAD(string("RT-Worker-") + std::to_string(threadNum));
676  }
677 
679 
680  assert(_aaSamples % 2 == 1 && "subSample: maskSize must be uneven");
681  double t1 = 0, t2;
682 
683  while (_nextLine < (SLint)_aaPixels.size())
684  {
685  // The next section must be protected
686  // Making _nextLine an atomic was not sufficient.
687  _mutex.lock();
688  SLuint mini = (SLuint)_nextLine;
689  _nextLine += 4;
690  _mutex.unlock();
691 
692  for (SLuint i = mini; i < mini + 4 && i < _aaPixels.size(); ++i)
693  {
694  SLuint x = _aaPixels[i].x;
695  SLuint y = _aaPixels[i].y;
696  CVVec4f c4f = _images[0]->getPixeli((SLint)x, (SLint)y);
697  SLCol4f centerColor(c4f[0], c4f[1], c4f[2], c4f[3]);
698  SLint centerIndex = _aaSamples >> 1;
699  SLfloat f = 1.0f / (SLfloat)_aaSamples;
700  SLfloat xpos = (SLfloat)x - (SLfloat)centerIndex * f;
701  SLfloat ypos = (SLfloat)y - (SLfloat)centerIndex * f;
702  SLfloat samples = (SLfloat)_aaSamples * (SLfloat)_aaSamples;
703  SLCol4f color(0, 0, 0);
704 
705  // Loop regularly over the float pixel
706 
707  for (SLint sy = 0; sy < _aaSamples; ++sy)
708  {
709  for (SLint sx = 0; sx < _aaSamples; ++sx)
710  {
711  if (sx == centerIndex && sy == centerIndex)
712  color += centerColor; // don't shoot for center position
713  else
714  {
715  SLRay primaryRay(_sv);
716  setPrimaryRay(xpos + (SLfloat)sx * f,
717  ypos + (SLfloat)sy * f,
718  &primaryRay);
719  color += trace(&primaryRay);
720  }
721  }
722  ypos += f;
723  }
724  SLRay::subsampledRays += (SLuint)samples;
725  color /= samples;
726 
727  color.gammaCorrect(_oneOverGamma);
728 
729  //_mutex.lock();
730  _images[0]->setPixeliRGB((SLint)x,
731  (SLint)y,
732  CVVec4f(color.r,
733  color.g,
734  color.b,
735  color.a));
736  //_mutex.unlock();
737  }
738 
739  if (_sv->onWndUpdate && isMainThread && !_doContinuous)
740  {
741  t2 = GlobalTimer::timeS();
742  if (t2 - t1 > 0.5)
743  {
744  _progressPC = 50 + (SLint)((SLfloat)_nextLine / (SLfloat)_aaPixels.size() * 50);
745  renderUIBeforeUpdate();
746  _sv->onWndUpdate();
747  t1 = GlobalTimer::timeS();
748  }
749  }
750  }
751 }
752 //-----------------------------------------------------------------------------
753 /*!
754 fogBlend: Blends the a fog color to the passed color according to to OpenGL fog
755 calculation. See OpenGL docs for more information on fog properties.
756 */
758 {
759  SLfloat f;
760 
761  if (z > _sv->_camera->clipFar())
762  z = _sv->_camera->clipFar();
763 
764  switch (_cam->fogMode())
765  {
766  case 0:
767  f = (_cam->fogDistEnd() - z) /
768  (_cam->fogDistEnd() - _cam->fogDistStart());
769  break;
770  case 1:
771  f = exp(-_cam->fogDensity() * z);
772  break;
773  default:
774  f = exp(-_cam->fogDensity() * z * _cam->fogDensity() * z);
775  break;
776  }
777  color = f * color + (1 - f) * _cam->fogColor();
778  color.clampMinMax(0, 1);
779  return color;
780 }
781 //-----------------------------------------------------------------------------
782 /*!
783 Initialises the statistic variables in SLRay to zero
784 */
786 {
787  SLRay::maxDepth = (depth) ? depth : SL_MAXTRACE;
788  SLRay::primaryRays = 0;
791  SLRay::tirRays = 0;
792  SLRay::shadowRays = 0;
795  SLRay::tests = 0;
798  SLRay::avgDepth = 0.0f;
799 }
800 //-----------------------------------------------------------------------------
801 /*!
802 Prints some statistics after the rendering
803 */
805 {
806  SL_LOG("\nRender time : %10.2f sec.", sec);
807  SL_LOG("Image size : %10d x %d", _images[0]->width(), _images[0]->height());
808  SL_LOG("Num. Threads : %10d", Utils::maxThreads());
809  SL_LOG("Allowed depth : %10d", SLRay::maxDepth);
810 
811  SLuint primarys = (SLuint)(_sv->viewportRect().width * _sv->viewportRect().height);
812  SLuint total = primarys +
817 
818  SL_LOG("Maximum depth : %10d", SLRay::maxDepthReached);
819  SL_LOG("Average depth : %10.6f", SLRay::avgDepth / primarys);
820  SL_LOG("AA threshold : %10.1f", _aaThreshold);
821  SL_LOG("AA subsampling : %8dx%d\n", _aaSamples, _aaSamples);
822  SL_LOG("Subsampled pixels : %10u, %4.1f%% of total", SLRay::subsampledPixels, (SLfloat)SLRay::subsampledPixels / primarys * 100.0f);
823  SL_LOG("Primary rays : %10u, %4.1f%% of total", primarys, (SLfloat)primarys / total * 100.0f);
824  SL_LOG("Reflected rays : %10u, %4.1f%% of total", SLRay::reflectedRays, (SLfloat)SLRay::reflectedRays / total * 100.0f);
825  SL_LOG("Refracted rays : %10u, %4.1f%% of total", SLRay::refractedRays, (SLfloat)SLRay::refractedRays / total * 100.0f);
826  SL_LOG("Ignored rays : %10u, %4.1f%% of total", SLRay::ignoredRays, (SLfloat)SLRay::ignoredRays / total * 100.0f);
827  SL_LOG("TIR rays : %10u, %4.1f%% of total", SLRay::tirRays, (SLfloat)SLRay::tirRays / total * 100.0f);
828  SL_LOG("Shadow rays : %10u, %4.1f%% of total", SLRay::shadowRays, (SLfloat)SLRay::shadowRays / total * 100.0f);
829  SL_LOG("AA subsampled rays: %10u, %4.1f%% of total", SLRay::subsampledRays, (SLfloat)SLRay::subsampledRays / total * 100.0f);
830  SL_LOG("Total rays : %10u,100.0%%\n", total);
831 
832  SL_LOG("Rays per second : %10u", (SLuint)(total / sec));
833  SL_LOG("Intersection tests: %10u", SLRay::tests);
834  SL_LOG("Intersections : %10u, %4.1f%%\n", SLRay::intersections, SLRay::intersections / (SLfloat)SLRay::tests * 100.0f);
835 }
836 //-----------------------------------------------------------------------------
837 /*!
838 Creates the inherited image in the texture class. The RT is drawn into
839 a texture map that is displayed with OpenGL in 2D-orthographic projection.
840 Also precalculate as much as possible.
841 */
843 {
844  ///////////////////////
845  // PRECALCULATIONS //
846  ///////////////////////
847 
848  _cam = _sv->_camera; // camera shortcut
849 
850  // get camera vectors eye, lookAt, lookUp
851  _cam->updateAndGetVM().lookAt(&_eye, &_la, &_lu, &_lr);
852 
853  if (_cam->projType() == P_monoOrthographic)
854  {
855  /*
856  In orthographic projection the bottom-left vector (_bl) points
857  from the eye to the center of the bottom-left pixel of a plane that
858  parallel to the projection plan at zero distance from the eye.
859  */
860  SLVec3f pos(_cam->updateAndGetVM().translation());
861  SLfloat hh = tan(Utils::DEG2RAD * _cam->fovV() * 0.5f) * pos.length();
862  SLfloat hw = hh * _sv->viewportWdivH();
863 
864  // calculate the size of a pixel in world coords.
865  _pxSize = hw * 2 / ((SLint)((SLfloat)_sv->viewportW() * _resolutionFactor));
866 
867  _bl = _eye - hw * _lr - hh * _lu + _pxSize / 2 * _lr - _pxSize / 2 * _lu;
868  }
869  else
870  {
871  /*
872  In perspective projection the bottom-left vector (_bl) points
873  from the eye to the center of the bottom-left pixel on a projection
874  plan in focal distance. See also the computer graphics script about
875  primary ray calculation.
876  */
877  // calculate half window width & height in world coords
878  SLfloat hh = tan(Utils::DEG2RAD * _cam->fovV() * 0.5f) * _cam->focalDist();
879  SLfloat hw = hh * _sv->viewportWdivH();
880 
881  // calculate the size of a pixel in world coords.
882  _pxSize = hw * 2 / ((SLint)((SLfloat)_sv->viewportW() * _resolutionFactor));
883 
884  // calculate a vector to the center (C) of the bottom left (BL) pixel
885  SLVec3f C = _la * _cam->focalDist();
886  _bl = C - hw * _lr - hh * _lu + _pxSize / 2 * _lr + _pxSize / 2 * _lu;
887  }
888 
889  // Create the image for the first time
890  if (_images.empty())
891  _images.push_back(new CVImage((SLint)((SLfloat)_sv->viewportW() * _resolutionFactor),
892  (SLint)((SLfloat)_sv->viewportH() * _resolutionFactor),
893  PF_rgb,
894  "Raytracer"));
895 
896  // Allocate image of the inherited texture class
897  if ((SLint)((SLfloat)_sv->viewportW() * _resolutionFactor) != (SLint)_images[0]->width() ||
898  (SLint)((SLfloat)_sv->viewportH() * _resolutionFactor) != (SLint)_images[0]->height())
899  {
900  // Delete the OpenGL Texture if it already exists
901  if (_texID)
902  {
903  glDeleteTextures(1, &_texID);
904  _texID = 0;
905  }
906 
907  _vaoSprite.clearAttribs();
908  _images[0]->allocate((SLint)((SLfloat)_sv->viewportW() * _resolutionFactor),
909  (SLint)((SLfloat)_sv->viewportH() * _resolutionFactor),
910  PF_rgb);
911 
912  _width = (SLint)_images[0]->width();
913  _height = (SLint)_images[0]->height();
914  _depth = (SLint)_images.size();
915  }
916 
917  // Fill image black for single RT
918  if (!_doContinuous) _images[0]->fill(0, 0, 0);
919 }
920 //-----------------------------------------------------------------------------
921 /*!
922 Draw the RT-Image as a textured quad in 2D-Orthographic projection
923 */
924 void SLRaytracer::renderImage(bool updateTextureGL)
925 {
927 
928  SLRecti vpRect = _sv->viewportRect();
929  SLfloat w = (SLfloat)vpRect.width;
930  SLfloat h = (SLfloat)vpRect.height;
931 
932  // Set orthographic projection with the size of the window
933  SLGLState* stateGL = SLGLState::instance();
934  stateGL->viewport(vpRect.x, vpRect.y, (SLsizei)w, (SLsizei)h);
935  stateGL->projectionMatrix.ortho(0.0f, w, 0.0f, h, -1.0f, 0.0f);
936  stateGL->viewMatrix.identity();
937  stateGL->modelMatrix.identity();
938  stateGL->clearColorBuffer();
939  stateGL->depthTest(false);
940  stateGL->multiSample(false);
941  stateGL->polygonLine(false);
942 
943  drawSprite(updateTextureGL, 0.0f, 0.0f, w, h);
944 
945  stateGL->depthTest(true);
946  GET_GL_ERROR;
947 }
948 //-----------------------------------------------------------------------------
949 //! Saves the current RT image as PNG image
951 {
952  static SLint no = 0;
953  SLchar filename[255];
954  snprintf(filename,
955  sizeof(filename),
956  "Raytraced_%d_%d.png",
957  _maxDepth,
958  no++);
959  _images[0]->savePNG(filename, 9, true, true);
960 }
961 //-----------------------------------------------------------------------------
962 //! Must be called before an inbetween frame updateRec
963 /* Ray and path tracing usually take much more time to render one frame.
964 We therefore call every half second _sv->onWndUpdate() that initiates another
965 paint message from the top-level UI system of the OS. We therefore have to
966 finish our UI and end OpenGL rendering properly.
967 */
969 {
970  _sv->gui()->onPaint(_sv->viewportRect());
972 }
973 //-----------------------------------------------------------------------------
@ PF_rgb
Definition: CVImage.h:36
cv::Vec4f CVVec4f
Definition: CVTypedefs.h:54
#define PROFILE_SCOPE(name)
Definition: Instrumentor.h:40
#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
unsigned short SLushort
Definition: SL.h:169
vector< SLbool > SLVbool
Definition: SL.h:189
int SLsizei
Definition: SL.h:172
int SLint
Definition: SL.h:170
@ P_monoOrthographic
standard mono orthographic projection
Definition: SLEnums.h:137
@ PT_triangles
Definition: SLGLEnums.h:35
SLSceneView * sv
Definition: SLGLImGui.h:28
#define GET_GL_ERROR
Definition: SLGLState.h:56
@ TT_diffuse
Definition: SLGLTexture.h:78
vector< SLGLTexture * > SLVGLTexture
STL vector of SLGLTexture pointers.
Definition: SLGLTexture.h:342
#define SL_MAXTRACE
Ray tracing constant for max. allowed recursion depth.
Definition: SLRay.h:30
@ rtBusy
Definition: SLRaytracer.h:30
@ rtFinished
Definition: SLRaytracer.h:31
@ rtReady
Definition: SLRaytracer.h:29
OpenCV image class with the same interface as the former SLImage class.
Definition: CVImage.h:64
static float timeS()
Definition: GlobalTimer.cpp:20
Singleton class holding all OpenGL states.
Definition: SLGLState.h:71
SLMat4f modelMatrix
Init all states.
Definition: SLGLState.h:89
void viewport(SLint x, SLint y, SLsizei width, SLsizei height)
Definition: SLGLState.cpp:378
void multiSample(SLbool state)
Definition: SLGLState.cpp:267
static SLGLState * instance()
Public static instance getter for singleton pattern.
Definition: SLGLState.h:74
SLMat4f viewMatrix
matrix for the active cameras view transform
Definition: SLGLState.h:91
void unbindAnythingAndFlush()
finishes all GL commands
Definition: SLGLState.cpp:465
SLMat4f projectionMatrix
matrix for projection transform
Definition: SLGLState.h:90
void polygonLine(SLbool state)
Definition: SLGLState.cpp:290
void clearColorBuffer()
Definition: SLGLState.h:121
void depthTest(SLbool state)
Definition: SLGLState.cpp:172
static SLCol4f globalAmbient
static global ambient light intensity
Definition: SLLight.h:202
void ortho(T l, T r, T b, T t, T n, T f)
Defines a orthographic projection matrix with a field of view angle.
Definition: SLMat4.h:911
void identity()
Sets the identity matrix.
Definition: SLMat4.h:1333
Defines a standard CG material with textures and a shader program.
Definition: SLMaterial.h:56
void specular(const SLCol4f &spec)
Definition: SLMaterial.h:173
void diffuse(const SLCol4f &diff)
Definition: SLMaterial.h:171
void kt(SLfloat kt)
Definition: SLMaterial.h:190
void shininess(SLfloat shin)
Definition: SLMaterial.h:177
void ambient(const SLCol4f &ambi)
Definition: SLMaterial.h:170
SLVGLTexture & textures(SLTextureType type)
Definition: SLMaterial.h:233
void kr(SLfloat kr)
Definition: SLMaterial.h:184
void emissive(const SLCol4f &emis)
Definition: SLMaterial.h:174
SLGLPrimitiveType primitive() const
Definition: SLMesh.h:179
SLVCol4f C
Vector of vertex colors (opt.) layout (location = 4)
Definition: SLMesh.h:206
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
Ray class with ray and intersection properties.
Definition: SLRay.h:40
SLVec3f origin
Vector to the origin of ray in WS.
Definition: SLRay.h:75
static SLint depthReached
depth reached for a primary ray
Definition: SLRay.h:134
static SLuint intersections
NO. of intersection.
Definition: SLRay.h:133
static SLuint primaryRays
NO. of primary rays shot.
Definition: SLRay.h:126
static SLuint tirRays
NO. of TIR refraction rays.
Definition: SLRay.h:131
SLCol4f backgroundColor
Background color at pixel x,y.
Definition: SLRay.h:100
static SLuint tests
NO. of intersection tests.
Definition: SLRay.h:132
SLMesh * hitMesh
Points to the intersected mesh.
Definition: SLRay.h:106
static SLuint reflectedRays
NO. of reflected rays.
Definition: SLRay.h:127
SLint depth
Recursion depth for ray tracing.
Definition: SLRay.h:78
SLfloat hitAO
Ambient occlusion factor at intersection point.
Definition: SLRay.h:113
SLVec3f dir
Direction vector of ray in WS.
Definition: SLRay.h:76
void refract(SLRay *refracted)
Definition: SLRay.cpp:197
static SLint maxDepthReached
max. depth reached for all rays
Definition: SLRay.h:135
static SLuint refractedRays
NO. of refracted rays.
Definition: SLRay.h:128
SLfloat length
length from origin to an intersection
Definition: SLRay.h:77
static SLuint ignoredRays
NO. of ignore refraction rays.
Definition: SLRay.h:129
SLfloat contrib
Current contribution of ray to color.
Definition: SLRay.h:79
static SLuint subsampledPixels
NO. of of subsampled pixels.
Definition: SLRay.h:138
SLVec3f hitPoint
Point of intersection.
Definition: SLRay.h:110
static SLuint totalNumRays()
Total NO. of rays shot during RT.
Definition: SLRay.h:84
void setDir(const SLVec3f &Dir)
Setter for the rays direction in world space also setting the inverse direction.
Definition: SLRay.h:146
static SLfloat avgDepth
average depth reached
Definition: SLRay.h:136
static SLfloat minContrib
Min. contibution to color (1/256)
Definition: SLRay.h:125
void reflect(SLRay *reflected) const
Definition: SLRay.cpp:156
static SLuint subsampledRays
NO. of of subsampled rays.
Definition: SLRay.h:137
SLCol4f hitTexColor
Color at intersection for texture or color attributes.
Definition: SLRay.h:112
SLfloat y
Pixel position for primary rays.
Definition: SLRay.h:94
SLfloat x
Definition: SLRay.h:94
static SLuint shadowRays
NO. of shadow rays.
Definition: SLRay.h:130
SLVec3f hitNormal
Surface normal at intersection point.
Definition: SLRay.h:111
SLSceneView * sv
Pointer to the sceneview.
Definition: SLRay.h:101
static SLint maxDepth
Max. recursion depth.
Definition: SLRay.h:124
virtual void prepareImage()
void getAAPixels()
SLbool renderClassic(SLSceneView *sv)
Definition: SLRaytracer.cpp:56
SLCol4f trace(SLRay *ray)
void renderSlices(bool isMainThread, SLuint threadNum)
~SLRaytracer() override
Definition: SLRaytracer.cpp:46
SLCol4f fogBlend(SLfloat z, SLCol4f color)
virtual void printStats(SLfloat sec)
void renderSlicesMS(bool isMainThread, SLuint threadNum)
SLCol4f shade(SLRay *ray)
SLbool renderDistrib(SLSceneView *sv)
virtual void renderImage(bool updateTextureGL)
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)
void sampleAAPixels(bool isMainThread, SLuint threadNum)
virtual void saveImage()
Saves the current RT image as PNG image.
T width
Definition: SLRect.h:29
T y
Definition: SLRect.h:29
T x
Definition: SLRect.h:29
T height
Definition: SLRect.h:29
The SLScene class represents the top level instance holding the scene structure.
Definition: SLScene.h:47
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
T y
Definition: SLVec2.h:30
T x
Definition: SLVec2.h:30
SLVec3 normalized() const
Definition: SLVec3.h:127
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
static SLVec4 BLACK
Definition: SLVec4.h:213
T w
Definition: SLVec4.h:32
SLVec3< T > vec3() const
Definition: SLVec4.h:111
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 diffRGB(const SLVec4 &v)
Definition: SLVec4.h:128
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
static const float DEG2RAD
Definition: Utils.h:239
unsigned int maxThreads()
Returns in release config the max. NO. of threads otherwise 1.
Definition: Utils.cpp:1191
Pixel index struct used in anti aliasing in ray tracing.
Definition: SLRaytracer.h:37