0027607: Visualization - Implement adaptive screen space sampling in path tracing
authordbp <dbp@opencascade.org>
Wed, 13 Jul 2016 09:19:27 +0000 (12:19 +0300)
committerkgv <kgv@opencascade.com>
Fri, 30 Sep 2016 08:17:10 +0000 (11:17 +0300)
This commit provides useful functionality for path tracing rendering core.

1) Graphic3d_RenderingParams class was extended with additional AdaptiveScreenSampling option (disabled by default).
   If this option is enabled, path tracing tries to adjust the number of samples for different screen areas.

   In this way, the more complex areas (from the point of light conditions) are sampled more intensively,
   while the simple areas are sampled very rarely.
   For example, caustics and glossy reflections are typical candidates for more precise sampling.

   In general, this allows to equalize image convergence and not to waste resources for already converged areas.
   It is also possible to visualize sampling densities by enabling ShowSamplingTiles option
   (activating and deactivating this option does not affect on the accumulated image).

2) Mixing OpenGL and ray-tracing output has been changed.
   Now blending is performed using OpenGL functionality, while ray-tracing shaders only output correct Z-value.

Test case bugs vis bug27083 has been updated,
since the alpha value is now correctly set by Ray-Tracing to 1, opaque.

22 files changed:
samples/tcl/pathtrace.tcl
src/Graphic3d/Graphic3d_RenderingParams.hxx
src/Graphic3d/Graphic3d_TypeOfLimit.hxx
src/OpenGl/FILES
src/OpenGl/OpenGl_Context.cxx
src/OpenGl/OpenGl_Context.hxx
src/OpenGl/OpenGl_GlCore11Fwd.hxx
src/OpenGl/OpenGl_GlFunctions.hxx
src/OpenGl/OpenGl_GraphicDriver.cxx
src/OpenGl/OpenGl_HaltonSampler.hxx [new file with mode: 0644]
src/OpenGl/OpenGl_SceneGeometry.cxx
src/OpenGl/OpenGl_TileSampler.cxx [new file with mode: 0644]
src/OpenGl/OpenGl_TileSampler.hxx [new file with mode: 0644]
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_View_Raytrace.cxx
src/OpenGl/OpenGl_View_Redraw.cxx
src/Shaders/Display.fs
src/Shaders/PathtraceBase.fs
src/Shaders/RaytraceBase.fs
src/Shaders/RaytraceRender.fs
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/bugs/vis/bug27083

index 670b4ce..d1bf449 100644 (file)
@@ -15,7 +15,7 @@ vlight del 0
 vlight del 1
 vlight add positional head 0 pos 0.5 0.5 0.85
 vlight change 0 sm 0.06
-vlight change 0 int 60.0
+vlight change 0 int 30.0
 
 vvbo 0
 vsetdispmode 1
index 8e0f231..687fe06 100644 (file)
@@ -61,6 +61,8 @@ public:
     IsTransparentShadowEnabled  (Standard_False),
     UseEnvironmentMapBackground (Standard_False),
     CoherentPathTracingMode     (Standard_False),
+    AdaptiveScreenSampling      (Standard_False),
+    ShowSamplingTiles           (Standard_False),
     // stereoscopic parameters
     StereoMode (Graphic3d_StereoMode_QuadBuffer),
     AnaglyphFilter (Anaglyph_RedCyan_Optimized),
@@ -99,6 +101,8 @@ public:
   Standard_Boolean        IsTransparentShadowEnabled;  //!< enables/disables light propagation through transparent media, False by default
   Standard_Boolean        UseEnvironmentMapBackground; //!< enables/disables environment map background
   Standard_Boolean        CoherentPathTracingMode;     //!< enables/disables 'coherent' tracing mode (single RNG seed within 16x16 image blocks)
+  Standard_Boolean        AdaptiveScreenSampling;      //!< enables/disables adaptive screen sampling mode for path tracing, FALSE by default
+  Standard_Boolean        ShowSamplingTiles;           //!< enables/disables debug mode for adaptive screen sampling, FALSE by default
 
   Graphic3d_StereoMode    StereoMode;                  //!< stereoscopic output mode, Graphic3d_StereoMode_QuadBuffer by default
   Anaglyph                AnaglyphFilter;              //!< filter for anaglyph output, Anaglyph_RedCyan_Optimized by default
index 8cc1cbd..beaefd9 100644 (file)
 //! Type of graphic resource limit.
 enum Graphic3d_TypeOfLimit
 {
-  Graphic3d_TypeOfLimit_MaxNbLights,     //!< maximum number of active light sources
-  Graphic3d_TypeOfLimit_MaxNbClipPlanes, //!< maximum number of active clipping planes
-  Graphic3d_TypeOfLimit_MaxNbViews,      //!< maximum number of views
-  Graphic3d_TypeOfLimit_MaxTextureSize,  //!< maximum size of texture
-  Graphic3d_TypeOfLimit_MaxMsaa,         //!< maximum number of MSAA samples
-  Graphic3d_TypeOfLimit_NB               //!< number of elements in this enumeration
+  Graphic3d_TypeOfLimit_MaxNbLights,                    //!< maximum number of active light sources
+  Graphic3d_TypeOfLimit_MaxNbClipPlanes,                //!< maximum number of active clipping planes
+  Graphic3d_TypeOfLimit_MaxNbViews,                     //!< maximum number of views
+  Graphic3d_TypeOfLimit_MaxTextureSize,                 //!< maximum size of texture
+  Graphic3d_TypeOfLimit_MaxMsaa,                        //!< maximum number of MSAA samples
+  Graphic3d_TypeOfLimit_HasRayTracing,                  //!< indicates whether ray tracing is supported
+  Graphic3d_TypeOfLimit_HasRayTracingTextures,          //!< indicates whether ray tracing textures are supported
+  Graphic3d_TypeOfLimit_HasRayTracingAdaptiveSampling,  //!< indicates whether adaptive screen sampling is supported
+  Graphic3d_TypeOfLimit_NB                              //!< number of elements in this enumeration
 };
 
 #endif // _Graphic3d_TypeOfLimit_HeaderFile
index 7fe3a11..326d2bc 100755 (executable)
@@ -131,6 +131,8 @@ OpenGl_Sphere.cxx
 OpenGl_Sphere.hxx
 OpenGl_StencilTest.cxx
 OpenGl_StencilTest.hxx
+OpenGl_TileSampler.hxx
+OpenGl_TileSampler.cxx
 OpenGl_TextureBufferArb.cxx
 OpenGl_TextureBufferArb.hxx
 OpenGl_Vec.hxx
@@ -141,4 +143,5 @@ OpenGl_VertexBufferCompat.cxx
 OpenGl_VertexBufferCompat.hxx
 OpenGl_VertexBufferEditor.hxx
 OpenGl_TextBuilder.hxx
-OpenGl_TextBuilder.cxx
\ No newline at end of file
+OpenGl_TextBuilder.cxx
+OpenGl_HaltonSampler.hxx
index cf724a8..fe8be66 100644 (file)
@@ -157,6 +157,9 @@ OpenGl_Context::OpenGl_Context (const Handle(OpenGl_Caps)& theCaps)
   myIsInitialized (Standard_False),
   myIsStereoBuffers (Standard_False),
   myIsGlNormalizeEnabled (Standard_False),
+  myHasRayTracing (Standard_False),
+  myHasRayTracingTextures (Standard_False),
+  myHasRayTracingAdaptiveSampling (Standard_False),
 #if !defined(GL_ES_VERSION_2_0)
   myPointSpriteOrig (GL_UPPER_LEFT),
   myRenderMode (GL_RENDER),
@@ -2302,6 +2305,20 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile)
   arbTBO = (OpenGl_ArbTBO* )(&(*myFuncs));
   arbIns = (OpenGl_ArbIns* )(&(*myFuncs));
 
