0026536: Visualization - Ray-tracing engine: improving BVH traverse and fixing textur...
authordbp <dbp@opencascade.com>
Thu, 27 Aug 2015 09:46:22 +0000 (12:46 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 3 Sep 2015 11:49:24 +0000 (14:49 +0300)
Replace 64-bit handles of bindless textures by uvec2 type in GLSL code for compatibility with AMD drivers.
OpenGl_View::initProgram() - fix NULL-dereference.

src/BVH/BVH_Tree.hxx
src/OpenGl/OpenGl_SceneGeometry.cxx
src/OpenGl/OpenGl_SceneGeometry.hxx
src/OpenGl/OpenGl_ShaderProgram.cxx
src/OpenGl/OpenGl_ShaderProgram.hxx
src/OpenGl/OpenGl_View_2.cxx
src/OpenGl/OpenGl_View_Raytrace.cxx
src/Shaders/PathtraceBase.fs
src/Shaders/RaytraceBase.fs

index 5c323a9..25095a9 100644 (file)
@@ -144,7 +144,7 @@ public:
   //! Is node a leaf (outer)?
   Standard_Boolean IsOuter (const Standard_Integer theNodeIndex) const
   {
-    return BVH::Array<Standard_Integer, 4>::Value (myNodeInfoBuffer, theNodeIndex).x() > 0;
+    return BVH::Array<Standard_Integer, 4>::Value (myNodeInfoBuffer, theNodeIndex).x() != 0;
   }
 
   //! Sets node type to 'outer'.
index f1d8ec1..50d08d3 100755 (executable)
@@ -236,7 +236,7 @@ struct OpenGL_BVHParallelBuilder
 Standard_Boolean OpenGl_RaytraceGeometry::ProcessAcceleration()
 {
 #ifdef RAY_TRACE_PRINT_INFO
-    OSD_Timer aTimer;
+  OSD_Timer aTimer;
 #endif
 
   MarkDirty(); // force BVH rebuilding
@@ -261,6 +261,18 @@ Standard_Boolean OpenGl_RaytraceGeometry::ProcessAcceleration()
     Standard_ASSERT_RETURN (!aTriangleSet->BVH().IsNull(),
       "Error! Failed to update bottom-level BVH of OpenGL element", Standard_False);
 
+    NCollection_Handle<BVH_Tree<Standard_ShortReal, 3> > aBVH = aTriangleSet->BVH();
+
+    // correct data array of bottom-level BVH to set special flag for outer
+    // nodes in order to distinguish them from outer nodes of top-level BVH
+    for (Standard_Integer aNodeIdx = 0; aNodeIdx < aBVH->Length(); ++aNodeIdx)
+    {
+      if (aBVH->IsOuter (aNodeIdx))
+      {
+        aBVH->NodeInfoBuffer()[aNodeIdx].x() = -1;
+      }
+    }
+
     myBottomLevelTreeDepth = Max (myBottomLevelTreeDepth, aTriangleSet->BVH()->Depth());
   }
 
@@ -475,13 +487,23 @@ Standard_Boolean OpenGl_RaytraceGeometry::UpdateTextureHandles (const Handle(Ope
     return Standard_False;
   }
 
+  if (myTextureSampler.IsNull())
+  {
+    myTextureSampler = new OpenGl_Sampler();
+    myTextureSampler->Init (*theContext.operator->());
+    myTextureSampler->SetParameter (*theContext.operator->(), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    myTextureSampler->SetParameter (*theContext.operator->(), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    myTextureSampler->SetParameter (*theContext.operator->(), GL_TEXTURE_WRAP_S,     GL_REPEAT);
+    myTextureSampler->SetParameter (*theContext.operator->(), GL_TEXTURE_WRAP_T,     GL_REPEAT);
+  }
+
   myTextureHandles.clear();
 
 #if !defined(GL_ES_VERSION_2_0)
   for (Standard_Integer anIdx = 0; anIdx < myTextures.Size(); ++anIdx)
   {
-    const GLuint64 aHandle = theContext->arbTexBindless->glGetTextureHandleARB (
-      myTextures.Value (anIdx)->TextureId());
+    const GLuint64 aHandle = theContext->arbTexBindless->glGetTextureSamplerHandleARB (
+      myTextures.Value (anIdx)->TextureId(), myTextureSampler->SamplerID());
 
     if (glGetError() != GL_NO_ERROR)
     {
index 74393dd..ac1b188 100755 (executable)
@@ -22,6 +22,7 @@
 #include <NCollection_StdAllocator.hxx>
 #include <OpenGl_TextureBufferArb.hxx>
 #include <OpenGl_Texture.hxx>
+#include <OpenGl_Sampler.hxx>
 
 class  OpenGl_Element;
 struct OpenGl_ElementNode;
@@ -331,6 +332,16 @@ public: //! @name methods related to texture management
     return !myTextures.IsEmpty();
   }
 
+  //! Releases OpenGL resources.
+  void ReleaseResources (const Handle(OpenGl_Context)& theContext)
+  {
+    if (!myTextureSampler.IsNull())
+    {
+      myTextureSampler->Release (theContext.operator->());
+      myTextureSampler.Nullify();
+    }
+  }
+
 public: //! @name auxiliary methods
 
   //! Returns depth of high-level scene BVH from last build.
@@ -348,6 +359,7 @@ public: //! @name auxiliary methods
 protected:
 
   NCollection_Vector<Handle(OpenGl_Texture)> myTextures;             //!< Array of texture maps shared between rendered objects
+  Handle(OpenGl_Sampler)                     myTextureSampler;       //!< Sampler object providing fixed sampling params for texures
   std::vector<GLuint64>                      myTextureHandles;       //!< Array of unique 64-bit texture handles obtained from OpenGL
   Standard_Integer                           myHighLevelTreeDepth;   //!< Depth of high-level scene BVH from last build
   Standard_Integer                           myBottomLevelTreeDepth; //!< Maximum depth of bottom-level scene BVHs from last build
index 6718fa1..f3ff0f0 100755 (executable)
@@ -26,6 +26,7 @@
 #include <OpenGl_ShaderManager.hxx>
 #include <OpenGl_ArbTexBindless.hxx>
 
+#include <OpenGl_GlCore32.hxx>
 
 OpenGl_VariableSetterSelector OpenGl_ShaderProgram::mySetterSelector = OpenGl_VariableSetterSelector();
 
@@ -791,30 +792,30 @@ Standard_Boolean OpenGl_ShaderProgram::SetUniform (const Handle(OpenGl_Context)&
 
 // =======================================================================
 // function : SetUniform
-// purpose  : Specifies the value of the 64-bit unsigned uniform variable
+// purpose  :
 // =======================================================================
 Standard_Boolean OpenGl_ShaderProgram::SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                    const GLchar*                 theName,
-                                                   GLuint64                      theValue)
+                                                   const OpenGl_Vec2u&           theValue)
 {
   return SetUniform (theCtx, GetUniformLocation (theCtx, theName), theValue);
 }
 
 // =======================================================================
 // function : SetUniform
-// purpose  : Specifies the value of the 64-bit unsigned uniform variable
+// purpose  :
 // =======================================================================
 Standard_Boolean OpenGl_ShaderProgram::SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                    GLint                         theLocation,
-                                                   GLuint64                      theValue)
+                                                   const OpenGl_Vec2u&           theValue)
 {
-  if (theCtx->arbTexBindless == NULL || myProgramID == NO_PROGRAM || theLocation == INVALID_LOCATION)
+  if (theCtx->core32 == NULL || myProgramID == NO_PROGRAM || theLocation == INVALID_LOCATION)
   {
     return Standard_False;
   }
 
 #if !defined(GL_ES_VERSION_2_0)
-  theCtx->arbTexBindless->glUniformHandleui64ARB (theLocation, theValue);
+  theCtx->core32->glUniform2uiv (theLocation, 1, theValue.GetData());
 #endif
 
   return Standard_True;
@@ -822,32 +823,32 @@ Standard_Boolean OpenGl_ShaderProgram::SetUniform (const Handle(OpenGl_Context)&
 
 // =======================================================================
 // function : SetUniform
-// purpose  : Specifies the value of the 64-bit unsigned uniform array
+// purpose  :
 // =======================================================================
 Standard_Boolean OpenGl_ShaderProgram::SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                    const GLchar*                 theName,
                                                    const GLsizei                 theCount,
-                                                   const GLuint64*               theValue)
+                                                   const OpenGl_Vec2u*           theValue)
 {
   return SetUniform (theCtx, GetUniformLocation (theCtx, theName), theCount, theValue);
 }
 
 // =======================================================================
 // function : SetUniform
-// purpose  : Specifies the value of the 64-bit unsigned uniform array
+// purpose  :
 // =======================================================================
 Standard_Boolean OpenGl_ShaderProgram::SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                    GLint                         theLocation,
                                                    const GLsizei                 theCount,
-                                                   const GLuint64*               theValue)
+                                                   const OpenGl_Vec2u*           theValue)
 {
-  if (theCtx->arbTexBindless == NULL || myProgramID == NO_PROGRAM || theLocation == INVALID_LOCATION)
+  if (theCtx->core32 == NULL || myProgramID == NO_PROGRAM || theLocation == INVALID_LOCATION)
   {
     return Standard_False;
   }
 
 #if !defined(GL_ES_VERSION_2_0)
-  theCtx->arbTexBindless->glUniformHandleui64vARB (theLocation, theCount, theValue);
+  theCtx->core32->glUniform2uiv (theLocation, theCount, theValue->GetData());
 #endif
 
   return Standard_True;
index b8a4ee6..d49b13e 100755 (executable)
@@ -365,27 +365,27 @@ public:
 
 public:
 
-  //! Specifies the value of the 64-bit unsigned integer uniform variable.
+  //! Specifies the value of the unsigned integer uniform 2D vector (uvec2).
   Standard_EXPORT Standard_Boolean SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                const GLchar*                 theName,
-                                               GLuint64                      theValue);
+                                               const OpenGl_Vec2u&           theValue);
 
