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