+  // check whether ray tracing mode is supported
+  myHasRayTracing = has31
+                 && arbTboRGB32
+                 && arbFBOBlit  != NULL;
+
+  // check whether textures in ray tracing mode are supported
+  myHasRayTracingTextures = myHasRayTracing
+                         && arbTexBindless != NULL;
+
+  // check whether adaptive screen sampling in ray tracing mode is supported
+  myHasRayTracingAdaptiveSampling = myHasRayTracing
+                                 && has44
+                                 && CheckExtension ("GL_NV_shader_atomic_float");
+
   if (!has32)
   {
     checkWrongVersion (3, 2);
index 8bb0444..1533183 100644 (file)
@@ -458,6 +458,15 @@ public:
   //! @return value for GL_MAX_CLIP_PLANES
   Standard_Integer MaxClipPlanes() const { return myMaxClipPlanes; }
 
+  //! @return TRUE if ray tracing mode is supported
+  Standard_Boolean HasRayTracing() const { return myHasRayTracing; }
+
+  //! @return TRUE if textures in ray tracing mode are supported
+  Standard_Boolean HasRayTracingTextures() const { return myHasRayTracingTextures; }
+
+  //! @return TRUE if adaptive screen sampling in ray tracing mode is supported
+  Standard_Boolean HasRayTracingAdaptiveSampling() const { return myHasRayTracingAdaptiveSampling; }
+
   //! Returns true if VBO is supported and permitted.
   inline bool ToUseVbo() const
   {
@@ -778,6 +787,10 @@ private: // context info
   Standard_Boolean myIsGlNormalizeEnabled; //!< GL_NORMALIZE flag
                                            //!< Used to tell OpenGl that normals should be normalized
 
+  Standard_Boolean myHasRayTracing;                 //! indicates whether ray tracing mode is supported 
+  Standard_Boolean myHasRayTracingTextures;         //! indicates whether textures in ray tracing mode are supported 
+  Standard_Boolean myHasRayTracingAdaptiveSampling; //! indicates whether adaptive screen sampling in ray tracing mode is supported 
+
   Handle(OpenGl_ShaderManager) myShaderManager; //! support object for managing shader programs
 
 private: //! @name fields tracking current state
index 358e833..8f84f63 100644 (file)
@@ -352,6 +352,13 @@ public: //! @name Texture mapping
   {
     ::glCopyTexSubImage1D(target, level, xoffset, x, y, width);
   }
+
+  inline void glGetTexImage (GLenum target, GLint level,
+                             GLenum format, GLenum type,
+                             GLvoid* pixels)
+  {
+    ::glGetTexImage (target, level, format, type, pixels);
+  }
 #endif
 
 #if !defined(GL_ES_VERSION_2_0)
@@ -400,13 +407,6 @@ public: //! @name Texture mapping
     ::glGetTexEnviv (target, pname, params);
   }
 
-  inline void glGetTexImage (GLenum target, GLint level,
-                             GLenum format, GLenum type,
-                             GLvoid* pixels)
-  {
-    ::glGetTexImage(target, level, format, type, pixels);
-  }
-
   inline void glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat* params)
   {
     ::glGetTexLevelParameterfv (target, level, pname, params);
index ff09ebb..37af470 100644 (file)
   #define GL_ALPHA8   0x803C
   #define GL_ALPHA16  0x803E
 
-  #define GL_RG       0x8227
-  #define GL_RG8      0x822B
-  #define GL_RG16     0x822C
-  #define GL_RG16F    0x822F
-  #define GL_RG32F    0x8230
+  #define GL_RG          0x8227
+  #define GL_RG8         0x822B
+  #define GL_RG16        0x822C
+  #define GL_RG16F       0x822F
+  #define GL_RG32F       0x8230
+  #define GL_RG_INTEGER  0x8228
+  #define GL_RED_INTEGER 0x8D94
 
   #define GL_R8I      0x8231
   #define GL_R8UI     0x8232
index 2b61a73..2ca1251 100644 (file)
@@ -386,6 +386,12 @@ Standard_Integer OpenGl_GraphicDriver::InquireLimit (const Graphic3d_TypeOfLimit
       return !aCtx.IsNull() ? aCtx->MaxTextureSize() : 1024;
     case Graphic3d_TypeOfLimit_MaxMsaa:
       return !aCtx.IsNull() ? aCtx->MaxMsaaSamples() : 0;
+    case Graphic3d_TypeOfLimit_HasRayTracing:
+      return (!aCtx.IsNull() && aCtx->HasRayTracing()) ? 1 : 0;
+    case Graphic3d_TypeOfLimit_HasRayTracingTextures:
+      return (!aCtx.IsNull() && aCtx->HasRayTracingTextures()) ? 1 : 0;
+    case Graphic3d_TypeOfLimit_HasRayTracingAdaptiveSampling:
+      return (!aCtx.IsNull() && aCtx->HasRayTracingAdaptiveSampling()) ? 1 : 0;
     case Graphic3d_TypeOfLimit_NB:
       return 0;
   }
diff --git a/src/OpenGl/OpenGl_HaltonSampler.hxx b/src/OpenGl/OpenGl_HaltonSampler.hxx
new file mode 100644 (file)
index 0000000..63f8a4c
--- /dev/null
@@ -0,0 +1,188 @@
+// Copyright (c) 2012 Leonhard Gruenschloss (leonhard@gruenschloss.org)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#ifndef _OpenGl_HaltonSampler_H
+#define _OpenGl_HaltonSampler_H
+
+#include <algorithm>
+#include <vector>
+
+//! Compute points of the Halton sequence with with digit-permutations for different bases.
+class OpenGl_HaltonSampler
+{
+public:
+
+  //! Return the number of supported dimensions.
+  static unsigned get_num_dimensions() { return 3u; }
+
+public:
+
+  //! Init the permutation arrays using Faure-permutations.
+  //! Alternatively, initRandom() can be called before the sampling functionality can be used.
+  void initFaure();
+
+  //! Init the permutation arrays using randomized permutations.
+  //! Alternatively, initFaure() can be called before the sampling functionality can be used.
+  //! The client needs to specify a random number generator function object that can be used to generate a random sequence of integers.
+  //! That is: if f is a random number generator and N is a positive integer,
+  //! then f(N) will return an integer less than N and greater than or equal to 0.
+  template <typename Random_number_generator>
+  void initRandom (Random_number_generator& theRand);
+
+  //! Return the Halton sample for the given dimension (component) and index.
+  //! The client must have called initRandom or initFaure() at least once before.
+  //! dimension must be smaller than the value returned by get_num_dimensions().
+  float sample (unsigned theDimension, unsigned theIndex) const
+  {
+    switch (theDimension)
+    {
+      case 0: return halton2 (theIndex);
+      case 1: return halton3 (theIndex);
+      case 2: return halton5 (theIndex);
+    }
+    return 0.0f;
+  }
+
+private:
+
+  static unsigned short invert (unsigned short theBase, unsigned short theDigits,
+                                unsigned short theIndex, const std::vector<unsigned short>& thePerm)
+  {
+    unsigned short aResult = 0;
+    for (unsigned short i = 0; i < theDigits; ++i)
+    {
+      aResult = aResult * theBase + thePerm[theIndex % theBase];
+      theIndex /= theBase;
+    }
+    return aResult;
+  }
+
+  void initTables (const std::vector<std::vector<unsigned short> >& thePerm)
+  {
+    for (unsigned short i = 0; i < 243; ++i)
+    {
+      myPerm3[i] = invert (3, 5, i, thePerm[3]);
+    }
+    for (unsigned short i = 0; i < 125; ++i)
+    {
+      myPerm5[i] = invert (5, 3, i, thePerm[5]);
+    }
+  }
+
+  //! Special case: radical inverse in base 2, with direct bit reversal.
+  float halton2 (unsigned theIndex) const
+  {
+    theIndex = (theIndex << 16) | (theIndex >> 16);
+    theIndex = ((theIndex & 0x00ff00ff) << 8) | ((theIndex & 0xff00ff00) >> 8);
+    theIndex = ((theIndex & 0x0f0f0f0f) << 4) | ((theIndex & 0xf0f0f0f0) >> 4);
+    theIndex = ((theIndex & 0x33333333) << 2) | ((theIndex & 0xcccccccc) >> 2);
+    theIndex = ((theIndex & 0x55555555) << 1) | ((theIndex & 0xaaaaaaaa) >> 1);
+    union Result { unsigned u; float f; } aResult; // Write reversed bits directly into floating-point mantissa.
+    aResult.u = 0x3f800000u | (theIndex >> 9);
+    return aResult.f - 1.0f;
+  }
+
+  float halton3 (unsigned theIndex) const
+  {
+    return (myPerm3[theIndex % 243u] * 14348907u
+          + myPerm3[(theIndex / 243u)      % 243u] * 59049u
+          + myPerm3[(theIndex / 59049u)    % 243u] * 243u
+          + myPerm3[(theIndex / 14348907u) % 243u]) * float(0.999999999999999f / 3486784401u); // Results in [0,1).
+  }
+
+  float halton5 (unsigned theIndex) const
+  {
+    return (myPerm5[theIndex % 125u] * 1953125u
+          + myPerm5[(theIndex / 125u)     % 125u] * 15625u
+          + myPerm5[(theIndex / 15625u)   % 125u] * 125u
+          + myPerm5[(theIndex / 1953125u) % 125u]) * float(0.999999999999999f / 244140625u); // Results in [0,1).
+  }
+
+private:
+
+  unsigned short myPerm3[243];
+  unsigned short myPerm5[125];
+
+};
+
+inline void OpenGl_HaltonSampler::initFaure()
+{
+  const unsigned THE_MAX_BASE = 5u;
+  std::vector<std::vector<unsigned short> > aPerms(THE_MAX_BASE + 1);
+  for (unsigned k = 1; k <= 3; ++k) // Keep identity permutations for base 1, 2, 3.
+  {
+    aPerms[k].resize(k);
+    for (unsigned i = 0; i < k; ++i)
+    {
+      aPerms[k][i] = static_cast<unsigned short> (i);
+    }
+  }
+
+  for (unsigned aBase = 4; aBase <= THE_MAX_BASE; ++aBase)
+  {
+    aPerms[aBase].resize(aBase);
+    const unsigned b = aBase / 2;
+    if (aBase & 1) // odd
+    {
+      for (unsigned i = 0; i < aBase - 1; ++i)
+      {
+        aPerms[aBase][i + (i >= b)] = aPerms[aBase - 1][i] + (aPerms[aBase - 1][i] >= b);
+      }
+      aPerms[aBase][b] = static_cast<unsigned short> (b);
+    }
+    else // even
+    {
+      for (unsigned i = 0; i < b; ++i)
+      {
+        aPerms[aBase][i]     = 2 * aPerms[b][i];
+        aPerms[aBase][b + i] = 2 * aPerms[b][i] + 1;
+      }
+    }
+  }
+  initTables (aPerms);
+}
+
+template <typename Random_number_generator>
+void OpenGl_HaltonSampler::initRandom (Random_number_generator& theRand)
+{
+  const unsigned THE_MAX_BASE = 5u;
+  std::vector<std::vector<unsigned short> > aPerms(THE_MAX_BASE + 1);
+  for (unsigned k = 1; k <= 3; ++k) // Keep identity permutations for base 1, 2, 3.
+  {
+    aPerms[k].resize (k);
+    for (unsigned i = 0; i < k; ++i)
+    {
+      aPerms[k][i] = i;
+    }
+  }
+
+  for (unsigned aBase = 4; aBase <= THE_MAX_BASE; ++aBase)
+  {
+    aPerms[aBase].resize (aBase);
+    for (unsigned i = 0; i < aBase; ++i)
+    {
+      aPerms[aBase][i] = i;
+    }
+    std::random_shuffle (aPerms[aBase].begin(), aPerms[aBase].end(), theRand);
+  }
+  initTables (aPerms);
+}
+
+#endif // _OpenGl_HaltonSampler_H
index d363629..3864a62 100755 (executable)
@@ -502,6 +502,11 @@ Standard_Boolean OpenGl_RaytraceGeometry::ReleaseTextures (const Handle(OpenGl_C
 // =======================================================================
 Standard_Integer OpenGl_RaytraceGeometry::AddTexture (const Handle(OpenGl_Texture)& theTexture)
 {
+  if (theTexture->TextureId() == OpenGl_Texture::NO_TEXTURE)
+  {
+    return -1;
+  }
+
   NCollection_Vector<Handle (OpenGl_Texture)>::iterator anIter =
     std::find (myTextures.begin(), myTextures.end(), theTexture);
 
diff --git a/src/OpenGl/OpenGl_TileSampler.cxx b/src/OpenGl/OpenGl_TileSampler.cxx
new file mode 100644 (file)
index 0000000..6e8a1dc
--- /dev/null
@@ -0,0 +1,187 @@
+// Created on: 2016-06-16
+// Created by: Denis BOGOLEPOV & Danila ULYANOV
+// Copyright (c) 2016 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <OpenGl_Context.hxx>
+#include <OpenGl_TileSampler.hxx>
+#include <TCollection_ExtendedString.hxx>
+
+namespace
+{
+  //! Scale factor for estimating visual error.
+  static const float THE_SCALE_FACTOR = 1.0f / 1e6f;
+}
+
+//=======================================================================
+//function : OpenGl_TileSampler
+//purpose  :
+//=======================================================================
+OpenGl_TileSampler::OpenGl_TileSampler()
+: mySample (0),
+  mySizeX  (0),
+  mySizeY  (0),
+  myTilesX (0),
+  myTilesY (0)
+{
+  mySampler.initFaure();
+}
+
+//=======================================================================
+//function : GrabVarianceMap
+//purpose  :
+//=======================================================================
+void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext)
+{
+#if !defined(GL_ES_VERSION_2_0)
+  std::vector<GLint> aRawData (NbTiles(), 0);
+
+  theContext->core11fwd->glGetTexImage (GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, &aRawData.front());
+  const GLenum anErr = theContext->core11fwd->glGetError();
+  if (anErr != GL_NO_ERROR)
+  {
+    theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
+                             "Error! Failed to fetch visual error map from the GPU");
+  }
+  else
+  {
+    for (int aTileIdx = 0, aNbTiles = NbTiles(); aTileIdx < aNbTiles; ++aTileIdx)
+    {
+      myVarianceMap[aTileIdx] = aRawData[aTileIdx] * THE_SCALE_FACTOR;
+    }
+
+    for (int aX = 0; aX < myTilesX; ++aX)
+    {
+      for (int aY = 0; aY < myTilesY; ++aY)
+      {
+        ChangeTile (aX, aY) *= 1.0f / TileArea (aX, aY); // average error over the tile
+
+        if (aY > 0)
+        {
+          ChangeTile (aX, aY) += Tile (aX, aY - 1);
+        }
+      }
+    }
+
+    myMarginalMap.resize (myTilesX); // build marginal distribution
+    for (int aX = 0; aX < myTilesX; ++aX)
+    {
+      myMarginalMap[aX] = Tile (aX, myTilesY - 1);
+
+      if (aX > 0)
+        myMarginalMap[aX] += myMarginalMap[aX - 1];
+    }
+  }
+#else
+  // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
+  (void )theContext;
+#endif
+}
+
+//=======================================================================
+//function : Sample
+//purpose  :
+//=======================================================================
+void OpenGl_TileSampler::Sample (int& theOffsetX,
+                                 int& theOffsetY)
+{
+  int aX = 0;
+  int aY = 0;
+
+  const float aKsiX = mySampler.sample (0, mySample) * myMarginalMap.back();
+  for (; aX < myTilesX - 1; ++aX)
+  {
+    if (aKsiX <= myMarginalMap[aX])
+    {
+      break;
+    }
+  }
+
+  const float aKsiY = mySampler.sample (1, mySample) * Tile (aX, myTilesY - 1);
+  for (; aY < myTilesY - 1; ++aY)
+  {
+    if (aKsiY <= Tile (aX, aY))
+    {
+      break;
+    }
+  }
+
+  theOffsetX = aX * TileSize();
+  theOffsetY = aY * TileSize();
+
+  ++mySample;
+}
+
+//=======================================================================
+//function : SetSize
+//purpose  :
+//=======================================================================
+void OpenGl_TileSampler::SetSize (const int theSizeX,
+                                  const int theSizeY)
+{
+  if (mySizeX == theSizeX
+   && mySizeY == theSizeY)
+  {
+    return;
+  }
+
+  mySizeX = theSizeX;
+  mySizeY = theSizeY;
+
+  myTilesX = static_cast<int> (ceilf (static_cast<float> (mySizeX) / TileSize()));
+  myTilesY = static_cast<int> (ceilf (static_cast<float> (mySizeY) / TileSize()));
+
+  myVarianceMap.resize (myTilesX * myTilesY);
+}
+
+//=======================================================================
+//function : Upload
+//purpose  :
+//=======================================================================
+void OpenGl_TileSampler::Upload (const Handle(OpenGl_Context)& theContext,
+                                 const Handle(OpenGl_Texture)& theTexture,
+                                 bool theAdaptive)
+{
+  if (theTexture.IsNull())
+  {
+    return;
+  }
+
+  std::vector<GLint> aData (myTilesX * myTilesY * 2);
+  for (int aX = 0; aX < myTilesX; ++aX)
+  {
+    for (int aY = 0; aY < myTilesY; ++aY)
+    {
+      if (!theAdaptive)
+      {
+        aData[(aY * myTilesX + aX) * 2 + 0] = aX * TileSize();
+        aData[(aY * myTilesX + aX) * 2 + 1] = aY * TileSize();
+      }
+      else
+      {
+        Sample (aData[(aY * myTilesX + aX) * 2 + 0],
+                aData[(aY * myTilesX + aX) * 2 + 1]);
+      }
+    }
+  }
+
+  theTexture->Bind (theContext);
+
+  theContext->core11fwd->glTexImage2D (GL_TEXTURE_2D, 0, GL_RG32I, myTilesX, myTilesY, 0, GL_RG_INTEGER, GL_UNSIGNED_INT, &aData.front());
+  const GLenum anErr = theContext->core11fwd->glGetError();
+  if (anErr != GL_NO_ERROR)
+  {
+    theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
+                             "Error! Failed to upload tile offset map on the GPU");
+  }
+}
diff --git a/src/OpenGl/OpenGl_TileSampler.hxx b/src/OpenGl/OpenGl_TileSampler.hxx
new file mode 100644 (file)
index 0000000..4f27293
--- /dev/null
@@ -0,0 +1,110 @@
+// Created on: 2016-06-16
+// Created by: Denis BOGOLEPOV & Danila ULYANOV
+// Copyright (c) 2016 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _OpenGl_TileSampler_H
+#define _OpenGl_TileSampler_H
+
+#include <OpenGl_Texture.hxx>
+#include <OpenGl_HaltonSampler.hxx>
+
+#include <vector>
+
+//! Tool object used for sampling screen tiles according to estimated pixel variance (used in path tracing engine).
+//! To improve GPU thread coherency, render window is split into pixel blocks or tiles.
+class OpenGl_TileSampler
+{
+public:
+
+  //! Size of individual tile in pixels.
+  static int TileSize() { return 32; }
+
+public:
+
+  //! Creates new tile sampler.
+  Standard_EXPORT OpenGl_TileSampler();
+
+  //! Returns width of ray-tracing viewport.
+  int SizeX() const { return mySizeX; }
+
+  //! Returns height of ray-tracing viewport.
+  int SizeY() const { return mySizeY; }
+
+  //! Returns number of tiles in X dimension.
+  int NbTilesX() const { return myTilesX; }
+
+  //! Returns number of tiles in Y dimension.
+  int NbTilesY() const { return myTilesY; }
+
+  //! Returns total number of tiles in viewport.
+  int NbTiles() const { return myTilesX * myTilesY; }
+
+  //! Specifies size of ray-tracing viewport.
+  Standard_EXPORT void SetSize (const int theSizeX,
+                                const int theSizeY);
+
+  //! Returns number of pixels in the given tile.
+  int TileArea (const int theX,
+                const int theY) const
+  {
+    return Min (TileSize(), mySizeX - theX * TileSize())
+         * Min (TileSize(), mySizeY - theY * TileSize());
+  }
+
+  //! Fetches current error estimation from the GPU and
+  //! builds 2D discrete distribution for tile sampling.
+  Standard_EXPORT void GrabVarianceMap (const Handle(OpenGl_Context)& theContext);
+
+  //! Samples tile location according to estimated error.
+  Standard_EXPORT void Sample (int& theOffsetX,
+                               int& theOffsetY);
+
+  //! Resets tile sampler to initial state.
+  void Reset() { mySample = 0; }
+
+  //! Uploads offsets of sampled tiles to the given OpenGL texture.
+  Standard_EXPORT void Upload (const Handle(OpenGl_Context)& theContext,
+                               const Handle(OpenGl_Texture)& theTexture,
+                               bool theAdaptive);
+
+protected:
+
+  //! Returns tile value (estimated error).
+  float Tile (const int theX,
+              const int theY) const
+  {
+    return myVarianceMap[theY * myTilesX + theX];
+  }
+
+  //! Returns tile value (estimated error).
+  float& ChangeTile (const int theX,
+                     const int theY)
+  {
+    return myVarianceMap[theY * myTilesX + theX];
+  }
+
+protected:
+
+  std::vector<float>   myVarianceMap; //!< Estimation of visual error per tile
+  std::vector<float>   myMarginalMap; //!< Marginal distribution of 2D error map
+  OpenGl_HaltonSampler mySampler;     //!< Halton sequence generator
+  int                  mySample;      //!< Index of generated sample
+  int                  mySizeX;       //!< Width of ray-tracing viewport
+  int                  mySizeY;       //!< Height of ray-tracing viewport
+  int                  myTilesX;      //!< Number of tiles in X dimension
+  int                  myTilesY;      //!< Number of tiles in Y dimension
+
+};
+
+#endif // _OpenGl_TileSampler_H
index 514ae93..06a8053 100644 (file)
@@ -49,6 +49,7 @@
 #include <OpenGl_Trihedron.hxx>
 #include <OpenGl_Window.hxx>
 #include <OpenGl_Workspace.hxx>
+#include <OpenGl_TileSampler.hxx>
 
 #include <map>
 #include <set>
@@ -513,6 +514,10 @@ protected:
 
   Handle(OpenGl_Texture) myTextureEnv;
 
+  //! Framebuffers for OpenGL output.
+  Handle(OpenGl_FrameBuffer) myOpenGlFBO;
+  Handle(OpenGl_FrameBuffer) myOpenGlFBO2;
+
 protected: //! @name Rendering properties
 
   //! Two framebuffers (left and right views) store cached main presentation
@@ -569,7 +574,7 @@ protected: //! @name data types related to ray-tracing
     OpenGl_RT_uDirectLB,
     OpenGl_RT_uDirectRT,
     OpenGl_RT_uDirectRB,
-    OpenGl_RT_uViewMat,
+    OpenGl_RT_uViewPrMat,
     OpenGl_RT_uUnviewMat,
 
     // 3D scene params
@@ -590,6 +595,10 @@ protected: //! @name data types related to ray-tracing
     OpenGl_RT_uTexSamplersArray,
     OpenGl_RT_uBlockedRngEnabled,
 
+    // size of render window
+    OpenGl_RT_uWinSizeX,
+    OpenGl_RT_uWinSizeY,
+
     // sampled frame params
     OpenGl_RT_uSampleWeight,
     OpenGl_RT_uFrameRndSeed,
@@ -599,10 +608,14 @@ protected: //! @name data types related to ray-tracing
     OpenGl_RT_uOffsetY,
     OpenGl_RT_uSamples,
 
+    // adaptive path tracing images
+    OpenGl_RT_uRenderImage,
+    OpenGl_RT_uOffsetImage,
+
     OpenGl_RT_NbVariables // special field
   };
 