-  //! Specifies the value of the 64-bit unsigned integer uniform variable.
+  //! Specifies the value of the unsigned integer uniform 2D vector (uvec2).
   Standard_EXPORT Standard_Boolean SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                GLint                         theLocation,
-                                               GLuint64                      theValue);
+                                               const OpenGl_Vec2u&           theValue);
 
-  //! Specifies the value of the 64-bit unsigned integer uniform array.
+  //! Specifies the value of the uvec2 uniform array
   Standard_EXPORT Standard_Boolean SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                const GLchar*                 theName,
                                                const GLsizei                 theCount,
-                                               const GLuint64*               theValue);
+                                               const OpenGl_Vec2u*           theValue);
 
-  //! Specifies the value of the 64-bit unsigned integer uniform array.
+  //! Specifies the value of the uvec2 uniform array
   Standard_EXPORT Standard_Boolean SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                GLint                         theLocation,
                                                const GLsizei                 theCount,
-                                               const GLuint64*               theValue);
+                                               const OpenGl_Vec2u*           theValue);
 
 public:
 
index 73f874c..d68de33 100644 (file)
@@ -1089,7 +1089,10 @@ void OpenGl_View::RedrawScene (const Handle(OpenGl_PrinterContext)& thePrintCont
 
     case Visual3d_TOD_ENVIRONMENT:
       theWorkspace->NamedStatus |= OPENGL_NS_FORBIDSETTEX;
-      theWorkspace->EnableTexture (myTextureEnv);
+      if (theCView.RenderParams.Method != Graphic3d_RM_RAYTRACING)
+      {
+        theWorkspace->EnableTexture (myTextureEnv);
+      }
       // Render the view
       RenderStructs (theWorkspace, theReadDrawFbo, theCView, theToDrawImmediate);
       theWorkspace->DisableTexture();
@@ -1106,7 +1109,10 @@ void OpenGl_View::RedrawScene (const Handle(OpenGl_PrinterContext)& thePrintCont
       if (theWorkspace->NamedStatus & OPENGL_NS_2NDPASSNEED)
       {
         theWorkspace->NamedStatus |= OPENGL_NS_2NDPASSDO;
-        theWorkspace->EnableTexture (myTextureEnv);
+        if (theCView.RenderParams.Method != Graphic3d_RM_RAYTRACING)
+        {
+          theWorkspace->EnableTexture (myTextureEnv);
+        }
 
         // Remember OpenGl properties
         GLint aSaveBlendDst = GL_ONE_MINUS_SRC_ALPHA, aSaveBlendSrc = GL_SRC_ALPHA;
index 27c76a7..9cceefb 100644 (file)
@@ -1216,8 +1216,7 @@ Handle(OpenGl_ShaderProgram) OpenGl_View::initProgram (const Handle(OpenGl_Conte
   }
   else if (theGlContext->caps->glslWarnings)
   {
-    myRaytraceProgram->FetchInfoLog (theGlContext, aLinkLog);
-
+    aProgram->FetchInfoLog (theGlContext, aLinkLog);
     if (!aLinkLog.IsEmpty() && !aLinkLog.IsEqual ("No errors.\n"))
     {
       const TCollection_ExtendedString aMessage = TCollection_ExtendedString (
@@ -1658,6 +1657,8 @@ void OpenGl_View::releaseRaytraceResources (const Handle(OpenGl_Context)& theGlC
   nullifyResource (theGlContext, myRaytraceLightSrcTexture);
   nullifyResource (theGlContext, myRaytraceMaterialTexture);
 
+  myRaytraceGeometry.ReleaseResources (theGlContext);
+
   if (myRaytraceScreenQuad.IsValid())
     myRaytraceScreenQuad.Release (theGlContext.operator->());
 }
@@ -2257,8 +2258,10 @@ Standard_Boolean OpenGl_View::setUniformState (const Graphic3d_CView&        the
   // Set array of 64-bit texture handles
   if (theGlContext->arbTexBindless != NULL && myRaytraceGeometry.HasTextures())
   {
+    const std::vector<GLuint64>& aTextures = myRaytraceGeometry.TextureHandles();
+
     theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uTexSamplersArray],
-      static_cast<GLsizei> (myRaytraceGeometry.TextureHandles().size()), &myRaytraceGeometry.TextureHandles()[0]);
+      static_cast<GLsizei> (aTextures.size()), (OpenGl_Vec2u* )&aTextures.front());
   }
 
   // Set background colors (only gradient background supported)
index d7e799e..5d11762 100644 (file)
@@ -687,7 +687,7 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse)
                            dot (aTrsfRow2, aTexCoord));
 
       vec3 aTexColor = textureLod (
-        uTextureSamplers[int (aMaterial.Kd.w)], aTexCoord.st, 0.f).rgb;
+        sampler2D (uTextureSamplers[int (aMaterial.Kd.w)]), aTexCoord.st, 0.f).rgb;
 
       aMaterial.Kd.rgb *= aTexColor;
     }
index e5e765b..13f17b2 100644 (file)
@@ -89,7 +89,7 @@ uniform float uSceneEpsilon;
 
 #ifdef USE_TEXTURES
   //! Unique 64-bit handles of OpenGL textures.
-  uniform sampler2D uTextureSamplers[MAX_TEX_NUMBER];
+  uniform uvec2 uTextureSamplers[MAX_TEX_NUMBER];
 #endif
 
 //! Top color of gradient background.
@@ -201,9 +201,9 @@ vec3 MatrixColMultiplyPnt (in vec3 v,
                            in vec4 m2,
                            in vec4 m3)
 {
-  return vec3 (m0[0] * v.x + m1[0] * v.y + m2[0] * v.z + m3[0],
-               m0[1] * v.x + m1[1] * v.y + m2[1] * v.z + m3[1],
-               m0[2] * v.x + m1[2] * v.y + m2[2] * v.z + m3[2]);
+  return vec3 (m0.x * v.x + m1.x * v.y + m2.x * v.z + m3.x,
+               m0.y * v.x + m1.y * v.y + m2.y * v.z + m3.y,
+               m0.z * v.x + m1.z * v.y + m2.z * v.z + m3.z);
 }
 
 // =======================================================================
@@ -213,12 +213,11 @@ vec3 MatrixColMultiplyPnt (in vec3 v,
 vec3 MatrixColMultiplyDir (in vec3 v,
                            in vec4 m0,
                            in vec4 m1,
-                           in vec4 m2,
-                           in vec4 m3)
+                           in vec4 m2)
 {
-  return vec3 (m0[0] * v.x + m1[0] * v.y + m2[0] * v.z,
-               m0[1] * v.x + m1[1] * v.y + m2[1] * v.z,
-               m0[2] * v.x + m1[2] * v.y + m2[2] * v.z);
+  return vec3 (m0.x * v.x + m1.x * v.y + m2.x * v.z,
+               m0.y * v.x + m1.y * v.y + m2.y * v.z,
+               m0.z * v.x + m1.z * v.y + m2.z * v.z);
 }
 
 //=======================================================================
@@ -353,21 +352,53 @@ float IntersectTriangle (in SRay theRay,
 //! Global stack shared between traversal functions.
 int Stack[STACK_SIZE];
 
+#define MATERIAL_AMBN(index) (18 * index + 0)
+#define MATERIAL_DIFF(index) (18 * index + 1)
+#define MATERIAL_SPEC(index) (18 * index + 2)
+#define MATERIAL_EMIS(index) (18 * index + 3)
+#define MATERIAL_REFL(index) (18 * index + 4)
+#define MATERIAL_REFR(index) (18 * index + 5)
+#define MATERIAL_TRAN(index) (18 * index + 6)
+#define MATERIAL_TRS1(index) (18 * index + 7)
+#define MATERIAL_TRS2(index) (18 * index + 8)
+#define MATERIAL_TRS3(index) (18 * index + 9)
+
+struct SSubTree
+{
+  //! Transformed ray.
+  SRay  TrsfRay;
+
+  //! Inversed ray direction.
+  vec3  Inverse;
+
+  //! Parameters of sub-root node.
+  ivec4 SubData;
+};
+
+#define TRS_OFFSET(treelet) treelet.SubData.x
+#define BVH_OFFSET(treelet) treelet.SubData.y
+#define VRT_OFFSET(treelet) treelet.SubData.z
+#define TRG_OFFSET(treelet) treelet.SubData.w
+
+#define EMPTY_ROOT ivec4(0)
+
 // =======================================================================
-// function : ObjectNearestHit
-// purpose  : Finds intersection with nearest object triangle
+// function : SceneNearestHit
+// purpose  : Finds intersection with nearest scene triangle
 // =======================================================================
-ivec4 ObjectNearestHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffset,
-  in SRay theRay, in vec3 theInverse, inout SIntersect theHit, in int theSentinel)
+ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theTrsfId)
 {
-  int aHead = theSentinel;  // stack pointer
-  int aNode = theBVHOffset; // node to visit
-
   ivec4 aTriIndex = INALID_HIT;
 
+  int aNode =  0; // node to traverse
+  int aHead = -1; // pointer of stack
+  int aStop = -1; // BVH level switch
+
+  SSubTree aSubTree = SSubTree (theRay, theInverse, EMPTY_ROOT);
+
   for (bool toContinue = true; toContinue;)
   {
-    ivec3 aData = texelFetch (uSceneNodeInfoTexture, aNode).xyz;
+    ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
 
     if (aData.x == 0) // if inner node
     {
@@ -375,22 +406,22 @@ ivec4 ObjectNearestHit (in int theBVHOffset, in int theVrtOffset, in int theTrgO
       float aTimeLft;
       float aTimeRgh;
 
-      aData.y += theBVHOffset;
-      aData.z += theBVHOffset;
+      aData.y += BVH_OFFSET (aSubTree);
+      aData.z += BVH_OFFSET (aSubTree);
 
       vec3 aNodeMinLft = texelFetch (uSceneMinPointTexture, aData.y).xyz;
       vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz;
       vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
       vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz;
 
-      vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
-      vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
+      vec3 aTime0 = (aNodeMinLft - aSubTree.TrsfRay.Origin) * aSubTree.Inverse;
+      vec3 aTime1 = (aNodeMaxLft - aSubTree.TrsfRay.Origin) * aSubTree.Inverse;
 
       vec3 aTimeMax = max (aTime0, aTime1);
       vec3 aTimeMin = min (aTime0, aTime1);
 
-      aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
-      aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
+      aTime0 = (aNodeMinRgh - aSubTree.TrsfRay.Origin) * aSubTree.Inverse;
+      aTime1 = (aNodeMaxRgh - aSubTree.TrsfRay.Origin) * aSubTree.Inverse;
 
       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
@@ -405,88 +436,107 @@ ivec4 ObjectNearestHit (in int theBVHOffset, in int theVrtOffset, in int theTrgO
 
       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theHit.Time);
 
-      if (bool(aHitLft & aHitRgh))
+      aNode = (aHitLft != 0) ? aData.y : aData.z;
+
+      if (aHitLft + aHitRgh == 2) // hit both children
       {
         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
 
         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
       }
-      else
+      else if (aHitLft == aHitRgh) // no hit
       {
-        if (bool(aHitLft | aHitRgh))
+        toContinue = (aHead >= 0);
+
+        if (aHead == aStop) // go to top-level BVH
         {
-          aNode = bool(aHitLft) ? aData.y : aData.z;
+          aStop = -1; aSubTree = SSubTree (theRay, theInverse, EMPTY_ROOT);
         }
-        else
-        {
-          toContinue = (aHead != theSentinel);
 
-          if (toContinue)
-            aNode = Stack[aHead--];
-        }
+        aNode = Stack[abs (aHead)]; --aHead;
       }
     }
-    else // if leaf node
+    else if (aData.x < 0) // leaf node (containg triangles)
     {
       vec3 aNormal;
       vec2 aParams;
 
       for (int anIdx = aData.y; anIdx <= aData.z; ++anIdx)
       {
-        ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + theTrgOffset);
+        ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + TRG_OFFSET (aSubTree));
 
-        vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x += theVrtOffset).xyz;
-        vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y += theVrtOffset).xyz;
-        vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z += theVrtOffset).xyz;
+        vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x += VRT_OFFSET (aSubTree)).xyz;
+        vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y += VRT_OFFSET (aSubTree)).xyz;
+        vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z += VRT_OFFSET (aSubTree)).xyz;
 