-  //! Defines texture samplers.
+  //! Defines OpenGL texture samplers.
   enum ShaderSamplerNames
   {
     OpenGl_RT_SceneNodeInfoTexture  = 0,
@@ -622,10 +635,17 @@ protected: //! @name data types related to ray-tracing
 
     OpenGl_RT_FsaaInputTexture = 11,
     OpenGl_RT_PrevAccumTexture = 12,
-    OpenGl_RT_DepthTexture = 13,
 
-    OpenGl_RT_OpenGlColorTexture = 14,
-    OpenGl_RT_OpenGlDepthTexture = 15
+    OpenGl_RT_RaytraceDepthTexture = 13
+  };
+
+  //! Defines OpenGL image samplers.
+  enum ShaderImageNames
+  {
+    OpenGl_RT_OutputImageLft = 0,
+    OpenGl_RT_OutputImageRgh = 1,
+    OpenGl_RT_VisualErrorImage = 2,
+    OpenGl_RT_TileOffsetsImage = 3
   };
 
   //! Tool class for management of shader sources.
@@ -700,13 +720,17 @@ protected: //! @name data types related to ray-tracing
     //! Enables/disables the use of OpenGL bindless textures.
     Standard_Boolean UseBindlessTextures;
 
+    //! Enables/disables adaptive screen sampling for path tracing.
+    Standard_Boolean AdaptiveScreenSampling;
+
     //! Creates default compile-time ray-tracing parameters.
     RaytracingParams()
     : StackSize (THE_DEFAULT_STACK_SIZE),
       NbBounces (THE_DEFAULT_NB_BOUNCES),
       TransparentShadows (Standard_False),
       GlobalIllumination  (Standard_False),
-      UseBindlessTextures (Standard_False)
+      UseBindlessTextures (Standard_False),
+      AdaptiveScreenSampling (Standard_False)
     {
       //
     }
@@ -867,25 +891,31 @@ protected: //! @name methods related to ray-tracing
   void unbindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext);
 
   //! Sets uniform state for the given ray-tracing shader program.
-  Standard_Boolean setUniformState (const OpenGl_Vec3*            theOrigins,
-                                    const OpenGl_Vec3*            theDirects,
-                                    const OpenGl_Mat4&            theViewMat,
-                                    const OpenGl_Mat4&            theUnviewMat,
-                                    const Standard_Integer        theProgramId,
+  Standard_Boolean setUniformState (const Standard_Integer        theProgramId,
+                                    const Standard_Integer        theSizeX,
+                                    const Standard_Integer        theSizeY,
                                     const Handle(OpenGl_Context)& theGlContext);
 
   //! Runs ray-tracing shader programs.
   Standard_Boolean runRaytraceShaders (const Standard_Integer        theSizeX,
                                        const Standard_Integer        theSizeY,
-                                       const OpenGl_Vec3*            theOrigins,
-                                       const OpenGl_Vec3*            theDirects,
-                                       const OpenGl_Mat4&            theViewMat,
-                                       const OpenGl_Mat4&            theUnviewMat,
                                        Graphic3d_Camera::Projection  theProjection,
                                        OpenGl_FrameBuffer*           theReadDrawFbo,
                                        const Handle(OpenGl_Context)& theGlContext);
 
-  //! Redraws the window using OpenGL/GLSL ray-tracing.
+  //! Runs classical (Whitted-style) ray-tracing kernel.
+  Standard_Boolean runRaytrace (const Standard_Integer        theSizeX,
+                                const Standard_Integer        theSizeY,
+                                Graphic3d_Camera::Projection  theProjection,
+                                OpenGl_FrameBuffer*           theReadDrawFbo,
+                                const Handle(OpenGl_Context)& theGlContext);
+
+  //! Runs path tracing (global illumination) kernel.
+  Standard_Boolean runPathtrace (const Graphic3d_Camera::Projection theProjection,
+                                 OpenGl_FrameBuffer*                theReadDrawFbo,
+                                 const Handle(OpenGl_Context)&      theGlContext);
+
+  //! Redraws the window using OpenGL/GLSL ray-tracing or path tracing.
   Standard_Boolean raytrace (const Standard_Integer        theSizeX,
                              const Standard_Integer        theSizeY,
                              Graphic3d_Camera::Projection  theProjection,
@@ -894,13 +924,13 @@ protected: //! @name methods related to ray-tracing
 
 protected: //! @name fields related to ray-tracing
 
-  //! Result of shaders initialization.
+  //! Result of RT/PT shaders initialization.
   RaytraceInitStatus myRaytraceInitStatus;
 
-  //! Is geometry data valid?
+  //! Is ray-tracing geometry data valid?
   Standard_Boolean myIsRaytraceDataValid;
 
-  //! Warning about missing extension GL_ARB_bindless_texture has been displayed?
+  //! True if warning about missing extension GL_ARB_bindless_texture has been displayed.
   Standard_Boolean myIsRaytraceWarnTextures;
 
   //! 3D scene geometry data for ray-tracing.
@@ -918,11 +948,15 @@ protected: //! @name fields related to ray-tracing
   ShaderSource myRaytraceShaderSource;
   //! OpenGL/GLSL source of adaptive-AA fragment shader.
   ShaderSource myPostFSAAShaderSource;
+  //! OpenGL/GLSL source of RT/PT display fragment shader.
+  ShaderSource myOutImageShaderSource;
 
   //! OpenGL/GLSL ray-tracing fragment shader.
   Handle(OpenGl_ShaderObject) myRaytraceShader;
   //! OpenGL/GLSL adaptive-AA fragment shader.
   Handle(OpenGl_ShaderObject) myPostFSAAShader;
+  //! OpenGL/GLSL ray-tracing display fragment shader.
+  Handle(OpenGl_ShaderObject) myOutImageShader;
 
   //! OpenGL/GLSL ray-tracing shader program.
   Handle(OpenGl_ShaderProgram) myRaytraceProgram;
@@ -955,12 +989,22 @@ protected: //! @name fields related to ray-tracing
   Handle(OpenGl_TextureBufferArb) myRaytraceLightSrcTexture;
 
   //! 1st framebuffer (FBO) to perform adaptive FSAA.
+  //! Used in compatibility mode (no adaptive sampling).
   Handle(OpenGl_FrameBuffer) myRaytraceFBO1[2];
   //! 2nd framebuffer (FBO) to perform adaptive FSAA.
+  //! Used in compatibility mode (no adaptive sampling).
   Handle(OpenGl_FrameBuffer) myRaytraceFBO2[2];
-  //! Framebuffer (FBO) for preliminary OpenGL output.
-  Handle(OpenGl_FrameBuffer) myOpenGlFBO;
-  Handle(OpenGl_FrameBuffer) myOpenGlFBO2;
+
+  //! Output textures (2 textures are used in stereo mode).
+  //! Used if adaptive screen sampling is activated.
+  Handle(OpenGl_Texture) myRaytraceOutputTexture[2];
+
+  //! Texture containing per-tile visual error estimation.
+  //! Used if adaptive screen sampling is activated.
+  Handle(OpenGl_Texture) myRaytraceVisualErrorTexture;
+  //! Texture containing offsets of sampled screen tiles.
+  //! Used if adaptive screen sampling is activated.
+  Handle(OpenGl_Texture) myRaytraceTileOffsetsTexture;
 
   //! Vertex buffer (VBO) for drawing dummy quad.
   OpenGl_VertexBuffer myRaytraceScreenQuad;
@@ -995,6 +1039,9 @@ protected: //! @name fields related to ray-tracing
   //! Bullard RNG to produce random sequence.
   math_BullardGenerator myRNG;
 
+  //! Tool object for sampling screen tiles in PT mode.
+  OpenGl_TileSampler myTileSampler;
+
 public:
 
   DEFINE_STANDARD_ALLOC
index 03f6f8d..df86d0e 100644 (file)
@@ -18,6 +18,7 @@
 #include <Graphic3d_TextureParams.hxx>
 #include <OpenGl_PrimitiveArray.hxx>
 #include <OpenGl_VertexBuffer.hxx>
+#include <OpenGl_GlCore44.hxx>
 #include <OSD_Protection.hxx>
 #include <OSD_File.hxx>
 
@@ -214,7 +215,9 @@ Standard_Boolean OpenGl_View::updateRaytraceGeometry (const RaytraceUpdateMode
     }
 
     if (toRestart)
+    {
       myAccumFrames = 0;
+    }
 
     myNonRaytraceStructureIDs = aNonRaytraceIDs;
   }
@@ -393,7 +396,7 @@ OpenGl_RaytraceMaterial OpenGl_View::convertMaterial (const OpenGl_AspectFace*
   // Handle material textures
   if (theAspect->Aspect()->ToMapTexture())
   {
-    if (theGlContext->arbTexBindless != NULL)
+    if (theGlContext->HasRayTracingTextures())
     {
       buildTextureTransform (theAspect->TextureParams(), theMaterial.TextureTransform);
 
@@ -1080,9 +1083,19 @@ TCollection_AsciiString OpenGl_View::generateShaderPrefix (const Handle(OpenGl_C
       TCollection_AsciiString ("\n#define MAX_TEX_NUMBER ") + TCollection_AsciiString (OpenGl_RaytraceGeometry::MAX_TEX_NUMBER);
   }
 
-  if (myRaytraceParameters.GlobalIllumination)
+  if (myRaytraceParameters.GlobalIllumination) // path tracing activated
   {
     aPrefixString += TCollection_AsciiString ("\n#define PATH_TRACING");
+
+    if (myRaytraceParameters.AdaptiveScreenSampling) // adaptive screen sampling requested
+    {
+      // to activate the feature we need OpenGL 4.4 and GL_NV_shader_atomic_float extension
+      if (theGlContext->IsGlGreaterEqual (4, 4) && theGlContext->CheckExtension ("GL_NV_shader_atomic_float"))
+      {
+        aPrefixString += TCollection_AsciiString ("\n#define ADAPTIVE_SAMPLING") +
+          TCollection_AsciiString ("\n#define BLOCK_SIZE ") + TCollection_AsciiString (OpenGl_TileSampler::TileSize());
+      }
+    }
   }
 
   return aPrefixString;
@@ -1155,6 +1168,10 @@ Handle(OpenGl_ShaderObject) OpenGl_View::initShader (const GLenum
 
     aShader->Release (theGlContext.operator->());
 
+#ifdef RAY_TRACE_PRINT_INFO
+    std::cout << "Shader build log:\n" << aBuildLog << "\n";
+#endif
+
     return Handle(OpenGl_ShaderObject)();
   }
   else if (theGlContext->caps->glslWarnings)
@@ -1169,6 +1186,10 @@ Handle(OpenGl_ShaderObject) OpenGl_View::initShader (const GLenum
       theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION,
         GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW, aMessage);
     }
+
+#ifdef RAY_TRACE_PRINT_INFO
+    std::cout << "Shader build log:\n" << aBuildLog << "\n";
+#endif
   }
 
   return aShader;
@@ -1300,15 +1321,32 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
       aToRebuildShaders = Standard_True;
     }
 
+    if (myRenderParams.AdaptiveScreenSampling != myRaytraceParameters.AdaptiveScreenSampling)
+    {
+      myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling;
+      if (myRenderParams.AdaptiveScreenSampling) // adaptive sampling was requested
+      {
+        if (!theGlContext->HasRayTracingAdaptiveSampling())
+        {
+          // disable the feature if it is not supported
+          myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling = Standard_False;
+          theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
+                                     "Adaptive sampling not supported (OpenGL 4.4 or GL_NV_shader_atomic_float is missing)");
+        }
+      }
+
+      aToRebuildShaders = Standard_True;
+    }
+
     if (aToRebuildShaders)
     {
       // Reject accumulated frames
       myAccumFrames = 0;
 
-      // We need to update environment texture
+      // Environment map should be updated
       myToUpdateEnvironmentMap = Standard_True;
 
-      TCollection_AsciiString aPrefixString = generateShaderPrefix (theGlContext);
+      const TCollection_AsciiString aPrefixString = generateShaderPrefix (theGlContext);
 
 #ifdef RAY_TRACE_PRINT_INFO
       std::cout << "GLSL prefix string:" << std::endl << aPrefixString << std::endl;
@@ -1316,23 +1354,29 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
 
       myRaytraceShaderSource.SetPrefix (aPrefixString);
       myPostFSAAShaderSource.SetPrefix (aPrefixString);
+      myOutImageShaderSource.SetPrefix (aPrefixString);
 
       if (!myRaytraceShader->LoadSource (theGlContext, myRaytraceShaderSource.Source())
-       || !myPostFSAAShader->LoadSource (theGlContext, myPostFSAAShaderSource.Source()))
+       || !myPostFSAAShader->LoadSource (theGlContext, myPostFSAAShaderSource.Source())
+       || !myOutImageShader->LoadSource (theGlContext, myOutImageShaderSource.Source()))
       {
         return safeFailBack ("Failed to load source into ray-tracing fragment shaders", theGlContext);
       }
 
       if (!myRaytraceShader->Compile (theGlContext)
-       || !myPostFSAAShader->Compile (theGlContext))
+       || !myPostFSAAShader->Compile (theGlContext)
+       || !myOutImageShader->Compile (theGlContext))
       {
         return safeFailBack ("Failed to compile ray-tracing fragment shaders", theGlContext);
       }
 
       myRaytraceProgram->SetAttributeName (theGlContext, Graphic3d_TOA_POS, "occVertex");
       myPostFSAAProgram->SetAttributeName (theGlContext, Graphic3d_TOA_POS, "occVertex");
+      myOutImageProgram->SetAttributeName (theGlContext, Graphic3d_TOA_POS, "occVertex");
+
       if (!myRaytraceProgram->Link (theGlContext)
-       || !myPostFSAAProgram->Link (theGlContext))
+       || !myPostFSAAProgram->Link (theGlContext)
+       || !myOutImageProgram->Link (theGlContext))
       {
         return safeFailBack ("Failed to initialize vertex attributes for ray-tracing program", theGlContext);
       }
@@ -1356,7 +1400,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
 
     myRaytraceParameters.NbBounces = myRenderParams.RaytracingDepth;
 
-    TCollection_AsciiString aFolder = Graphic3d_ShaderProgram::ShadersFolder();
+    const TCollection_AsciiString aFolder = Graphic3d_ShaderProgram::ShadersFolder();
 
     if (aFolder.IsEmpty())
     {
@@ -1369,7 +1413,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
         myRaytraceGeometry.TopLevelTreeDepth() + myRaytraceGeometry.BotLevelTreeDepth());
     }
 
-    TCollection_AsciiString aPrefixString  = generateShaderPrefix (theGlContext);
+    const TCollection_AsciiString aPrefixString  = generateShaderPrefix (theGlContext);
 
 #ifdef RAY_TRACE_PRINT_INFO
     std::cout << "GLSL prefix string:" << std::endl << aPrefixString << std::endl;
@@ -1444,11 +1488,10 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
     }
 
     {
-      ShaderSource aDispShaderSrc;
       TCollection_AsciiString aFiles[] = { aFolder + "/Display.fs", "" };
-      if (!aDispShaderSrc.Load (aFiles, aPrefixString))
+      if (!myOutImageShaderSource.Load (aFiles, aPrefixString))
       {
-        return safeFailBack (aDispShaderSrc.ErrorDescription(), theGlContext);
+        return safeFailBack (myOutImageShaderSource.ErrorDescription(), theGlContext);
       }
 
       Handle(OpenGl_ShaderObject) aBasicVertShader = initShader (GL_VERTEX_SHADER, aBasicVertShaderSrc, theGlContext);
@@ -1457,17 +1500,17 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
         return safeFailBack ("Failed to set vertex shader source", theGlContext);
       }
 
-      Handle(OpenGl_ShaderObject) aDisplayShader = initShader (GL_FRAGMENT_SHADER, aDispShaderSrc, theGlContext);
-      if (aDisplayShader.IsNull())
+      myOutImageShader = initShader (GL_FRAGMENT_SHADER, myOutImageShaderSource, theGlContext);
+      if (myOutImageShader.IsNull())
       {
         aBasicVertShader->Release (theGlContext.operator->());
         return safeFailBack ("Failed to set display fragment shader source", theGlContext);
       }
 
-      myOutImageProgram = initProgram (theGlContext, aBasicVertShader, aDisplayShader);
+      myOutImageProgram = initProgram (theGlContext, aBasicVertShader, myOutImageShader);
       if (myOutImageProgram.IsNull())
       {
-        return safeFailBack ("Failed to initialize output shader program", theGlContext);
+        return safeFailBack ("Failed to initialize display shader program", theGlContext);
       }
     }
   }
@@ -1504,11 +1547,6 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
       aShaderProgram->SetSampler (theGlContext,
         "uRaytraceLightSrcTexture", OpenGl_RT_RaytraceLightSrcTexture);
 
-      aShaderProgram->SetSampler (theGlContext,
-        "uOpenGlColorTexture", OpenGl_RT_OpenGlColorTexture);
-      aShaderProgram->SetSampler (theGlContext,
-        "uOpenGlDepthTexture", OpenGl_RT_OpenGlDepthTexture);
-
       if (anIndex == 1)
       {
         aShaderProgram->SetSampler (theGlContext,
@@ -1539,7 +1577,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
         aShaderProgram->GetUniformLocation (theGlContext, "uDirectLT");
       myUniformLocations[anIndex][OpenGl_RT_uDirectRT] =
         aShaderProgram->GetUniformLocation (theGlContext, "uDirectRT");
-      myUniformLocations[anIndex][OpenGl_RT_uViewMat] =
+      myUniformLocations[anIndex][OpenGl_RT_uViewPrMat] =
         aShaderProgram->GetUniformLocation (theGlContext, "uViewMat");
       myUniformLocations[anIndex][OpenGl_RT_uUnviewMat] =
         aShaderProgram->GetUniformLocation (theGlContext, "uUnviewMat");
@@ -1574,11 +1612,21 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
       myUniformLocations[anIndex][OpenGl_RT_uBlockedRngEnabled] =
         aShaderProgram->GetUniformLocation (theGlContext, "uBlockedRngEnabled");
 
+      myUniformLocations[anIndex][OpenGl_RT_uWinSizeX] =
+        aShaderProgram->GetUniformLocation (theGlContext, "uWinSizeX");
+      myUniformLocations[anIndex][OpenGl_RT_uWinSizeY] =
+        aShaderProgram->GetUniformLocation (theGlContext, "uWinSizeY");
+
       myUniformLocations[anIndex][OpenGl_RT_uSampleWeight] =
         aShaderProgram->GetUniformLocation (theGlContext, "uSampleWeight");
       myUniformLocations[anIndex][OpenGl_RT_uFrameRndSeed] =
         aShaderProgram->GetUniformLocation (theGlContext, "uFrameRndSeed");
 
+      myUniformLocations[anIndex][OpenGl_RT_uRenderImage] =
+        aShaderProgram->GetUniformLocation (theGlContext, "uRenderImage");
+      myUniformLocations[anIndex][OpenGl_RT_uOffsetImage] =
+        aShaderProgram->GetUniformLocation (theGlContext, "uOffsetImage");
+
       myUniformLocations[anIndex][OpenGl_RT_uBackColorTop] =
         aShaderProgram->GetUniformLocation (theGlContext, "uBackColorTop");
       myUniformLocations[anIndex][OpenGl_RT_uBackColorBot] =
@@ -1591,7 +1639,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
       "uInputTexture", OpenGl_RT_PrevAccumTexture);
 
     myOutImageProgram->SetSampler (theGlContext,
-      "uDepthTexture", OpenGl_RT_DepthTexture);
+      "uDepthTexture", OpenGl_RT_RaytraceDepthTexture);
 
     theGlContext->BindProgram (NULL);
   }
@@ -1617,11 +1665,10 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
 
 // =======================================================================
 // function : nullifyResource
-// purpose  :
+// purpose  : Releases OpenGL resource
 // =======================================================================
 template <class T>