-        float aTime = IntersectTriangle (theRay,
-                                         aPoint0,
-                                         aPoint1,
-                                         aPoint2,
-                                         aParams,
-                                         aNormal);
+        float aTime = IntersectTriangle (aSubTree.TrsfRay,
+          aPoint0, aPoint1, aPoint2, aParams, aNormal);
 
         if (aTime < theHit.Time)
         {
           aTriIndex = aTriangle;
 
+          theTrsfId = TRS_OFFSET (aSubTree);
+
           theHit = SIntersect (aTime, aParams, aNormal);
         }
       }
 
-      toContinue = (aHead != theSentinel);
+      toContinue = (aHead >= 0);
+
+      if (aHead == aStop) // go to top-level BVH
+      {
+        aStop = -1; aSubTree = SSubTree (theRay, theInverse, EMPTY_ROOT);
+      }
+
+      aNode = Stack[abs (aHead)]; --aHead;
+    }
+    else if (aData.x > 0) // switch node
+    {
+      aSubTree.SubData = ivec4 (4 * aData.x - 4, aData.yzw); // store BVH sub-root
+
+      vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 0);
+      vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 1);
+      vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 2);
+      vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 3);
+
+      aSubTree.TrsfRay.Direct = MatrixColMultiplyDir (theRay.Direct,
+                                                      aInvTransf0,
+                                                      aInvTransf1,
+                                                      aInvTransf2);
+
+      aSubTree.Inverse = mix (-UNIT, UNIT, step (ZERO, aSubTree.TrsfRay.Direct)) /
+        max (abs (aSubTree.TrsfRay.Direct), SMALL);
 