-inline void nullifyResource (const Handle(OpenGl_Context)& theGlContext,
-                             Handle(T)& theResource)
+inline void nullifyResource (const Handle(OpenGl_Context)& theGlContext, Handle(T)& theResource)
 {
   if (!theResource.IsNull())
   {
@@ -1641,6 +1688,12 @@ void OpenGl_View::releaseRaytraceResources (const Handle(OpenGl_Context)& theGlC
   myRaytraceFBO2[0]->Release (theGlContext.operator->());
   myRaytraceFBO2[1]->Release (theGlContext.operator->());
 
+  nullifyResource (theGlContext, myRaytraceOutputTexture[0]);
+  nullifyResource (theGlContext, myRaytraceOutputTexture[1]);
+
+  nullifyResource (theGlContext, myRaytraceTileOffsetsTexture);
+  nullifyResource (theGlContext, myRaytraceVisualErrorTexture);
+
   nullifyResource (theGlContext, myRaytraceShader);
   nullifyResource (theGlContext, myPostFSAAShader);
 
@@ -1664,7 +1717,9 @@ void OpenGl_View::releaseRaytraceResources (const Handle(OpenGl_Context)& theGlC
   myRaytraceGeometry.ReleaseResources (theGlContext);
 
   if (myRaytraceScreenQuad.IsValid())
+  {
     myRaytraceScreenQuad.Release (theGlContext.operator->());
+  }
 }
 
 // =======================================================================
@@ -1682,6 +1737,7 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
     myRaytraceFBO2[0]->Release (theGlContext.operator->());
     myRaytraceFBO1[1]->Release (theGlContext.operator->());
     myRaytraceFBO2[1]->Release (theGlContext.operator->());
+
     return Standard_True;
   }
 
@@ -1700,6 +1756,53 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
     myRaytraceFBO2[1]->Release (theGlContext.operator->());
   }
 
+  myTileSampler.SetSize (theSizeX, theSizeY);
+
+  if (myRaytraceTileOffsetsTexture.IsNull())
+  {
+    myRaytraceOutputTexture[0] = new OpenGl_Texture();
+    myRaytraceOutputTexture[1] = new OpenGl_Texture();
+
+    myRaytraceTileOffsetsTexture = new OpenGl_Texture();
+    myRaytraceVisualErrorTexture = new OpenGl_Texture();
+  }
+
+  if (myRaytraceOutputTexture[0]->SizeX() / 3 != theSizeX
+   || myRaytraceOutputTexture[0]->SizeY() / 2 != theSizeY)
+  {
+    // Due to limitations of OpenGL image load-store extension
+    // atomic operations are supported only for single-channel
+    // images, so we define GL_R32F image. It is used as array
+    // of 6D floating point vectors:
+    // 0 - R color channel
+    // 1 - G color channel
+    // 2 - B color channel
+    // 3 - hit time transformed into OpenGL NDC space
+    // 4 - luminance accumulated for odd samples only
+    myRaytraceOutputTexture[0]->InitRectangle (theGlContext,
+      theSizeX * 3, theSizeY * 2, OpenGl_TextureFormat::Create<GLfloat, 1>());
+
+    myRaytraceVisualErrorTexture->Init (theGlContext,
+      GL_R32I, GL_RED_INTEGER, GL_INT, myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
+
+    myRaytraceTileOffsetsTexture->Init (theGlContext,
+      GL_RG32I, GL_RG_INTEGER, GL_INT, myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
+  }
+
+  if (myCamera->ProjectionType() == Graphic3d_Camera::Projection_Stereo)
+  {
+    if (myRaytraceOutputTexture[1]->SizeX() / 3 != theSizeX
+     || myRaytraceOutputTexture[1]->SizeY() / 2 != theSizeY)
+    {
+      myRaytraceOutputTexture[1]->InitRectangle (theGlContext,
+        theSizeX * 3, theSizeY * 2, OpenGl_TextureFormat::Create<GLfloat, 1>());
+    }
+  }
+  else
+  {
+    myRaytraceOutputTexture[1]->Release (theGlContext.operator->());
+  }
+
   return Standard_True;
 }
 
@@ -1711,14 +1814,14 @@ void OpenGl_View::updateCamera (const OpenGl_Mat4& theOrientation,
                                 const OpenGl_Mat4& theViewMapping,
                                 OpenGl_Vec3*       theOrigins,
                                 OpenGl_Vec3*       theDirects,
-                                OpenGl_Mat4&       theView,
+                                OpenGl_Mat4&       theViewPr,
                                 OpenGl_Mat4&       theUnview)
 {
   // compute view-projection matrix
-  theView = theViewMapping * theOrientation;
+  theViewPr = theViewMapping * theOrientation;
 
   // compute inverse view-projection matrix
-  theView.Inverted (theUnview);
+  theViewPr.Inverted (theUnview);
 
   Standard_Integer aOriginIndex = 0;
   Standard_Integer aDirectIndex = 0;
@@ -1797,7 +1900,7 @@ Standard_Boolean OpenGl_View::uploadRaytraceData (const Handle(OpenGl_Context)&
   /////////////////////////////////////////////////////////////////////////////
   // Create OpenGL BVH buffers
 
-  if (mySceneNodeInfoTexture.IsNull())  // create scene BVH buffers
+  if (mySceneNodeInfoTexture.IsNull()) // create scene BVH buffers
   {
     mySceneNodeInfoTexture  = new OpenGl_TextureBufferArb;
     mySceneMinPointTexture  = new OpenGl_TextureBufferArb;
@@ -1816,7 +1919,7 @@ Standard_Boolean OpenGl_View::uploadRaytraceData (const Handle(OpenGl_Context)&
     }
   }
 
-  if  (myGeometryVertexTexture.IsNull())  // create geometry buffers
+  if (myGeometryVertexTexture.IsNull()) // create geometry buffers
   {
     myGeometryVertexTexture = new OpenGl_TextureBufferArb;
     myGeometryNormalTexture = new OpenGl_TextureBufferArb;
@@ -2184,26 +2287,26 @@ Standard_Boolean OpenGl_View::updateRaytraceEnvironmentMap (const Handle(OpenGl_
     return aResult;
   }
 
+  Handle(OpenGl_ShaderProgram) aPrograms[] = { myRaytraceProgram,
+                                               myPostFSAAProgram };
+
   for (Standard_Integer anIdx = 0; anIdx < 2; ++anIdx)
   {
-    const Handle(OpenGl_ShaderProgram)& aProgram =
-      anIdx == 0 ? myRaytraceProgram : myPostFSAAProgram;
-
-    if (!aProgram.IsNull())
+    if (!aPrograms[anIdx].IsNull())
     {
-      aResult &= theGlContext->BindProgram (aProgram);
+      aResult &= theGlContext->BindProgram (aPrograms[anIdx]);
 
       if (!myTextureEnv.IsNull())
       {
         myTextureEnv->Bind (theGlContext,
           GL_TEXTURE0 + OpenGl_RT_EnvironmentMapTexture);
 
-        aResult &= aProgram->SetUniform (theGlContext,
+        aResult &= aPrograms[anIdx]->SetUniform (theGlContext,
           myUniformLocations[anIdx][OpenGl_RT_uSphereMapEnabled], 1);
       }
       else
       {
-        aResult &= aProgram->SetUniform (theGlContext,
+        aResult &= aPrograms[anIdx]->SetUniform (theGlContext,
           myUniformLocations[anIdx][OpenGl_RT_uSphereMapEnabled], 0);
       }
     }
@@ -2220,63 +2323,85 @@ Standard_Boolean OpenGl_View::updateRaytraceEnvironmentMap (const Handle(OpenGl_
 // function : setUniformState
 // purpose  : Sets uniform state for the given ray-tracing shader program
 // =======================================================================
-Standard_Boolean OpenGl_View::setUniformState (const OpenGl_Vec3*            theOrigins,
-                                               const OpenGl_Vec3*            theDirects,
-                                               const OpenGl_Mat4&            theViewMat,
-                                               const OpenGl_Mat4&            theUnviewMat,
-                                               const Standard_Integer        theProgramId,
+Standard_Boolean OpenGl_View::setUniformState (const Standard_Integer        theProgramId,
+                                               const Standard_Integer        theWinSizeX,
+                                               const Standard_Integer        theWinSizeY,
                                                const Handle(OpenGl_Context)& theGlContext)
 {
-  Handle(OpenGl_ShaderProgram)& theProgram =
-    theProgramId == 0 ? myRaytraceProgram : myPostFSAAProgram;
+  // Get projection state
+  OpenGl_MatrixState<Standard_ShortReal>& aCntxProjectionState = theGlContext->ProjectionState;
+
+  OpenGl_Mat4 aViewPrjMat;
+  OpenGl_Mat4 anUnviewMat;
+  OpenGl_Vec3 aOrigins[4];
+  OpenGl_Vec3 aDirects[4];
+
+  updateCamera (myCamera->OrientationMatrixF(),
+                aCntxProjectionState.Current(),
+                aOrigins,
+                aDirects,
+                aViewPrjMat,
+                anUnviewMat);
+
+  Handle(OpenGl_ShaderProgram)& theProgram = theProgramId == 0
+                                           ? myRaytraceProgram
+                                           : myPostFSAAProgram;
 
   if (theProgram.IsNull())
   {
     return Standard_False;
   }
 
-  const Standard_Integer aLightSourceBufferSize =
-    static_cast<Standard_Integer> (myRaytraceGeometry.Sources.size());
-
   // Set camera state
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uOriginLB], theOrigins[0]);
+    myUniformLocations[theProgramId][OpenGl_RT_uOriginLB], aOrigins[0]);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uOriginRB], theOrigins[1]);
+    myUniformLocations[theProgramId][OpenGl_RT_uOriginRB], aOrigins[1]);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uOriginLT], theOrigins[2]);
+    myUniformLocations[theProgramId][OpenGl_RT_uOriginLT], aOrigins[2]);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uOriginRT], theOrigins[3]);
+    myUniformLocations[theProgramId][OpenGl_RT_uOriginRT], aOrigins[3]);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uDirectLB], theDirects[0]);
+    myUniformLocations[theProgramId][OpenGl_RT_uDirectLB], aDirects[0]);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uDirectRB], theDirects[1]);
+    myUniformLocations[theProgramId][OpenGl_RT_uDirectRB], aDirects[1]);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uDirectLT], theDirects[2]);
+    myUniformLocations[theProgramId][OpenGl_RT_uDirectLT], aDirects[2]);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uDirectRT], theDirects[3]);
+    myUniformLocations[theProgramId][OpenGl_RT_uDirectRT], aDirects[3]);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uViewMat], theViewMat);
+    myUniformLocations[theProgramId][OpenGl_RT_uViewPrMat], aViewPrjMat);
   theProgram->SetUniform (theGlContext,
-    myUniformLocations[theProgramId][OpenGl_RT_uUnviewMat], theUnviewMat);
+    myUniformLocations[theProgramId][OpenGl_RT_uUnviewMat], anUnviewMat);
 
-  // Set scene parameters
+  // Set ray-tracing intersection parameters
   theProgram->SetUniform (theGlContext,
     myUniformLocations[theProgramId][OpenGl_RT_uSceneRad], myRaytraceSceneRadius);
   theProgram->SetUniform (theGlContext,
     myUniformLocations[theProgramId][OpenGl_RT_uSceneEps], myRaytraceSceneEpsilon);
+
+  const Standard_Integer aLightSourceBufferSize =
+    static_cast<Standard_Integer> (myRaytraceGeometry.Sources.size());
+
+  // Set ray-tracing light source parameters
   theProgram->SetUniform (theGlContext,
     myUniformLocations[theProgramId][OpenGl_RT_uLightCount], aLightSourceBufferSize);
   theProgram->SetUniform (theGlContext,
     myUniformLocations[theProgramId][OpenGl_RT_uLightAmbnt], myRaytraceGeometry.Ambient);
 
-  // Set run-time rendering options
+  // Enable/disable run time rendering effects
   theProgram->SetUniform (theGlContext,
     myUniformLocations[theProgramId][OpenGl_RT_uShadowsEnabled], myRenderParams.IsShadowEnabled ?  1 : 0);
   theProgram->SetUniform (theGlContext,
     myUniformLocations[theProgramId][OpenGl_RT_uReflectEnabled], myRenderParams.IsReflectionEnabled ?  1 : 0);
 
-  if (myRenderParams.IsGlobalIlluminationEnabled)
+  // Set screen dimensions
+  myRaytraceProgram->SetUniform (theGlContext,
+    myUniformLocations[theProgramId][OpenGl_RT_uWinSizeX], theWinSizeX);
+  myRaytraceProgram->SetUniform (theGlContext,
+    myUniformLocations[theProgramId][OpenGl_RT_uWinSizeY], theWinSizeY);
+
+  if (myRenderParams.IsGlobalIlluminationEnabled) // if Monte-Carlo sampling enabled
   {
     theProgram->SetUniform (theGlContext,
       myUniformLocations[theProgramId][OpenGl_RT_uBlockedRngEnabled], myRenderParams.CoherentPathTracingMode ?  1 : 0);
@@ -2288,12 +2413,11 @@ Standard_Boolean OpenGl_View::setUniformState (const OpenGl_Vec3*            the
     const std::vector<GLuint64>& aTextures = myRaytraceGeometry.TextureHandles();
 
     theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uTexSamplersArray],
-      static_cast<GLsizei> (aTextures.size()), (OpenGl_Vec2u* )&aTextures.front());
+      static_cast<GLsizei> (aTextures.size()), reinterpret_cast<const OpenGl_Vec2u*> (&aTextures.front()));
   }
 
   // Set background colors (only gradient background supported)
-  if (myBgGradientArray != NULL
-   && myBgGradientArray->IsDefined())
+  if (myBgGradientArray != NULL && myBgGradientArray->IsDefined())
   {
     theProgram->SetUniform (theGlContext,
       myUniformLocations[theProgramId][OpenGl_RT_uBackColorTop], myBgGradientArray->GradientColor (0));
@@ -2303,6 +2427,7 @@ Standard_Boolean OpenGl_View::setUniformState (const OpenGl_Vec3*            the
   else
   {
     const OpenGl_Vec4& aBackColor = myBgColor;
+
     theProgram->SetUniform (theGlContext,
       myUniformLocations[theProgramId][OpenGl_RT_uBackColorTop], aBackColor);
     theProgram->SetUniform (theGlContext,
@@ -2321,6 +2446,21 @@ Standard_Boolean OpenGl_View::setUniformState (const OpenGl_Vec3*            the
 // =======================================================================
 void OpenGl_View::bindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext)
 {
+  if (myRaytraceParameters.AdaptiveScreenSampling)
+  {
+  #if !defined(GL_ES_VERSION_2_0)
+    theGlContext->core42->glBindImageTexture (OpenGl_RT_OutputImageLft,
+      myRaytraceOutputTexture[0]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F);
+    theGlContext->core42->glBindImageTexture (OpenGl_RT_OutputImageRgh,
+      myRaytraceOutputTexture[1]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F);
+
+    theGlContext->core42->glBindImageTexture (OpenGl_RT_VisualErrorImage,
+      myRaytraceVisualErrorTexture->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I);
+    theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImage,
+      myRaytraceTileOffsetsTexture->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I);
+  #endif
+  }
+
   mySceneMinPointTexture->BindTexture    (theGlContext, GL_TEXTURE0 + OpenGl_RT_SceneMinPointTexture);
   mySceneMaxPointTexture->BindTexture    (theGlContext, GL_TEXTURE0 + OpenGl_RT_SceneMaxPointTexture);
   mySceneNodeInfoTexture->BindTexture    (theGlContext, GL_TEXTURE0 + OpenGl_RT_SceneNodeInfoTexture);
@@ -2331,12 +2471,6 @@ void OpenGl_View::bindRaytraceTextures (const Handle(OpenGl_Context)& theGlConte
   mySceneTransformTexture->BindTexture   (theGlContext, GL_TEXTURE0 + OpenGl_RT_SceneTransformTexture);
   myRaytraceMaterialTexture->BindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceMaterialTexture);
   myRaytraceLightSrcTexture->BindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceLightSrcTexture);
-
-  if (!myOpenGlFBO.IsNull())
-  {
-    myOpenGlFBO->ColorTexture()->Bind        (theGlContext, GL_TEXTURE0 + OpenGl_RT_OpenGlColorTexture);
-    myOpenGlFBO->DepthStencilTexture()->Bind (theGlContext, GL_TEXTURE0 + OpenGl_RT_OpenGlDepthTexture);
-  }
 }
 
 // =======================================================================
@@ -2356,12 +2490,6 @@ void OpenGl_View::unbindRaytraceTextures (const Handle(OpenGl_Context)& theGlCon
   myRaytraceMaterialTexture->UnbindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceMaterialTexture);
   myRaytraceLightSrcTexture->UnbindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceLightSrcTexture);
 
-  if (!myOpenGlFBO.IsNull())
-  {
-    myOpenGlFBO->ColorTexture()->Unbind        (theGlContext, GL_TEXTURE0 + OpenGl_RT_OpenGlColorTexture);
-    myOpenGlFBO->DepthStencilTexture()->Unbind (theGlContext, GL_TEXTURE0 + OpenGl_RT_OpenGlDepthTexture);
-  }
-
   theGlContext->core15fwd->glActiveTexture (GL_TEXTURE0);
 }
 
@@ -2371,81 +2499,70 @@ void OpenGl_View::unbindRaytraceTextures (const Handle(OpenGl_Context)& theGlCon
 // =======================================================================
 Standard_Boolean OpenGl_View::runRaytraceShaders (const Standard_Integer        theSizeX,
                                                   const Standard_Integer        theSizeY,
-                                                  const OpenGl_Vec3*            theOrigins,
-                                                  const OpenGl_Vec3*            theDirects,
-                                                  const OpenGl_Mat4&            theViewMat,
-                                                  const OpenGl_Mat4&            theUnviewMat,
                                                   Graphic3d_Camera::Projection  theProjection,
                                                   OpenGl_FrameBuffer*           theReadDrawFbo,
                                                   const Handle(OpenGl_Context)& theGlContext)
 {
-  bindRaytraceTextures (theGlContext);
-
-  Handle(OpenGl_FrameBuffer) aRenderFramebuffer;
-  Handle(OpenGl_FrameBuffer) aDepthSourceFramebuffer;
-  Handle(OpenGl_FrameBuffer) anAccumFramebuffer;
+  Standard_Boolean aResult = theGlContext->BindProgram (myRaytraceProgram);
 
-  // Choose proper set of framebuffers for stereo rendering
-  Standard_Boolean isStereo   = myCamera->ProjectionType() == Graphic3d_Camera::Projection_Stereo;
-  Standard_Boolean isRightEye = theProjection              == Graphic3d_Camera::Projection_MonoRightEye;
-  Standard_Integer aFBOIdx    = (isStereo && isRightEye) ? 1 : 0;
+  aResult &= setUniformState (0,
+                              theSizeX,
+                              theSizeY,
+                              theGlContext);
 
-  if (myRaytraceParameters.GlobalIllumination) // if path-tracing is used
+  if (myRaytraceParameters.GlobalIllumination) // path tracing
   {
-    aRenderFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx];
-    anAccumFramebuffer = myAccumFrames % 2 ? myRaytraceFBO2[aFBOIdx] : myRaytraceFBO1[aFBOIdx];
-    aDepthSourceFramebuffer = aRenderFramebuffer;
-
-    anAccumFramebuffer->ColorTexture()->Bind (
-      theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture);
-
-    aRenderFramebuffer->BindBuffer (theGlContext);
+    aResult &= runPathtrace (theProjection, theReadDrawFbo, theGlContext);
   }
-  else if (myRenderParams.IsAntialiasingEnabled) // if 2-pass ray-tracing is used
+  else // Whitted-style ray-tracing
   {
-    myRaytraceFBO1[aFBOIdx]->BindBuffer (theGlContext);
+    aResult &= runRaytrace (theSizeX, theSizeY, theProjection, theReadDrawFbo, theGlContext);
   }
 
-  Standard_Boolean aResult = theGlContext->BindProgram (myRaytraceProgram);
+  return aResult;
+}
 
-  aResult &= setUniformState (theOrigins,
-                              theDirects,
-                              theViewMat,
-                              theUnviewMat,
-                              0, // ID of RT program
-                              theGlContext);
+// =======================================================================
+// function : runRaytrace
+// purpose  : Runs Whitted-style ray-tracing
+// =======================================================================
+Standard_Boolean OpenGl_View::runRaytrace (const Standard_Integer        theSizeX,
+                                           const Standard_Integer        theSizeY,
+                                           Graphic3d_Camera::Projection  theProjection,
+                                           OpenGl_FrameBuffer*           theReadDrawFbo,
+                                           const Handle(OpenGl_Context)& theGlContext)
+{
+  Standard_Boolean aResult = Standard_True;
 
-  if (myRaytraceParameters.GlobalIllumination)
-  {
-    if (myAccumFrames == 0)
-    {
-      myRNG.SetSeed();
-    }
+  bindRaytraceTextures (theGlContext);
 
-    // Set frame accumulation weight
-    myRaytraceProgram->SetUniform (theGlContext,
-      myUniformLocations[0][OpenGl_RT_uSampleWeight], 1.f / (myAccumFrames + 1));
+  Handle(OpenGl_FrameBuffer) aRenderImageFramebuffer;
+  Handle(OpenGl_FrameBuffer) aDepthSourceFramebuffer;
 
-    // Set random number generator seed
-    myRaytraceProgram->SetUniform (theGlContext,
-      myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast<Standard_Integer> (myRNG.NextInt() >> 2));
+  // Choose proper set of frame buffers for stereo rendering
+  const Standard_Integer aFBOIdx (theProjection == Graphic3d_Camera::Projection_MonoRightEye);
+
+  if (myRenderParams.IsAntialiasingEnabled) // if second FSAA pass is used
+  {
+    myRaytraceFBO1[aFBOIdx]->BindBuffer (theGlContext);
+
+    glClear (GL_DEPTH_BUFFER_BIT); // render the image with depth
   }
 
   theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
 
-  if (myRenderParams.IsAntialiasingEnabled && !myRenderParams.IsGlobalIlluminationEnabled)
+  if (myRenderParams.IsAntialiasingEnabled)
   {
-    glDepthMask (GL_FALSE);
+    glDisable (GL_DEPTH_TEST); // improve jagged edges without depth buffer
 
+    // bind ray-tracing output image as input
     myRaytraceFBO1[aFBOIdx]->ColorTexture()->Bind (theGlContext, GL_TEXTURE0 + OpenGl_RT_FsaaInputTexture);
 
     aResult &= theGlContext->BindProgram (myPostFSAAProgram);
 
-    aResult &= setUniformState (theOrigins,
-                                theDirects,
-                                theViewMat,
-                                theUnviewMat,
-                                1, // ID of FSAA program
+    aResult &= setUniformState (1 /* FSAA ID */,
+                                theSizeX,
+                                theSizeY,
                                 theGlContext);
 
     // Perform multi-pass adaptive FSAA using ping-pong technique.
@@ -2480,49 +2597,48 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Standard_Integer
       aResult &= myPostFSAAProgram->SetUniform (theGlContext,
         myUniformLocations[1][OpenGl_RT_uOffsetY], aOffsetY);
 
-      Handle(OpenGl_FrameBuffer)& aFramebuffer = anIt % 2 ? myRaytraceFBO2[aFBOIdx] : myRaytraceFBO1[aFBOIdx];
+      Handle(OpenGl_FrameBuffer)& aFramebuffer = anIt % 2
+                                               ? myRaytraceFBO2[aFBOIdx]
+                                               : myRaytraceFBO1[aFBOIdx];
 
       aFramebuffer->BindBuffer (theGlContext);
 
+      // perform adaptive FSAA pass
       theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
 
       aFramebuffer->ColorTexture()->Bind (theGlContext, GL_TEXTURE0 + OpenGl_RT_FsaaInputTexture);
     }
 
-    aRenderFramebuffer = myRaytraceFBO2[aFBOIdx];
+    aRenderImageFramebuffer = myRaytraceFBO2[aFBOIdx];
     aDepthSourceFramebuffer = myRaytraceFBO1[aFBOIdx];
-  }
 
-  if (myRaytraceParameters.GlobalIllumination || myRenderParams.IsAntialiasingEnabled)
-  {
-    // Output accumulated image
-    glDepthMask (GL_TRUE);
+    glEnable (GL_DEPTH_TEST);
 
+    // Display filtered image
     theGlContext->BindProgram (myOutImageProgram);
 
-    myOutImageProgram->SetUniform (theGlContext, "uApplyGamma", static_cast<Standard_Integer> (myRaytraceParameters.GlobalIllumination));
-
     if (theReadDrawFbo != NULL)
     {
       theReadDrawFbo->BindBuffer (theGlContext);
     }
     else
     {
-      aRenderFramebuffer->UnbindBuffer (theGlContext);
+      aRenderImageFramebuffer->UnbindBuffer (theGlContext);
     }
 
-    aRenderFramebuffer->ColorTexture()->Bind (
+    aRenderImageFramebuffer->ColorTexture()->Bind (
       theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture);
 
     aDepthSourceFramebuffer->DepthStencilTexture()->Bind (
-      theGlContext, GL_TEXTURE0 + OpenGl_RT_DepthTexture);
+      theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceDepthTexture);
 
+    // copy the output image with depth values
     theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
 
     aDepthSourceFramebuffer->DepthStencilTexture()->Unbind (
-      theGlContext, GL_TEXTURE0 + OpenGl_RT_DepthTexture);
+      theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceDepthTexture);
 
-    aRenderFramebuffer->ColorTexture()->Unbind (
+    aRenderImageFramebuffer->ColorTexture()->Unbind (
       theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture);
   }
 
@@ -2534,6 +2650,140 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Standard_Integer
 }
 
 // =======================================================================
+// function : runPathtrace
+// purpose  : Runs path tracing shader
+// =======================================================================
+Standard_Boolean OpenGl_View::runPathtrace (const Graphic3d_Camera::Projection  theProjection,
+                                            OpenGl_FrameBuffer*                 theReadDrawFbo,
+                                            const Handle(OpenGl_Context)&       theGlContext)
+{
+  Standard_Boolean aResult = Standard_True;
+
+  if (myRaytraceParameters.AdaptiveScreenSampling)
+  {
+    if (myAccumFrames == 0)
+    {
+      myTileSampler.Reset(); // reset tile sampler to its initial state
+    }
+
+    // We upload tile offset texture each 4 frames in order
+    // to minimize overhead of additional memory bandwidth.
+    // Adaptive sampling is starting after first 10 frames.
+    if (myAccumFrames % 4 == 0)
+    {
+      myTileSampler.Upload (theGlContext, myRaytraceTileOffsetsTexture, myAccumFrames > 10);
+    }
+  }
+
+  bindRaytraceTextures (theGlContext);
+
+  Handle(OpenGl_FrameBuffer) aRenderImageFramebuffer;
+  Handle(OpenGl_FrameBuffer) aDepthSourceFramebuffer;
+  Handle(OpenGl_FrameBuffer) anAccumImageFramebuffer;
+
+  // Choose proper set of frame buffers for stereo rendering
+  const Standard_Integer aFBOIdx (theProjection == Graphic3d_Camera::Projection_MonoRightEye);
+
+  const Standard_Integer anImageId = (aFBOIdx != 0)
+                                   ? OpenGl_RT_OutputImageRgh
+                                   : OpenGl_RT_OutputImageLft;
+
+  aRenderImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx];
+  anAccumImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO2[aFBOIdx] : myRaytraceFBO1[aFBOIdx];
+
+  aDepthSourceFramebuffer = aRenderImageFramebuffer;
+
+  anAccumImageFramebuffer->ColorTexture()->Bind (
+    theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture);
+
+  aRenderImageFramebuffer->BindBuffer (theGlContext);
+
+  if (myAccumFrames == 0)
+  {
+    myRNG.SetSeed(); // start RNG from beginning
+  }
+
+  // Clear adaptive screen sampling images
+  if (myRaytraceParameters.AdaptiveScreenSampling)
+  {
+  #if !defined(GL_ES_VERSION_2_0)
+    if (myAccumFrames == 0)
+    {
+      theGlContext->core44->glClearTexImage (myRaytraceOutputTexture[aFBOIdx]->TextureId(), 0, GL_RED, GL_FLOAT, NULL);
+    }
+
+    theGlContext->core44->glClearTexImage (myRaytraceVisualErrorTexture->TextureId(), 0, GL_RED_INTEGER, GL_INT, NULL);
+  #endif
+  }
+
+  // Set frame accumulation weight
+  myRaytraceProgram->SetUniform (theGlContext,
+    myUniformLocations[0][OpenGl_RT_uSampleWeight], 1.f / (myAccumFrames + 1));
+
+  // Set random number generator seed
+  myRaytraceProgram->SetUniform (theGlContext,
+    myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast<Standard_Integer> (myRNG.NextInt() >> 2));
+
+  // Set image uniforms for render program
+  myRaytraceProgram->SetUniform (theGlContext,
+    myUniformLocations[0][OpenGl_RT_uRenderImage], anImageId);
+  myRaytraceProgram->SetUniform (theGlContext,
+    myUniformLocations[0][OpenGl_RT_uOffsetImage], OpenGl_RT_TileOffsetsImage);
+
+  glDisable (GL_DEPTH_TEST);
+
+  // Generate for the given RNG seed
+  theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
+
+  // Output accumulated path traced image
+  theGlContext->BindProgram (myOutImageProgram);
+
+  if (myRaytraceParameters.AdaptiveScreenSampling)
+  {
+    // Set uniforms for display program
+    myOutImageProgram->SetUniform (theGlContext, "uRenderImage",   anImageId);
+    myOutImageProgram->SetUniform (theGlContext, "uAccumFrames",   myAccumFrames);
+    myOutImageProgram->SetUniform (theGlContext, "uVarianceImage", OpenGl_RT_VisualErrorImage);
+    myOutImageProgram->SetUniform (theGlContext, "uDebugAdaptive", myRenderParams.ShowSamplingTiles ?  1 : 0);
+  }
+
+  if (theReadDrawFbo != NULL)
+  {
+    theReadDrawFbo->BindBuffer (theGlContext);
+  }
+  else
+  {
+    aRenderImageFramebuffer->UnbindBuffer (theGlContext);
+  }
+
+  aRenderImageFramebuffer->ColorTexture()->Bind (
+    theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture);
+
+  glEnable (GL_DEPTH_TEST);
+
+  // Copy accumulated image with correct depth values
+  theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
+
+  aRenderImageFramebuffer->ColorTexture()->Unbind (
+    theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture);
+
+  if (myRaytraceParameters.AdaptiveScreenSampling)
+  {
+    myRaytraceVisualErrorTexture->Bind (theGlContext);
+
+    // Download visual error map from the GPU and build
+    // adjusted tile offsets for optimal image sampling
+    myTileSampler.GrabVarianceMap (theGlContext);
+  }
+
+  unbindRaytraceTextures (theGlContext);
+
+  theGlContext->BindProgram (NULL);
+
+  return aResult;
+}
+
+// =======================================================================
 // function : raytrace
 // purpose  : Redraws the window using OpenGL/GLSL ray-tracing
 // =======================================================================
@@ -2558,35 +2808,17 @@ Standard_Boolean OpenGl_View::raytrace (const Standard_Integer        theSizeX,
     return Standard_False;
   }
 
-  // Get model-view and projection matrices
-  OpenGl_Mat4 aOrientationMatrix = myCamera->OrientationMatrixF();
-  OpenGl_Mat4 aViewMappingMatrix = theGlContext->ProjectionState.Current();
+  OpenGl_Mat4 aLightSourceMatrix;
 
-  OpenGl_Mat4 aInverOrientMatrix;
-  aOrientationMatrix.Inverted (aInverOrientMatrix);
-  if (!updateRaytraceLightSources (aInverOrientMatrix, theGlContext))
-  {
-    return Standard_False;
-  }
+  // Get inversed model-view matrix for transforming lights
+  myCamera->OrientationMatrixF().Inverted (aLightSourceMatrix);
 
-  OpenGl_Vec3 aOrigins[4];
-  OpenGl_Vec3 aDirects[4];
-  OpenGl_Mat4 aViewMat;
-  OpenGl_Mat4 anUnviewMat;
-
-  updateCamera (aOrientationMatrix,
-                aViewMappingMatrix,
-                aOrigins,
-                aDirects,
-                aViewMat,
-                anUnviewMat);
-
-  if (theReadDrawFbo != NULL)
+  if (!updateRaytraceLightSources (aLightSourceMatrix, theGlContext))
   {
-    theReadDrawFbo->BindBuffer (theGlContext);
+    return Standard_False;
   }
 
-  // Generate ray-traced image
+  // Generate image using Whitted-style ray-tracing or path tracing
   if (myIsRaytraceDataValid)
   {
     myRaytraceScreenQuad.BindVertexAttrib (theGlContext, Graphic3d_TOA_POS);
@@ -2597,29 +2829,13 @@ Standard_Boolean OpenGl_View::raytrace (const Standard_Integer        theSizeX,
         0, GL_DEBUG_SEVERITY_MEDIUM, "Error: Failed to acquire OpenGL image textures");
     }
 
-    // Remember the old depth function and mask
-    GLint aDepthFunc;
-    theGlContext->core11fwd->glGetIntegerv (GL_DEPTH_FUNC, &aDepthFunc);
-
-    GLboolean aDepthMask;
-    theGlContext->core11fwd->glGetBooleanv (GL_DEPTH_WRITEMASK, &aDepthMask);
-
     glDisable (GL_BLEND);