-      if (toContinue)
-        aNode = Stack[aHead--];
+      aSubTree.TrsfRay.Origin = MatrixColMultiplyPnt (theRay.Origin,
+                                                      aInvTransf0,
+                                                      aInvTransf1,
+                                                      aInvTransf2,
+                                                      aInvTransf3);
+
+      aNode = BVH_OFFSET (aSubTree); // go to sub-root node
+
+      aStop = aHead; // store current stack pointer
     }
   }
 
   return aTriIndex;
 }
 
-#define MATERIAL_AMBN(index) (18 * index + 0)
-#define MATERIAL_DIFF(index) (18 * index + 1)
-#define MATERIAL_SPEC(index) (18 * index + 2)
-#define MATERIAL_EMIS(index) (18 * index + 3)
-#define MATERIAL_REFL(index) (18 * index + 4)
-#define MATERIAL_REFR(index) (18 * index + 5)
-#define MATERIAL_TRAN(index) (18 * index + 6)
-#define MATERIAL_TRS1(index) (18 * index + 7)
-#define MATERIAL_TRS2(index) (18 * index + 8)
-#define MATERIAL_TRS3(index) (18 * index + 9)
-
 // =======================================================================
-// function : ObjectAnyHit
-// purpose  : Finds intersection with any object triangle
+// function : SceneAnyHit
+// purpose  : Finds intersection with any scene triangle
 // =======================================================================