-    glDepthFunc (GL_ALWAYS);
-
-    Standard_Boolean aResult = runRaytraceShaders (theSizeX,
-                                                   theSizeY,
-                                                   aOrigins,
-                                                   aDirects,
-                                                   aViewMat,
-                                                   anUnviewMat,
-                                                   theProjection,
-                                                   theReadDrawFbo,
-                                                   theGlContext);
-
-    // Restore depth function and mask
-    glDepthFunc (aDepthFunc);
-    glDepthMask (aDepthMask);
+
+    const Standard_Boolean aResult = runRaytraceShaders (theSizeX,
+                                                         theSizeY,
+                                                         theProjection,
+                                                         theReadDrawFbo,
+                                                         theGlContext);
 
     if (!aResult)
     {
index dab7ad6..6b76f4f 100644 (file)
@@ -991,20 +991,13 @@ void OpenGl_View::renderStructs (Graphic3d_Camera::Projection theProjection,
         {
           if (theReadDrawFbo != NULL)
           {
-            theReadDrawFbo->BindReadBuffer (aCtx);
+            theReadDrawFbo->BindDrawBuffer (aCtx);
           }
           else
           {
-            aCtx->arbFBO->glBindFramebuffer (GL_READ_FRAMEBUFFER, 0);
+            aCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, 0);
           }
 
-          myOpenGlFBO->BindDrawBuffer (aCtx);
-
-          aCtx->arbFBOBlit->glBlitFramebuffer (0, 0, aSizeX, aSizeY,
-                                               0, 0, aSizeX, aSizeY,
-                                               GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
-                                               GL_NEAREST);
-
           // Render non-polygonal elements in default layer
           myZLayers.Render (myWorkspace, theToDrawImmediate, OpenGl_LF_Default);
         }
index d367df8..c4f9faa 100644 (file)
-//! Input image.
-uniform sampler2D uInputTexture;
+#ifdef ADAPTIVE_SAMPLING
 
-//! Ray tracing depth image.
-uniform sampler2D uDepthTexture;
+  #extension GL_ARB_shader_image_load_store : require
 
-//! Gamma correction flag.
-uniform int uApplyGamma;
+  //! OpenGL image used for accumulating rendering result.
+  volatile restrict layout(size1x32) uniform image2D uRenderImage;
+
+  //! OpenGL image storing variance of sampled pixels blocks.
+  volatile restrict layout(size1x32) uniform iimage2D uVarianceImage;
+
+#else // ADAPTIVE_SAMPLING
+
+  //! Input image.
+  uniform sampler2D uInputTexture;
+
+  //! Ray tracing depth image.
+  uniform sampler2D uDepthTexture;
+
+#endif // ADAPTIVE_SAMPLING
+
+//! Number of accumulated frames.
+uniform int uAccumFrames;
+
+//! Is debug mode enabled for importance screen sampling.
+uniform int uDebugAdaptive;
 
 //! Output pixel color.
 out vec4 OutColor;
 
+//! RGB weight factors to calculate luminance.
+#define LUMA vec3 (0.2126f, 0.7152f, 0.0722f)
+
+//! Scale factor used to quantize visual error.
+#define SCALE_FACTOR 1.0e6f
+
+// =======================================================================
+// function : main
+// purpose  :
+// =======================================================================
 void main (void)
 {
+#ifndef ADAPTIVE_SAMPLING
+
   vec4 aColor = texelFetch (uInputTexture, ivec2 (gl_FragCoord.xy), 0);
 
+#ifdef PATH_TRACING
+  float aDepth = aColor.w; // path tracing uses averaged depth
+#else
   float aDepth = texelFetch (uDepthTexture, ivec2 (gl_FragCoord.xy), 0).r;
+#endif
+
   gl_FragDepth = aDepth;
 
-  if (uApplyGamma == 1)
+#else // ADAPTIVE_SAMPLING
+
+  ivec2 aPixel = ivec2 (gl_FragCoord.xy);
+
+  vec4 aColor = vec4 (0.0);
+
+  // fetch accumulated color and total number of samples
+  aColor.x = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 0,
+                                             2 * aPixel.y + 0)).x;
+  aColor.y = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 1,
+                                             2 * aPixel.y + 0)).x;
+  aColor.z = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 1,
+                                             2 * aPixel.y + 1)).x;
+  aColor.w = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 0,
+                                             2 * aPixel.y + 1)).x;
+
+  // calculate normalization factor
+  float aSampleWeight = 1.f / max (1.0, aColor.w);
+
+  // calculate averaged depth value
+  gl_FragDepth = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 2,
+                                                 2 * aPixel.y + 1)).x * aSampleWeight;
+
+  // calculate averaged radiance for all samples and even samples only
+  float aHalfRad = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 2,
+                                                   2 * aPixel.y + 0)).x * aSampleWeight * 2.f;
+
+  float aAverRad = dot (aColor.rgb, LUMA) * aSampleWeight;
+
+  // apply our 'tone mapping' operator (gamma correction and clamping)
+  aHalfRad = min (1.f, sqrt (aHalfRad));
+  aAverRad = min (1.f, sqrt (aAverRad));
+
+  // calculate visual error
+  float anError = (aAverRad - aHalfRad) * (aAverRad - aHalfRad);
+
+  // accumulate visual error to current block
+  imageAtomicAdd (uVarianceImage, ivec2 (aPixel / vec2 (BLOCK_SIZE)), int (anError * SCALE_FACTOR));
+
+  if (uDebugAdaptive == 0) // normal rendering
   {
-    // apply gamma correction (we use gamma = 2)
-    OutColor = vec4 (sqrt (aColor.rgb), aColor.a);
+    aColor = vec4 (aColor.rgb * aSampleWeight, 1.0);
   }
-  else
+  else // showing number of samples
   {
-    OutColor = aColor;
+    aColor = vec4 (0.5f * aColor.rgb * aSampleWeight + vec3 (0.f, aColor.w / uAccumFrames * 0.35f, 0.f), 1.0);
   }
+
+#endif // ADAPTIVE_SAMPLING
+
+#ifdef PATH_TRACING
+
+   // apply gamma correction (we use gamma = 2)
+   OutColor = vec4 (sqrt (aColor.rgb), 0.f);
+
+#else // not PATH_TRACING
+
+   OutColor = aColor;
+
+#endif
 }
index a9c2e10..bf778ad 100644 (file)
@@ -512,15 +512,15 @@ float handleDirectLight (in vec3 theInput, in vec3 theToLight, in float theCosMa
 
 //=======================================================================
 // function : sampleLight
-// purpose  : general sampling function for directional and point lights
+// purpose  : General sampling function for directional and point lights
 //=======================================================================
-vec3 sampleLight (in vec3 theToLight, in bool isDirectional, in float theSmoothness, inout float thePDF)
+vec3 sampleLight (in vec3 theToLight, inout float theDistance, in bool isInfinite, in float theSmoothness, inout float thePDF)
 {
-  SLocalSpace aSpace = LocalSpace (theToLight);
+  SLocalSpace aSpace = LocalSpace (theToLight * (1.f / theDistance));
 
   // for point lights smoothness defines radius
-  float aCosMax = isDirectional ? theSmoothness :
-    inversesqrt (1.f + theSmoothness * theSmoothness / dot (theToLight, theToLight));
+  float aCosMax = isInfinite ? theSmoothness :
+    inversesqrt (1.f + theSmoothness * theSmoothness / (theDistance * theDistance));
 
   float aKsi1 = RandFloat();
   float aKsi2 = RandFloat();
@@ -613,7 +613,6 @@ vec3 intersectLight (in SRay theRay, in bool isViewRay, in int theBounce, in flo
 //=======================================================================
 vec4 PathTrace (in SRay theRay, in vec3 theInverse)
 {
-  float anOpenGlDepth = ComputeOpenGlDepth (theRay);
   float aRaytraceDepth = MAXFLOAT;
 
   vec3 aRadiance   = ZERO;
@@ -648,32 +647,22 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse)
                                    dot (aInvTransf1, aHit.Normal),
                                    dot (aInvTransf2, aHit.Normal)));
 
-    // For polygons that are parallel to the screen plane, the depth slope
-    // is equal to 1, resulting in small polygon offset. For polygons that
-    // that are at a large angle to the screen, the depth slope tends to 1,
-    // resulting in a larger polygon offset
-    float aPolygonOffset = uSceneEpsilon * EPS_SCALE /
-      max (abs (dot (theRay.Direct, aHit.Normal)), MIN_SLOPE);
-
-    if (anOpenGlDepth < aHit.Time + aPolygonOffset)
-    {
-      vec4 aSrcColorRGBA = ComputeOpenGlColor();
-
-      aRadiance   += aThroughput.xyz * aSrcColorRGBA.xyz;
-
-      aDepth = INVALID_BOUNCES; // terminate path
-    }
-
     theRay.Origin += theRay.Direct * aHit.Time; // get new intersection point
 
-    // Evaluate depth
+    // Evaluate depth on first hit
     if (aDepth == 0)
     {
+      // For polygons that are parallel to the screen plane, the depth slope
+      // is equal to 1, resulting in small polygon offset. For polygons that
+      // that are at a large angle to the screen, the depth slope tends to 1,
+      // resulting in a larger polygon offset
+      float aPolygonOffset = uSceneEpsilon * EPS_SCALE /
+        max (abs (dot (theRay.Direct, aHit.Normal)), MIN_SLOPE);
+
       // Hit point in NDC-space [-1,1] (the polygon offset is applied in the world space)
       vec4 aNDCPoint = uViewMat * vec4 (theRay.Origin + theRay.Direct * aPolygonOffset, 1.f);
-      aNDCPoint.xyz *= 1.f / aNDCPoint.w;
 
-      aRaytraceDepth = aNDCPoint.z * 0.5f + 0.5f;
+      aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w) * 0.5f + 0.5f;
     }
 
     // fetch material (BSDF)
@@ -701,7 +690,7 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse)
       vec3 aTexColor = textureLod (
         sampler2D (uTextureSamplers[int (aMaterial.Kd.w)]), aTexCoord.st, 0.f).rgb;
 
-      aMaterial.Kd.rgb *= aTexColor;
+      aMaterial.Kd.rgb *= aTexColor * aTexColor; // de-gamma correction (for gamma = 2)
     }
 #endif
 
@@ -732,8 +721,8 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse)
 
       float aPDF = 1.f / uLightCount, aDistance = length (aLight.xyz);
 
-      aLight.xyz = sampleLight (aLight.xyz * (1.f / aDistance),
-        aLight.w == 0.f /* is infinite */, aParam.w /* angle cosine */, aPDF);
+      aLight.xyz = sampleLight (aLight.xyz, aDistance,
+        aLight.w == 0.f /* is infinite */, aParam.w /* max cos or radius */, aPDF);
 
       vec3 aContrib = (1.f / aPDF) * aParam.rgb /* Le */ * handleMaterial (
           aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));
@@ -787,13 +776,11 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse)
       aHit.Normal * mix (-uSceneEpsilon, uSceneEpsilon, step (0.f, dot (aHit.Normal, anInput))), anInput);
 
     theInverse = InverseDirection (anInput);
-
-    anOpenGlDepth = MAXFLOAT; // disable combining image with OpenGL output
   }
 
   gl_FragDepth = aRaytraceDepth;
 
-  return vec4 (aRadiance, 0.f);
+  return vec4 (aRadiance, aRaytraceDepth);
 }
 
 #endif
index e7a3e3d..6c5f5e4 100644 (file)
@@ -1,3 +1,8 @@
+#ifdef ADAPTIVE_SAMPLING
+  #extension GL_ARB_shader_image_load_store : require
+  #extension GL_NV_shader_atomic_float : require
+#endif
+
 #ifdef USE_TEXTURES
   #extension GL_ARB_bindless_texture : require
 #endif
@@ -66,11 +71,6 @@ uniform samplerBuffer uRaytraceLightSrcTexture;
 //! Environment map texture.
 uniform sampler2D uEnvironmentMapTexture;
 
-//! Input pre-raytracing image rendered by OpenGL.
-uniform sampler2D uOpenGlColorTexture;
-//! Input pre-raytracing depth image rendered by OpenGL.
-uniform sampler2D uOpenGlDepthTexture;
-
 //! Total number of light sources.
 uniform int uLightCount;
 //! Intensity of global ambient light.
@@ -95,6 +95,14 @@ uniform float uSceneEpsilon;
   uniform uvec2 uTextureSamplers[MAX_TEX_NUMBER];
 #endif
 
+#ifdef ADAPTIVE_SAMPLING
+  //! OpenGL image used for accumulating rendering result.
+  volatile restrict layout(size1x32) uniform image2D  uRenderImage;
+
+  //! OpenGL image storing offsets of sampled pixels blocks.
+  coherent restrict layout(size2x32) uniform iimage2D uOffsetImage;
+#endif
+
 //! Top color of gradient background.
 uniform vec4 uBackColorTop = vec4 (0.0);
 //! Bottom color of gradient background.
@@ -240,7 +248,22 @@ vec3 InverseDirection (in vec3 theInput)
 //=======================================================================
 vec4 BackgroundColor()
 {
+#ifdef ADAPTIVE_SAMPLING
+
+  ivec2 aFragCoord = ivec2 (gl_FragCoord.xy);
+
+  ivec2 aTileXY = imageLoad (uOffsetImage, ivec2 (aFragCoord.x / BLOCK_SIZE,
+                                                  aFragCoord.y / BLOCK_SIZE)).xy;
+
+  aTileXY.y += aFragCoord.y % min (uWinSizeY - aTileXY.y, BLOCK_SIZE);
+
+  return mix (uBackColorBot, uBackColorTop, float (aTileXY.y) / uWinSizeY);
+
+#else
+
   return mix (uBackColorBot, uBackColorTop, vPixel.y);
+
+#endif
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
@@ -264,39 +287,6 @@ SRay GenerateRay (in vec2 thePixel)
 }
 
 // =======================================================================