-float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffset,
-  in SRay theRay, in vec3 theInverse, in float theDistance, in int theSentinel)
+float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance)
 {
-  int aHead = theSentinel;  // stack pointer
-  int aNode = theBVHOffset; // node to visit
-
   float aFactor = 1.f;
 
+  int aNode =  0; // node to traverse
+  int aHead = -1; // pointer of stack
+  int aStop = -1; // BVH level switch
+
+  SSubTree aSubTree = SSubTree (theRay, theInverse, EMPTY_ROOT);
+
   for (bool toContinue = true; toContinue;)
   {
     ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
@@ -497,22 +547,22 @@ float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffse
       float aTimeLft;
       float aTimeRgh;
 
-      aData.y += theBVHOffset;
-      aData.z += theBVHOffset;
+      aData.y += BVH_OFFSET (aSubTree);
+      aData.z += BVH_OFFSET (aSubTree);
 
       vec3 aNodeMinLft = texelFetch (uSceneMinPointTexture, aData.y).xyz;
-      vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
       vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz;
+      vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
       vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz;
 
-      vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
-      vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
+      vec3 aTime0 = (aNodeMinLft - aSubTree.TrsfRay.Origin) * aSubTree.Inverse;
+      vec3 aTime1 = (aNodeMaxLft - aSubTree.TrsfRay.Origin) * aSubTree.Inverse;
 
       vec3 aTimeMax = max (aTime0, aTime1);
       vec3 aTimeMin = min (aTime0, aTime1);
 
-      aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
-      aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
+      aTime0 = (aNodeMinRgh - aSubTree.TrsfRay.Origin) * aSubTree.Inverse;
+      aTime1 = (aNodeMaxRgh - aSubTree.TrsfRay.Origin) * aSubTree.Inverse;
 
       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
@@ -527,46 +577,41 @@ float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffse
 
       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theDistance);
 
-      if (bool(aHitLft & aHitRgh))
+      aNode = (aHitLft != 0) ? aData.y : aData.z;
+
+      if (aHitLft + aHitRgh == 2) // hit both children
       {
         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
 
         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
       }
-      else
+      else if (aHitLft == aHitRgh) // no hit
       {
-        if (bool(aHitLft | aHitRgh))
+        toContinue = (aHead >= 0);
+
+        if (aHead == aStop) // go to top-level BVH
         {
-          aNode = bool(aHitLft) ? aData.y : aData.z;
+          aStop = -1; aSubTree = SSubTree (theRay, theInverse, EMPTY_ROOT);
         }
-        else
-        {
-          toContinue = (aHead != theSentinel);
 
-          if (toContinue)
-            aNode = Stack[aHead--];
-        }
+        aNode = Stack[abs (aHead)]; --aHead;
       }
     }
-    else // if leaf node
+    else if (aData.x < 0) // leaf node
     {
       vec3 aNormal;
       vec2 aParams;
 
       for (int anIdx = aData.y; anIdx <= aData.z; ++anIdx)
       {
-        ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + theTrgOffset);
+        ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + TRG_OFFSET (aSubTree));
 
-        vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x + theVrtOffset).xyz;
-        vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y + theVrtOffset).xyz;
-        vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z + theVrtOffset).xyz;
+        vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x += VRT_OFFSET (aSubTree)).xyz;
+        vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y += VRT_OFFSET (aSubTree)).xyz;
+        vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z += VRT_OFFSET (aSubTree)).xyz;
 
-        float aTime = IntersectTriangle (theRay,
-                                         aPoint0,
-                                         aPoint1,
-                                         aPoint2,
-                                         aParams,
-                                         aNormal);
+        float aTime = IntersectTriangle (aSubTree.TrsfRay,
+          aPoint0, aPoint1, aPoint2, aParams, aNormal);
 
 #ifdef TRANSPARENT_SHADOWS
         if (aTime < theDistance)
@@ -581,238 +626,40 @@ float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffse
 #endif
       }
 
-      toContinue = (aHead != theSentinel) && (aFactor > 0.1f);
-
-      if (toContinue)
-        aNode = Stack[aHead--];
-    }
-  }
-
-  return aFactor;
-}
-
-// =======================================================================
-// function : SceneNearestHit
-// purpose  : Finds intersection with nearest scene triangle
-// =======================================================================
-ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theTrsfId)
-{
-  int aHead = -1; // stack pointer
-  int aNode =  0; // node to visit
-
-  ivec4 aHitObject = INALID_HIT;
-
-  for (bool toContinue = true; toContinue;)
-  {
-    ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
-
-    if (aData.x != 0) // if leaf node
-    {
-      vec3 aNodeMin = texelFetch (uSceneMinPointTexture, aNode).xyz;
-      vec3 aNodeMax = texelFetch (uSceneMaxPointTexture, aNode).xyz;
-
-      vec3 aTime0 = (aNodeMin - theRay.Origin) * theInverse;
-      vec3 aTime1 = (aNodeMax - theRay.Origin) * theInverse;
-
-      vec3 aTimes = min (aTime0, aTime1);
+      toContinue = (aHead >= 0) && (aFactor > 0.1f);
 
-      if (max (aTimes.x, max (aTimes.y, aTimes.z)) < theHit.Time)
+      if (aHead == aStop) // go to top-level BVH
       {
-        // fetch object transformation
-        int aTrsfId = (aData.x - 1) * 4;
-
-        vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, aTrsfId + 0);
-        vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, aTrsfId + 1);
-        vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, aTrsfId + 2);
-        vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, aTrsfId + 3);
-
-        SRay aTrsfRay = SRay (
-          MatrixColMultiplyPnt (theRay.Origin, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3),
-          MatrixColMultiplyDir (theRay.Direct, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3));
-
-        vec3 aTrsfInverse = 1.0f / max (abs (aTrsfRay.Direct), SMALL);
-
-        aTrsfInverse = mix (-aTrsfInverse, aTrsfInverse, step (ZERO, aTrsfRay.Direct));
-
-        ivec4 aTriIndex = ObjectNearestHit (
-          aData.y, aData.z, aData.w, aTrsfRay, aTrsfInverse, theHit, aHead);
-
-        if (aTriIndex.x != -1)
-        {
-          aHitObject = ivec4 (aTriIndex.x,  // vertex 0
-                              aTriIndex.y,  // vertex 1
-                              aTriIndex.z,  // vertex 2
-                              aTriIndex.w); // material
-
-          theTrsfId = aTrsfId;
-        }
+        aStop = -1; aSubTree = SSubTree (theRay, theInverse, EMPTY_ROOT);
       }
 