-// function : ComputeOpenGlDepth
-// purpose  :
-// =======================================================================
-float ComputeOpenGlDepth (in SRay theRay)
-{
-  // a depth in range [0,1]
-  float anOpenGlDepth = texelFetch (uOpenGlDepthTexture, ivec2 (gl_FragCoord.xy), 0).r;
-  // pixel point in NDC-space [-1,1]
-  vec4 aPoint = vec4 (2.0f * vPixel.x - 1.0f,
-                      2.0f * vPixel.y - 1.0f,
-                      2.0f * anOpenGlDepth - 1.0f,
-                      1.0f);
-  vec4 aFinal = uUnviewMat * aPoint;
-  aFinal.xyz *= 1.f / aFinal.w;
-
-  return (anOpenGlDepth < 1.f) ? length (aFinal.xyz - theRay.Origin) : MAXFLOAT;
-}
-
-// =======================================================================
-// function : ComputeOpenGlColor
-// purpose  :
-// =======================================================================
-vec4 ComputeOpenGlColor()
-{
-  vec4 anOpenGlColor = texelFetch (uOpenGlColorTexture, ivec2 (gl_FragCoord.xy), 0);
-  // During blending with factors GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA (for text and markers)
-  // the alpha channel (written in the color buffer) was squared.
-  anOpenGlColor.a = 1.f - sqrt (anOpenGlColor.a);
-
-  return anOpenGlColor;
-}
-
-// =======================================================================
 // function : IntersectSphere
 // purpose  : Computes ray-sphere intersection
 // =======================================================================
@@ -725,7 +715,7 @@ float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance)
 #endif
       }
 
-      toContinue = (aHead >= 0);
+      toContinue = (aHead >= 0) && (aFactor > 0.1f);
 
       if (aHead == aStop) // go to top-level BVH
       {
@@ -876,7 +866,6 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse)
 
   int aTrsfId;
 
-  float anOpenGlDepth = ComputeOpenGlDepth (theRay);
   float aRaytraceDepth = MAXFLOAT;
 
   for (int aDepth = 0; aDepth < NB_BOUNCES; ++aDepth)
@@ -898,8 +887,7 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse)
       }
       else
       {
-        vec4 aGlColor = ComputeOpenGlColor();
-        aColor = vec4 (mix (aGlColor.rgb, BackgroundColor().rgb, aGlColor.w), aGlColor.w);
+        aColor = BackgroundColor();
       }
 
       aResult += aWeight.xyz * aColor.xyz; aWeight.w *= aColor.w;
@@ -915,31 +903,22 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse)
                                    dot (aInvTransf1, aHit.Normal),
                                    dot (aInvTransf2, aHit.Normal)));
 
-    // For polygons that are parallel to the screen plane, the depth slope
-    // is equal to 1, resulting in small polygon offset. For polygons that
-    // that are at a large angle to the screen, the depth slope tends to 1,
-    // resulting in a larger polygon offset
-    float aPolygonOffset = uSceneEpsilon * EPS_SCALE /
-      max (abs (dot (theRay.Direct, aHit.Normal)), MIN_SLOPE);
-
-    if (anOpenGlDepth < aHit.Time + aPolygonOffset)
-    {
-      vec4 aGlColor = ComputeOpenGlColor();
-
-      aResult += aWeight.xyz * aGlColor.xyz;
-      aWeight *= aGlColor.w;
-    }
-
     theRay.Origin += theRay.Direct * aHit.Time; // intersection point
 
-    // Evaluate depth
+    // Evaluate depth on first hit
     if (aDepth == 0)
     {
+      // For polygons that are parallel to the screen plane, the depth slope
+      // is equal to 1, resulting in small polygon offset. For polygons that
+      // that are at a large angle to the screen, the depth slope tends to 1,
+      // resulting in a larger polygon offset
+      float aPolygonOffset = uSceneEpsilon * EPS_SCALE /
+        max (abs (dot (theRay.Direct, aHit.Normal)), MIN_SLOPE);
+
       // Hit point in NDC-space [-1,1] (the polygon offset is applied in the world space)
       vec4 aNDCPoint = uViewMat * vec4 (theRay.Origin + theRay.Direct * aPolygonOffset, 1.f);
-      aNDCPoint.xyz *= 1.f / aNDCPoint.w;
 
-      aRaytraceDepth = aNDCPoint.z * 0.5f + 0.5f;
+      aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w) * 0.5f + 0.5f;
     }
 
     vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);
@@ -1042,10 +1021,6 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse)
       {
         theRay.Direct = Refract (theRay.Direct, aNormal, aOpacity.z, aOpacity.w);
       }
-      else
-      {
-        anOpenGlDepth -= aHit.Time + uSceneEpsilon;
-      }
     }
     else
     {
@@ -1074,8 +1049,6 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse)
       theInverse = 1.0f / max (abs (theRay.Direct), SMALL);
 
       theInverse = mix (-theInverse, theInverse, step (ZERO, theRay.Direct));
-
-      anOpenGlDepth = MAXFLOAT; // disable combining image with OpenGL output
     }
 
     theRay.Origin += theRay.Direct * uSceneEpsilon;
index d75e962..ae25d4e 100644 (file)
@@ -1,19 +1,24 @@
 out vec4 OutColor;
 
-// Seed for random number generator
+// Seed for random number generator (generated on CPU).
 uniform int uFrameRndSeed;
 
-// Weight of current frame related to accumulated frames.
-uniform float uSampleWeight;
+//! Enables/disables using of single RNG seed for 16x16 image
+//! blocks. Increases performance up to 4x, but the noise has
+//! become structured. Can be used fo final rendering.
+uniform int uBlockedRngEnabled;
 
-//! Input accumulated image.
-uniform sampler2D uAccumTexture;
+#ifndef ADAPTIVE_SAMPLING
+  //! Weight of current frame related to accumulated samples.
+  uniform float uSampleWeight;
 
-//! Enabled/disbales using of single RNG seed for image 16x16 blocks.
-//! Increases performance up to 4 times, but noise becomes structured.
-uniform int uBlockedRngEnabled;
+  //! Input accumulated image.
+  uniform sampler2D uAccumTexture;
+#endif
 
-#define MAX_RADIANCE vec3 (10.f)
+//! Maximum radiance that can be added to the pixel. Decreases noise
+//! level, but introduces some bias.
+#define MAX_RADIANCE vec3 (25.f)
 
 // =======================================================================
 // function : main
@@ -21,35 +26,82 @@ uniform int uBlockedRngEnabled;
 // =======================================================================
 void main (void)
 {
+  SeedRand (uFrameRndSeed, uWinSizeX, uBlockedRngEnabled == 0 ? 1 : 16);
+
 #ifndef PATH_TRACING
+
   SRay aRay = GenerateRay (vPixel);
+
 #else
-  ivec2 aWinSize = textureSize (uAccumTexture, 0);
 
-  SeedRand (uFrameRndSeed, aWinSize.x, uBlockedRngEnabled == 0 ? 1 : 8);
+  ivec2 aFragCoord = ivec2 (gl_FragCoord.xy);
 
-  SRay aRay = GenerateRay (vPixel +
-    vec2 (RandFloat() + 1.f, RandFloat() + 1.f) / vec2 (aWinSize));
-#endif
+#ifdef ADAPTIVE_SAMPLING
 
-  vec3 aInvDirect = 1.f / max (abs (aRay.Direct), SMALL);
+  ivec2 aTileXY = imageLoad (uOffsetImage, ivec2 (aFragCoord.x / BLOCK_SIZE,
+                                                  aFragCoord.y / BLOCK_SIZE)).xy;
 
-  aInvDirect = vec3 (aRay.Direct.x < 0.f ? -aInvDirect.x : aInvDirect.x,
-                     aRay.Direct.y < 0.f ? -aInvDirect.y : aInvDirect.y,
-                     aRay.Direct.z < 0.f ? -aInvDirect.z : aInvDirect.z);
+  ivec2 aRealBlockSize = ivec2 (min (uWinSizeX - aTileXY.x, BLOCK_SIZE),
+                                min (uWinSizeY - aTileXY.y, BLOCK_SIZE));
+
+  aFragCoord.x = aTileXY.x + (aFragCoord.x % aRealBlockSize.x);
+  aFragCoord.y = aTileXY.y + (aFragCoord.y % aRealBlockSize.y);
+
+#endif // ADAPTIVE_SAMPLING
+
+  vec2 aPnt = vec2 (aFragCoord.x + RandFloat(),
+                    aFragCoord.y + RandFloat());
+
+  SRay aRay = GenerateRay (aPnt / vec2 (uWinSizeX, uWinSizeY));
+
+#endif // PATH_TRACING
+
+  vec3 aInvDirect = InverseDirection (aRay.Direct);
 
 #ifdef PATH_TRACING
+
   vec4 aColor = PathTrace (aRay, aInvDirect);
 
-  if (any (isnan (aColor.xyz)))
+  if (any (isnan (aColor.rgb)))
   {
     aColor.rgb = ZERO;
   }
 
   aColor.rgb = min (aColor.rgb, MAX_RADIANCE);
 
+#ifdef ADAPTIVE_SAMPLING
+
+  // accumulate RGB color and depth
+  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0,
+                                       2 * aFragCoord.y + 0), aColor.r);
+  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 1,
+                                       2 * aFragCoord.y + 0), aColor.g);
+  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 1,
+                                       2 * aFragCoord.y + 1), aColor.b);
+  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2,
+                                       2 * aFragCoord.y + 1), aColor.w);
+
+  // accumulate number of samples
+  float aNbSamples = imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0,
+                                                          2 * aFragCoord.y + 1), 1.0);
+
+  if (int (aNbSamples) % 2 == 0) // accumulate luminance for even samples only
+  {
+    imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2,
+                                         2 * aFragCoord.y + 0), dot (LUMA, aColor.rgb));
+  }
+
+  discard; // fragment should not be written to frame buffer
+
+#else
+
   OutColor = mix (texture2D (uAccumTexture, vPixel), aColor, uSampleWeight);
+
+#endif // ADAPTIVE_SAMPLING
+
 #else
+
   OutColor = clamp (Radiance (aRay, aInvDirect), 0.f, 1.f);
-#endif
+
+#endif // PATH_TRACING
 }
\ No newline at end of file
index cc7a130..1aa1699 100644 (file)
@@ -8168,7 +8168,7 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       case Graphic3d_RM_RAYTRACING:    theDI << "raytrace ";      break;
     }
     theDI << "\n";
-    theDI << "msaa:         " <<  aParams.NbMsaaSamples << "\n";
+    theDI << "msaa:         " <<  aParams.NbMsaaSamples                               << "\n";
     theDI << "rayDepth:     " <<  aParams.RaytracingDepth                             << "\n";
     theDI << "fsaa:         " << (aParams.IsAntialiasingEnabled       ? "on" : "off") << "\n";
     theDI << "shadows:      " << (aParams.IsShadowEnabled             ? "on" : "off") << "\n";
@@ -8176,6 +8176,8 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
     theDI << "gleam:        " << (aParams.IsTransparentShadowEnabled  ? "on" : "off") << "\n";
     theDI << "GI:           " << (aParams.IsGlobalIlluminationEnabled ? "on" : "off") << "\n";
     theDI << "blocked RNG:  " << (aParams.CoherentPathTracingMode     ? "on" : "off") << "\n";
+    theDI << "iss:          " << (aParams.AdaptiveScreenSampling      ? "on" : "off") << "\n";
+    theDI << "iss debug:    " << (aParams.ShowSamplingTiles           ? "on" : "off") << "\n";
     theDI << "shadingModel: ";
     switch (aView->ShadingModel())
     {
@@ -8401,6 +8403,38 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       }
       aParams.CoherentPathTracingMode = toEnable;
     }
+    else if (aFlag == "-iss")
+    {
+      if (toPrint)
+      {
+        theDI << (aParams.AdaptiveScreenSampling ? "on" : "off") << " ";
+        continue;
+      }
+
+      Standard_Boolean toEnable = Standard_True;
+      if (++anArgIter < theArgNb
+        && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toEnable))
+      {
+        --anArgIter;
+      }
+      aParams.AdaptiveScreenSampling = toEnable;
+    }
+    else if (aFlag == "-issd")
+    {
+      if (toPrint)
+      {
+        theDI << (aParams.ShowSamplingTiles ? "on" : "off") << " ";
+        continue;
+      }
+
+      Standard_Boolean toEnable = Standard_True;
+      if (++anArgIter < theArgNb
+        && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toEnable))
+      {
+        --anArgIter;
+      }
+      aParams.ShowSamplingTiles = toEnable;
+    }
     else if (aFlag == "-env")
     {
       if (toPrint)
@@ -9500,6 +9534,8 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n      '-gi           on|off'  Enables/disables global illumination effects"
     "\n      '-brng         on|off'  Enables/disables blocked RNG (fast coherent PT)"
     "\n      '-env          on|off'  Enables/disables environment map background"
+    "\n      '-iss          on|off'  Enables/disables adaptive screen sampling (PT mode)"
+    "\n      '-issd         on|off'  Shows screen sampling distribution in ISS mode"
     "\n      '-shadingModel model'   Controls shading model from enumeration"
     "\n                              color, flat, gouraud, phong"
     "\n      '-resolution   value'   Sets a new pixels density (PPI), defines scaling factor for parameters like text size"
index 9311674..bb30320 100644 (file)
@@ -1,20 +1,18 @@
 puts "========"
 puts "OCC27083"
+puts "Visualization, Ray Tracing - shape with visible face boundaries disappears after turning the ray-tracing on"
 puts "========"
 puts ""
-##################################################################
-puts "Visualization, Ray Tracing - shape with visible face boundaries disappears after turning the ray-tracing on"
-##################################################################
 
 pload VISUALIZATION MODELING
 
 box b 1 1 1
 
-vinit
+vclear
+vinit View1
 
-vdisplay b
+vdisplay -noupdate -dispMode 1 b
 vfit
-vsetdispmode 1
 vshowfaceboundary b 1 255 0 0 3
 vraytrace 1
 
@@ -22,7 +20,7 @@ if {[vreadpixel 295 255 name] != "GOLDENROD4 0"} {
   puts "ERROR: the box with boundary aspect set is not shown in ray-tracing mode!"
 }
 
-if {[vreadpixel 105 58 name] != "RED 0"} {
+if {[vreadpixel 105 58 name] != "RED 1"} {
   puts "ERROR: the box's boundaries are not shown in ray-tracing mode!"
 }