-      toContinue = aHead >= 0;
-
-      if (toContinue)
-        aNode = Stack[aHead--];
+      aNode = Stack[abs (aHead)]; --aHead;
     }
-    else // if inner node
+    else if (aData.x > 0) // switch node
     {
-      float aTimeOut;
-      float aTimeLft;
-      float aTimeRgh;
+      aSubTree.SubData = ivec4 (4 * aData.x - 4, aData.yzw); // store BVH sub-root
 
-      vec3 aNodeMinLft = texelFetch (uSceneMinPointTexture, aData.y).xyz;
-      vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
-      vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz;
-      vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz;
-
-      vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
-      vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
-
-      vec3 aTimeMax = max (aTime0, aTime1);
-      vec3 aTimeMin = min (aTime0, aTime1);
-
-      aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
-      aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
-
-      int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeLft <= theHit.Time);
-
-      aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
-      aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
-
-      aTimeMax = max (aTime0, aTime1);
-      aTimeMin = min (aTime0, aTime1);
-
-      aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
-      aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
-
-      int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theHit.Time);
-
-      if (bool(aHitLft & aHitRgh))
-      {
-        aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
-
-        Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
-      }
-      else
-      {
-        if (bool(aHitLft | aHitRgh))
-        {
-          aNode = bool(aHitLft) ? aData.y : aData.z;
-        }
-        else
-        {
-          toContinue = aHead >= 0;
-
-          if (toContinue)
-            aNode = Stack[aHead--];
-        }
-      }
-    }
-  }
-
-  return aHitObject;
-}
+      vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 0);
+      vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 1);
+      vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 2);
+      vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, TRS_OFFSET (aSubTree) + 3);
 
-// =======================================================================
-// function : SceneAnyHit
-// purpose  : Finds intersection with any scene triangle
-// =======================================================================
-float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance)
-{
-  int aHead = -1; // stack pointer
-  int aNode =  0; // node to visit
+      aSubTree.TrsfRay.Direct = MatrixColMultiplyDir (theRay.Direct,
+                                                      aInvTransf0,
+                                                      aInvTransf1,
+                                                      aInvTransf2);
 
-  float aFactor = 1.f;
-
-  for (bool toContinue = true; toContinue;)
-  {
-    ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
+      aSubTree.TrsfRay.Origin = MatrixColMultiplyPnt (theRay.Origin,
+                                                      aInvTransf0,
+                                                      aInvTransf1,
+                                                      aInvTransf2,
+                                                      aInvTransf3);
 
-    if (aData.x != 0) // if leaf node
-    {
-      // fetch object transformation
-      int aTrsfId = (aData.x - 1) * 4;
+      aSubTree.Inverse = mix (-UNIT, UNIT, step (ZERO, aSubTree.TrsfRay.Direct)) / max (abs (aSubTree.TrsfRay.Direct), SMALL);
 
-      vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, aTrsfId + 0);
-      vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, aTrsfId + 1);
-      vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, aTrsfId + 2);
-      vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, aTrsfId + 3);
+      aNode = BVH_OFFSET (aSubTree); // go to sub-root node
 
-      SRay aTrsfRay = SRay (
-        MatrixColMultiplyPnt (theRay.Origin, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3),
-        MatrixColMultiplyDir (theRay.Direct, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3));
-
-      vec3 aTrsfInverse = 1.0f / max (abs (aTrsfRay.Direct), SMALL);
-
-      aTrsfInverse = mix (-aTrsfInverse, aTrsfInverse, step (ZERO, aTrsfRay.Direct));
-
-#ifdef TRANSPARENT_SHADOWS
-      aFactor *= ObjectAnyHit (
-        aData.y, aData.z, aData.w, aTrsfRay, aTrsfInverse, theDistance, aHead);
-
-      toContinue = aHead >= 0 && aFactor >= 0.1f;
-#else
-      aFactor = ObjectAnyHit (
-        aData.y, aData.z, aData.w, aTrsfRay, aTrsfInverse, theDistance, aHead);
-
-      toContinue = aHead >= 0 && aFactor != 0.0f;
-#endif
-
-      if (toContinue)
-        aNode = Stack[aHead--];
-    }
-    else // if inner node
-    {
-      float aTimeOut;
-      float aTimeLft;
-      float aTimeRgh;
-
-      vec3 aNodeMinLft = texelFetch (uSceneMinPointTexture, aData.y).xyz;
-      vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
-      vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz;
-      vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz;
-
-      vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
-      vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
-
-      vec3 aTimeMax = max (aTime0, aTime1);
-      vec3 aTimeMin = min (aTime0, aTime1);
-
-      aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
-      aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
-
-      int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeLft <= theDistance);
-
-      aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
-      aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
-
-      aTimeMax = max (aTime0, aTime1);
-      aTimeMin = min (aTime0, aTime1);
-
-      aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
-      aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
-
-      int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theDistance);
-
-      if (bool(aHitLft & aHitRgh))
-      {
-        aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
-
-        Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
-      }
-      else
-      {
-        if (bool(aHitLft | aHitRgh))
-        {
-          aNode = bool(aHitLft) ? aData.y : aData.z;
-        }
-        else
-        {
-          toContinue = aHead >= 0;
-
-          if (toContinue)
-            aNode = Stack[aHead--];
-        }
-      }
+      aStop = aHead; // store current stack pointer
     }
   }
 
@@ -1013,7 +860,7 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse)
                            dot (aTrsfRow2, aTexCoord));
 
       vec3 aTexColor = textureLod (
-        uTextureSamplers[int(aDiffuse.w)], aTexCoord.st, 0.f).rgb;
+        sampler2D (uTextureSamplers[int(aDiffuse.w)]), aTexCoord.st, 0.f).rgb;
 
       aDiffuse.rgb *= aTexColor;
       aAmbient.rgb *= aTexColor;