0032039: Visualization, TKOpenGl - implement simple shadow mapping for a direct light...
authorkgv <kgv@opencascade.com>
Mon, 4 Jan 2021 09:17:44 +0000 (12:17 +0300)
committerbugmaster <bugmaster@opencascade.com>
Wed, 13 Jan 2021 14:10:26 +0000 (17:10 +0300)
Graphic3d_CLight::ToCastShadows() - added new property defining if light should cast shadows (ignored by Ray-Tracing).

OpenGl_ShaderManager::stdComputeLighting() now implements shadow mapping for directional lights.
OpenGl_ShaderManager::prepareGeomMainSrc() now handles copying of arrays.
OpenGl_Context::ShadowMapTexUnit() - added property defining an offset for shadow map texture units.
OpenGl_ShadowMap - added new class storing shadow map FBO with parameters.
OpenGl_View::prepareFrameBuffers() - added resizing of shadow map FBOs.
OpenGl_View::Redraw() - added section redrawing scene into shadow map FBOs via OpenGl_View::renderShadowMap() method.

vrenderparams - added new parameters -shadowMapResolution and -shadowMapBias.

33 files changed:
src/Graphic3d/Graphic3d_CLight.cxx
src/Graphic3d/Graphic3d_CLight.hxx
src/Graphic3d/Graphic3d_LightSet.cxx
src/Graphic3d/Graphic3d_LightSet.hxx
src/Graphic3d/Graphic3d_RenderingParams.hxx
src/Graphic3d/Graphic3d_ShaderProgram.cxx
src/Graphic3d/Graphic3d_ShaderProgram.hxx
src/Graphic3d/Graphic3d_TextureUnit.hxx
src/OpenGl/FILES
src/OpenGl/OpenGl_Context.cxx
src/OpenGl/OpenGl_Context.hxx
src/OpenGl/OpenGl_FrameBuffer.hxx
src/OpenGl/OpenGl_FrameStats.cxx
src/OpenGl/OpenGl_LayerList.cxx
src/OpenGl/OpenGl_ShaderManager.cxx
src/OpenGl/OpenGl_ShaderManager.hxx
src/OpenGl/OpenGl_ShaderProgram.cxx
src/OpenGl/OpenGl_ShaderProgram.hxx
src/OpenGl/OpenGl_ShaderStates.hxx
src/OpenGl/OpenGl_ShadowMap.cxx [new file with mode: 0644]
src/OpenGl/OpenGl_ShadowMap.hxx [new file with mode: 0644]
src/OpenGl/OpenGl_Structure.cxx
src/OpenGl/OpenGl_View.cxx
src/OpenGl/OpenGl_View.hxx
src/Shaders/DirectionalLightShadow.glsl [new file with mode: 0644]
src/Shaders/FILES
src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx [new file with mode: 0644]
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/v3d/grids.list
tests/v3d/shadows/buggy [new file with mode: 0644]
tests/v3d/shadows/dir1 [new file with mode: 0644]
tests/v3d/shadows/dir2 [new file with mode: 0644]
tests/v3d/shadows/dirhead [new file with mode: 0644]

index b46de00..182cbe0 100644 (file)
@@ -14,6 +14,7 @@
 #include <Graphic3d_CLight.hxx>
 
 #include <Standard_Atomic.hxx>
+#include <Standard_NotImplemented.hxx>
 #include <Standard_OutOfRange.hxx>
 
 IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_CLight, Standard_Transient)
@@ -56,7 +57,8 @@ Graphic3d_CLight::Graphic3d_CLight (Graphic3d_TypeOfLightSource theType)
   myType       (theType),
   myRevision   (0),
   myIsHeadlight(false),
-  myIsEnabled  (true)
+  myIsEnabled  (true),
+  myToCastShadows (false)
 {
   switch (theType)
   {
@@ -108,6 +110,20 @@ void Graphic3d_CLight::SetEnabled (Standard_Boolean theIsOn)
   myIsEnabled = theIsOn;
 }
 
+// =======================================================================
+// function : SetCastShadows
+// purpose  :
+// =======================================================================
+void Graphic3d_CLight::SetCastShadows (Standard_Boolean theToCast)
+{
+  if (myType != Graphic3d_TOLS_DIRECTIONAL)
+  {
+    throw Standard_NotImplemented ("Graphic3d_CLight::SetCastShadows() is not implemented for this light type");
+  }
+  updateRevisionIf (myToCastShadows != theToCast);
+  myToCastShadows = theToCast;
+}
+
 // =======================================================================
 // function : SetHeadlight
 // purpose  :
index b2218ff..12fbd73 100644 (file)
@@ -58,6 +58,13 @@ public:
   //! instead it turns it OFF so that it just have no effect.
   Standard_EXPORT void SetEnabled (Standard_Boolean theIsOn);
 
+  //! Return TRUE if shadow casting is enabled; FALSE by default.
+  //! Has no effect in Ray-Tracing rendering mode.
+  Standard_Boolean ToCastShadows() const { return myToCastShadows; }
+
+  //! Enable/disable shadow casting.
+  Standard_EXPORT void SetCastShadows (Standard_Boolean theToCast);
+
   //! Returns true if the light is a headlight; FALSE by default.
   //! Headlight flag means that light position/direction are defined not in a World coordinate system, but relative to the camera orientation.
   Standard_Boolean IsHeadlight() const { return myIsHeadlight; }
@@ -258,6 +265,7 @@ protected:
   Standard_Size                     myRevision;    //!< modification counter
   Standard_Boolean                  myIsHeadlight; //!< flag to mark head light
   Standard_Boolean                  myIsEnabled;   //!< enabled state
+  Standard_Boolean                  myToCastShadows;//!< casting shadows is requested
 
 };
 
index 0b51b70..61dcca9 100644 (file)
@@ -36,6 +36,7 @@ namespace
 Graphic3d_LightSet::Graphic3d_LightSet()
 : myAmbient (0.0f, 0.0f, 0.0f, 0.0f),
   myNbEnabled (0),
+  myNbCastShadows (0),
   myRevision (1),
   myCacheRevision (0)
 {
@@ -110,6 +111,7 @@ Standard_Size Graphic3d_LightSet::UpdateRevision()
   }
 
   myCacheRevision = myRevision;
+  myNbCastShadows = 0;
   myAmbient.SetValues (0.0f, 0.0f, 0.0f, 0.0f);
   memset (myLightTypesEnabled, 0, sizeof(myLightTypesEnabled));
   NCollection_LocalArray<char, 32> aKeyLong (myLights.Extent() + 1);
@@ -130,7 +132,15 @@ Standard_Size Graphic3d_LightSet::UpdateRevision()
     }
     else
     {
-      aKeyLong[aLightLast++] = THE_LIGHT_KEY_LETTERS[aLight->Type()];
+      if (aLight->ToCastShadows())
+      {
+        ++myNbCastShadows;
+        aKeyLong[aLightLast++] = UpperCase (THE_LIGHT_KEY_LETTERS[aLight->Type()]);
+      }
+      else
+      {
+        aKeyLong[aLightLast++] = THE_LIGHT_KEY_LETTERS[aLight->Type()];
+      }
     }
   }
   aKeyLong[aLightLast] = '\0';
index 418cdcc..32be557 100644 (file)
@@ -29,7 +29,9 @@ public:
     IterationFilter_None            = 0x0000, //!< no filter
     IterationFilter_ExcludeAmbient  = 0x0002, //!< exclude ambient  light sources
     IterationFilter_ExcludeDisabled = 0x0004, //!< exclude disabled light sources
+    IterationFilter_ExcludeNoShadow = 0x0008, //!< exclude light sources not casting shadow
     IterationFilter_ExcludeDisabledAndAmbient = IterationFilter_ExcludeAmbient | IterationFilter_ExcludeDisabled,
+    IterationFilter_ActiveShadowCasters = IterationFilter_ExcludeDisabledAndAmbient | IterationFilter_ExcludeNoShadow,
   };
 
   //! Iterator through light sources.
@@ -95,6 +97,11 @@ public:
         {
           continue;
         }
+        else if ((myFilter & IterationFilter_ExcludeNoShadow) != 0
+              && !myIter.Key()->ToCastShadows())
+        {
+          continue;
+        }
 
         break;
       }
@@ -155,6 +162,10 @@ public:
   //! @sa UpdateRevision()
   Standard_Integer NbEnabledLightsOfType (Graphic3d_TypeOfLightSource theType) const { return myLightTypesEnabled[theType]; }
 
+  //! Returns total amount of enabled lights castings shadows.
+  //! @sa UpdateRevision()
+  Standard_Integer NbCastShadows() const { return myNbCastShadows; }
+
   //! Returns cumulative ambient color, which is computed as sum of all enabled ambient light sources.
   //! Values are NOT clamped (can be greater than 1.0f) and alpha component is fixed to 1.0f.
   //! @sa UpdateRevision()
@@ -181,6 +192,7 @@ protected:
   Standard_Integer        myLightTypes       [Graphic3d_TypeOfLightSource_NB]; //!< counters per each light source type defined in the list
   Standard_Integer        myLightTypesEnabled[Graphic3d_TypeOfLightSource_NB]; //!< counters per each light source type enabled in the list
   Standard_Integer        myNbEnabled;              //!< number of enabled light sources, excluding ambient
+  Standard_Integer        myNbCastShadows;          //!< number of enabled light sources casting shadows
   Standard_Size           myRevision;               //!< current revision of light source set
   Standard_Size           myCacheRevision;          //!< revision of cached state
 };
index 472311f..7469c9e 100644 (file)
@@ -106,6 +106,8 @@ public:
     OitDepthFactor              (0.0f),
     NbMsaaSamples               (0),
     RenderResolutionScale       (1.0f),
+    ShadowMapResolution         (1024),
+    ShadowMapBias               (0.005f),
     ToEnableDepthPrepass        (Standard_False),
     ToEnableAlphaToCoverage     (Standard_True),
     // ray tracing parameters
@@ -199,6 +201,8 @@ public:
   Standard_Integer                  NbMsaaSamples;               //!< number of MSAA samples (should be within 0..GL_MAX_SAMPLES, power-of-two number), 0 by default
   Standard_ShortReal                RenderResolutionScale;       //!< rendering resolution scale factor, 1 by default;
                                                                  //!  incompatible with MSAA (e.g. NbMsaaSamples should be set to 0)
+  Standard_Integer                  ShadowMapResolution;         //!< shadow texture map resolution, 1024 by default
+  Standard_ShortReal                ShadowMapBias;               //!< shadowmap bias, 0.005 by default;
   Standard_Boolean                  ToEnableDepthPrepass;        //!< enables/disables depth pre-pass, False by default
   Standard_Boolean                  ToEnableAlphaToCoverage;     //!< enables/disables alpha to coverage, True by default
 
index aef9958..28c8e7e 100755 (executable)
@@ -78,6 +78,7 @@ const TCollection_AsciiString& Graphic3d_ShaderProgram::ShadersFolder()
 // =======================================================================
 Graphic3d_ShaderProgram::Graphic3d_ShaderProgram()
 : myNbLightsMax (THE_MAX_LIGHTS_DEFAULT),
+  myNbShadowMaps (0),
   myNbClipPlanesMax (THE_MAX_CLIP_PLANES_DEFAULT),
   myNbFragOutputs (THE_NB_FRAG_OUTPUTS),
   myTextureSetBits (Graphic3d_TextureSetBits_NONE),
index 218d8a4..fb612f1 100755 (executable)
@@ -96,6 +96,12 @@ public:
   //! Specify the length of array of light sources (THE_MAX_LIGHTS).
   void SetNbLightsMax (Standard_Integer theNbLights) { myNbLightsMax = theNbLights; }
 
+  //! Return the length of array of shadow maps (THE_NB_SHADOWMAPS); 0 by default.
+  Standard_Integer NbShadowMaps() const { return myNbShadowMaps; }
+
+  //! Specify the length of array of shadow maps (THE_NB_SHADOWMAPS).
+  void SetNbShadowMaps (Standard_Integer theNbMaps) { myNbShadowMaps = theNbMaps; }
+
   //! Return the length of array of clipping planes (THE_MAX_CLIP_PLANES),
   //! to be used for initialization occClipPlaneEquations.
   //! Default value is THE_MAX_CLIP_PLANES_DEFAULT.
@@ -213,6 +219,7 @@ private:
   Graphic3d_ShaderAttributeList myAttributes;    //!< the list of custom vertex attributes
   TCollection_AsciiString       myHeader;        //!< GLSL header with version code and used extensions
   Standard_Integer              myNbLightsMax;   //!< length of array of light sources (THE_MAX_LIGHTS)
+  Standard_Integer              myNbShadowMaps;  //!< length of array of shadow maps (THE_NB_SHADOWMAPS)
   Standard_Integer              myNbClipPlanesMax; //!< length of array of clipping planes (THE_MAX_CLIP_PLANES)
   Standard_Integer              myNbFragOutputs; //!< length of array of Fragment Shader outputs (THE_NB_FRAG_OUTPUTS)
   Standard_Integer              myTextureSetBits;//!< texture units declared within the program, @sa Graphic3d_TextureSetBits
index f554b8e..34d4aa0 100644 (file)
@@ -67,6 +67,10 @@ enum Graphic3d_TextureUnit
   //! Note that it can be overridden to Graphic3d_TextureUnit_0 for FFP fallback on hardware without multi-texturing.
   Graphic3d_TextureUnit_PointSprite = Graphic3d_TextureUnit_1,
 
+  //! sampler2D occShadowMapSampler.
+  //! Directional light source shadowmap texture.
+  Graphic3d_TextureUnit_ShadowMap = -4,
+
   //! sampler2D occEnvLUT.
   //! Lookup table for approximated PBR environment lighting.
   //! Configured as index at the end of available texture units - 3.
index f9b42b0..5c61112 100755 (executable)
@@ -122,6 +122,8 @@ OpenGl_ShaderProgram.cxx
 OpenGl_ShaderProgram.hxx
 OpenGl_ShaderStates.cxx
 OpenGl_ShaderStates.hxx
+OpenGl_ShadowMap.cxx
+OpenGl_ShadowMap.hxx
 OpenGl_StencilTest.cxx
 OpenGl_StencilTest.hxx
 OpenGl_TileSampler.hxx
index 26bf048..0e294b4 100644 (file)
@@ -220,6 +220,7 @@ OpenGl_Context::OpenGl_Context (const Handle(OpenGl_Caps)& theCaps)
   myPBREnvLUTTexUnit       (Graphic3d_TextureUnit_PbrEnvironmentLUT),
   myPBRDiffIBLMapSHTexUnit (Graphic3d_TextureUnit_PbrIblDiffuseSH),
   myPBRSpecIBLMapTexUnit   (Graphic3d_TextureUnit_PbrIblSpecular),
+  myShadowMapTexUnit       (Graphic3d_TextureUnit_ShadowMap),
   myFrameStats (new OpenGl_FrameStats()),
   myActiveMockTextures (0),
   myActiveHatchType (Aspect_HS_SOLID),
@@ -3353,6 +3354,7 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile)
     myPBRDiffIBLMapSHTexUnit = static_cast<Graphic3d_TextureUnit>(myMaxTexCombined + Graphic3d_TextureUnit_PbrIblDiffuseSH);
     myPBRSpecIBLMapTexUnit   = static_cast<Graphic3d_TextureUnit>(myMaxTexCombined + Graphic3d_TextureUnit_PbrIblSpecular);
   }
+  myShadowMapTexUnit = static_cast<Graphic3d_TextureUnit>(myMaxTexCombined + Graphic3d_TextureUnit_ShadowMap);
 }
 
 // =======================================================================
index 7c55ba1..8e7d657 100644 (file)
@@ -630,6 +630,9 @@ public:
   //! Returns texture unit where Specular IBL map is expected to be bound, or 0 if PBR is unavailable.
   Graphic3d_TextureUnit PBRSpecIBLMapTexUnit() const { return myPBRSpecIBLMapTexUnit; }
 
+  //! Returns texture unit where shadow map is expected to be bound, or 0 if unavailable.
+  Graphic3d_TextureUnit ShadowMapTexUnit() const { return myShadowMapTexUnit; }
+
   //! Returns true if VBO is supported and permitted.
   inline bool ToUseVbo() const
   {
@@ -1169,6 +1172,7 @@ private: // context info
   Graphic3d_TextureUnit myPBRDiffIBLMapSHTexUnit; //!< sampler2D occDiffIBLMapSHCoeffs, texture unit where diffuse (irradiance) IBL map's spherical harmonics coefficients is expected to  be binded
                                                   //!  (0 if PBR is not supported)
   Graphic3d_TextureUnit myPBRSpecIBLMapTexUnit;   //!< samplerCube occSpecIBLMap, texture unit where specular IBL map is expected to  be binded (0 if PBR is not supported)
+  Graphic3d_TextureUnit myShadowMapTexUnit;       //!< sampler2D occShadowMapSampler
 
   Handle(OpenGl_ShaderManager) myShaderManager; //! support object for managing shader programs
 
index 1fef00a..d043967 100644 (file)
@@ -236,13 +236,13 @@ public:
   Standard_EXPORT virtual void UnbindBuffer (const Handle(OpenGl_Context)& theGlCtx);
 
   //! Returns the color texture for the given color buffer index.
-  inline const Handle(OpenGl_Texture)& ColorTexture (const GLint theColorBufferIndex = 0) const
+  const Handle(OpenGl_Texture)& ColorTexture (const GLint theColorBufferIndex = 0) const
   {
     return myColorTextures (theColorBufferIndex);
   }
 
   //! Returns the depth-stencil texture.
-  inline const Handle(OpenGl_Texture)& DepthStencilTexture() const
+  const Handle(OpenGl_Texture)& DepthStencilTexture() const
   {
     return myDepthStencilTexture;
   }
index c751d51..d4bf258 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <OpenGl_GlCore20.hxx>
 #include <OpenGl_View.hxx>
+#include <OpenGl_ShadowMap.hxx>
 #include <OpenGl_Workspace.hxx>
 
 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_FrameStats, Graphic3d_FrameStats)
@@ -148,6 +149,8 @@ void OpenGl_FrameStats::updateStatistics (const Handle(Graphic3d_CView)& theView
       aMemFbos += estimatedDataSize (aView->myMainSceneFbosOit[1]);
       aMemFbos += estimatedDataSize (aView->myImmediateSceneFbosOit[0]);
       aMemFbos += estimatedDataSize (aView->myImmediateSceneFbosOit[1]);
+      // shadowmap FBOs
+      aMemFbos += aView->myShadowMaps->EstimatedDataSize();
       // dump FBO
       aMemFbos += estimatedDataSize (aView->myFBO);
       // RayTracing FBO
index e01146f..12e2754 100644 (file)
@@ -589,12 +589,13 @@ void OpenGl_LayerList::renderLayer (const Handle(OpenGl_Workspace)& theWorkspace
 
   const Standard_Boolean hasLocalCS = !aLayerSettings.OriginTransformation().IsNull();
   const Handle(OpenGl_ShaderManager)& aManager = aCtx->ShaderManager();
-  Handle(Graphic3d_LightSet) aLightsBack = aManager->LightSourceState().LightSources();
+  Handle(Graphic3d_LightSet)    aLightsBack = aManager->LightSourceState().LightSources();
+  Handle(OpenGl_ShadowMapArray) aShadowMaps = aManager->LightSourceState().ShadowMaps();
   const bool hasOwnLights = aCtx->ColorMask() && !aLayerSettings.Lights().IsNull() && aLayerSettings.Lights() != aLightsBack;
   if (hasOwnLights)
   {
     aLayerSettings.Lights()->UpdateRevision();
-    aManager->UpdateLightSourceStateTo (aLayerSettings.Lights(), theWorkspace->View()->SpecIBLMapLevels());
+    aManager->UpdateLightSourceStateTo (aLayerSettings.Lights(), theWorkspace->View()->SpecIBLMapLevels(), Handle(OpenGl_ShadowMapArray)());
   }
 
   const Handle(Graphic3d_Camera)& aWorldCamera = theWorkspace->View()->Camera();
@@ -665,7 +666,7 @@ void OpenGl_LayerList::renderLayer (const Handle(OpenGl_Workspace)& theWorkspace
 
   if (hasOwnLights)
   {
-    aManager->UpdateLightSourceStateTo (aLightsBack, theWorkspace->View()->SpecIBLMapLevels());
+    aManager->UpdateLightSourceStateTo (aLightsBack, theWorkspace->View()->SpecIBLMapLevels(), aShadowMaps);
   }
   if (hasLocalCS)
   {
@@ -703,6 +704,8 @@ void OpenGl_LayerList::Render (const Handle(OpenGl_Workspace)& theWorkspace,
   aCtx->core11fwd->glGetIntegerv (GL_DEPTH_FUNC,      &aPrevSettings.DepthFunc);
   aCtx->core11fwd->glGetBooleanv (GL_DEPTH_WRITEMASK, &aPrevSettings.DepthMask);
   OpenGl_GlobalLayerSettings aDefaultSettings = aPrevSettings;
+  const bool isShadowMapPass = theReadDrawFbo != NULL
+                           && !theReadDrawFbo->HasColor();
 
   // Two render filters are used to support transparency draw. Opaque filter accepts
   // only non-transparent OpenGl elements of a layer and counts number of skipped
@@ -722,8 +725,10 @@ void OpenGl_LayerList::Render (const Handle(OpenGl_Workspace)& theWorkspace,
   OpenGl_LayerStack::iterator aStackIter (myTransparentToProcess.Origin());
   Standard_Integer aClearDepthLayerPrev = -1, aClearDepthLayer = -1;
   const bool toPerformDepthPrepass = theWorkspace->View()->RenderingParams().ToEnableDepthPrepass
-                                  && aPrevSettings.DepthMask == GL_TRUE;
-  const Handle(Graphic3d_LightSet) aLightsBack = aCtx->ShaderManager()->LightSourceState().LightSources();
+                                  && aPrevSettings.DepthMask == GL_TRUE
+                                  && !isShadowMapPass;
+  const Handle(Graphic3d_LightSet)    aLightsBack = aCtx->ShaderManager()->LightSourceState().LightSources();
+  const Handle(OpenGl_ShadowMapArray) aShadowMaps = aCtx->ShaderManager()->LightSourceState().ShadowMaps();
   for (OpenGl_FilteredIndexedLayerIterator aLayerIterStart (myLayers, theToDrawImmediate, theLayersToProcess); aLayerIterStart.More();)
   {
     bool hasSkippedDepthLayers = false;
@@ -732,7 +737,7 @@ void OpenGl_LayerList::Render (const Handle(OpenGl_Workspace)& theWorkspace,
       if (aPassIter == 0)
       {
         aCtx->SetColorMask (false);
-        aCtx->ShaderManager()->UpdateLightSourceStateTo (Handle(Graphic3d_LightSet)(), theWorkspace->View()->SpecIBLMapLevels());
+        aCtx->ShaderManager()->UpdateLightSourceStateTo (Handle(Graphic3d_LightSet)(), theWorkspace->View()->SpecIBLMapLevels(), Handle(OpenGl_ShadowMapArray)());
         aDefaultSettings.DepthFunc = aPrevSettings.DepthFunc;
         aDefaultSettings.DepthMask = GL_TRUE;
       }
@@ -743,13 +748,21 @@ void OpenGl_LayerList::Render (const Handle(OpenGl_Workspace)& theWorkspace,
           continue;
         }
         aCtx->SetColorMask (true);
-        aCtx->ShaderManager()->UpdateLightSourceStateTo (aLightsBack, theWorkspace->View()->SpecIBLMapLevels());
+        aCtx->ShaderManager()->UpdateLightSourceStateTo (aLightsBack, theWorkspace->View()->SpecIBLMapLevels(), aShadowMaps);
         aDefaultSettings = aPrevSettings;
       }
       else if (aPassIter == 2)
       {
-        aCtx->SetColorMask (true);
-        aCtx->ShaderManager()->UpdateLightSourceStateTo (aLightsBack, theWorkspace->View()->SpecIBLMapLevels());
+        if (isShadowMapPass)
+        {
+          aCtx->SetColorMask (false);
+          aCtx->ShaderManager()->UpdateLightSourceStateTo (Handle(Graphic3d_LightSet)(), theWorkspace->View()->SpecIBLMapLevels(), Handle(OpenGl_ShadowMapArray)());
+        }
+        else
+        {
+          aCtx->SetColorMask (true);
+          aCtx->ShaderManager()->UpdateLightSourceStateTo (aLightsBack, theWorkspace->View()->SpecIBLMapLevels(), aShadowMaps);
+        }
         if (toPerformDepthPrepass)
         {
           aDefaultSettings.DepthFunc = GL_EQUAL;
@@ -830,6 +843,7 @@ void OpenGl_LayerList::Render (const Handle(OpenGl_Workspace)& theWorkspace,
     }
   }
 
+  aCtx->ShaderManager()->UpdateLightSourceStateTo (aLightsBack, theWorkspace->View()->SpecIBLMapLevels(), aShadowMaps);
   aCtx->core11fwd->glDepthMask (aPrevSettings.DepthMask);
   aCtx->core11fwd->glDepthFunc (aPrevSettings.DepthFunc);
 
index 857b5c6..c9b9aad 100644 (file)
 #include <OpenGl_Context.hxx>
 #include <Graphic3d_CubeMapPacked.hxx>
 #include <OpenGl_ShaderManager.hxx>
+#include <OpenGl_ShadowMap.hxx>
 #include <OpenGl_ShaderProgram.hxx>
 #include <OpenGl_VertexBufferCompat.hxx>
 #include <OpenGl_PointSprite.hxx>
 #include <OpenGl_Workspace.hxx>
 #include <TCollection_ExtendedString.hxx>
 
+#include "../Shaders/Shaders_DirectionalLightShadow_glsl.pxx"
 #include "../Shaders/Shaders_PBRDistribution_glsl.pxx"
 #include "../Shaders/Shaders_PBRGeometry_glsl.pxx"
 #include "../Shaders/Shaders_PBRFresnel_glsl.pxx"
@@ -249,7 +251,8 @@ const char THE_FUNC_directionalLight[] =
   EOL"void directionalLight (in int  theId,"
   EOL"                       in vec3 theNormal,"
   EOL"                       in vec3 theView,"
-  EOL"                       in bool theIsFront)"
+  EOL"                       in bool theIsFront,"
+  EOL"                       in float theShadow)"
   EOL"{"
   EOL"  vec3 aLight = vec3 (occWorldViewMatrix * vec4 (occLight_Position (theId), 0.0));"
   EOL
@@ -265,8 +268,8 @@ const char THE_FUNC_directionalLight[] =
   EOL"    aSpecl = pow (aNdotH, theIsFront ? occFrontMaterial_Shininess() : occBackMaterial_Shininess());"
   EOL"  }"
   EOL
-  EOL"  Diffuse  += occLight_Diffuse  (theId) * aNdotL;"
-  EOL"  Specular += occLight_Specular (theId) * aSpecl;"
+  EOL"  Diffuse  += occLight_Diffuse  (theId) * aNdotL * theShadow;"
+  EOL"  Specular += occLight_Specular (theId) * aSpecl * theShadow;"
   EOL"}";
 
 //! Function computes contribution of directional light source
@@ -274,7 +277,8 @@ const char THE_FUNC_PBR_directionalLight[] =
   EOL"void directionalLight (in int  theId,"
   EOL"                       in vec3 theNormal,"
   EOL"                       in vec3 theView,"
-  EOL"                       in bool theIsFront)"
+  EOL"                       in bool theIsFront,"
+  EOL"                       in float theShadow)"
   EOL"{"
   EOL"  vec3 aLight = occLight_Position (theId);"
   EOL
@@ -282,7 +286,7 @@ const char THE_FUNC_PBR_directionalLight[] =
   EOL"  DirectLighting += occPBRIllumination (theView, aLight, theNormal,"
   EOL"                                        BaseColor, Metallic, Roughness, IOR,"
   EOL"                                        occLight_Specular (theId),"
-  EOL"                                        occLight_Intensity(theId));"
+  EOL"                                        occLight_Intensity(theId)) * theShadow;"
   EOL"}";
 
 //! The same as THE_FUNC_directionalLight but for the light with zero index
@@ -290,7 +294,8 @@ const char THE_FUNC_PBR_directionalLight[] =
 const char THE_FUNC_directionalLightFirst[] =
   EOL"void directionalLightFirst (in vec3 theNormal,"
   EOL"                            in vec3 theView,"
-  EOL"                            in bool theIsFront)"
+  EOL"                            in bool theIsFront,"
+  EOL"                            in float theShadow)"
   EOL"{"
   EOL"  vec3 aLight = vec3 (occWorldViewMatrix * vec4 (occLight_Position (0), 0.0));"
   EOL
@@ -306,8 +311,8 @@ const char THE_FUNC_directionalLightFirst[] =
   EOL"    aSpecl = pow (aNdotH, theIsFront ? occFrontMaterial_Shininess() : occBackMaterial_Shininess());"
   EOL"  }"
   EOL
-  EOL"  Diffuse  += occLight_Diffuse(0)  * aNdotL;"
-  EOL"  Specular += occLight_Specular(0) * aSpecl;"
+  EOL"  Diffuse  += occLight_Diffuse(0)  * aNdotL * theShadow;"
+  EOL"  Specular += occLight_Specular(0) * aSpecl * theShadow;"
   EOL"}";
 
 //! Returns the real cubemap fetching direction considering sides orientation, memory layout and vertical flip.
@@ -489,11 +494,14 @@ EOL"  gl_Position = occProjectionMatrix * occWorldViewMatrix * occModelWorldMatr
 #endif
 
   //! Generate map key for light sources configuration.
-  static TCollection_AsciiString genLightKey (const Handle(Graphic3d_LightSet)& theLights)
+  static TCollection_AsciiString genLightKey (const Handle(Graphic3d_LightSet)& theLights,
+                                              const bool theHasShadowMap)
   {
     if (theLights->NbEnabled() <= THE_NB_UNROLLED_LIGHTS_MAX)
     {
-      return TCollection_AsciiString ("l_") + theLights->KeyEnabledLong();
+      return theHasShadowMap
+           ? TCollection_AsciiString ("ls_") + theLights->KeyEnabledLong()
+           : TCollection_AsciiString ("l_") + theLights->KeyEnabledLong();
     }
 
     const Standard_Integer aMaxLimit = roundUpMaxLightSources (theLights->NbEnabled());
@@ -661,7 +669,7 @@ void OpenGl_ShaderManager::switchLightPrograms()
     return;
   }
 
-  const TCollection_AsciiString aKey = genLightKey (aLights);
+  const TCollection_AsciiString aKey = genLightKey (aLights, myLightSourceState.HasShadowMaps());
   if (!myMapOfLightPrograms.Find (aKey, myLightPrograms))
   {
     myLightPrograms = new OpenGl_SetOfShaderPrograms();
@@ -691,10 +699,12 @@ void OpenGl_ShaderManager::UpdateSRgbState()
 // purpose  : Updates state of OCCT light sources
 // =======================================================================
 void OpenGl_ShaderManager::UpdateLightSourceStateTo (const Handle(Graphic3d_LightSet)& theLights,
-                                                     Standard_Integer                  theSpecIBLMapLevels)
+                                                     Standard_Integer theSpecIBLMapLevels,
+                                                     const Handle(OpenGl_ShadowMapArray)& theShadowMaps)
 {
   myLightSourceState.Set (theLights);
   myLightSourceState.SetSpecIBLMapLevels (theSpecIBLMapLevels);
+  myLightSourceState.SetShadowMaps (theShadowMaps);
   myLightSourceState.Update();
   switchLightPrograms();
 }
@@ -954,6 +964,31 @@ void OpenGl_ShaderManager::pushLightSourceState (const Handle(OpenGl_ShaderProgr
   {
     theProgram->SetUniform (myContext, aLocation, myLightSourceState.SpecIBLMapLevels());
   }
+
+  // update shadow map variables
+  if (const OpenGl_ShaderUniformLocation aShadowMatLoc = theProgram->GetStateLocation (OpenGl_OCC_LIGHT_SHADOWMAP_MATRICES))
+  {
+    if (myShadowMatArray.Size() < theProgram->NbShadowMaps())
+    {
+      myShadowMatArray.Resize (0, theProgram->NbShadowMaps() - 1, false);
+    }
+
+    Graphic3d_Vec2 aSizeBias;
+    if (myLightSourceState.HasShadowMaps())
+    {
+      aSizeBias.SetValues (1.0f / (float )myLightSourceState.ShadowMaps()->First()->Texture()->SizeX(),
+                           myLightSourceState.ShadowMaps()->First()->ShadowMapBias());
+      const Standard_Integer aNbShadows = Min (theProgram->NbShadowMaps(), myLightSourceState.ShadowMaps()->Size());
+      for (Standard_Integer aShadowIter = 0; aShadowIter < aNbShadows; ++aShadowIter)
+      {
+        const Handle(OpenGl_ShadowMap)& aShadow = myLightSourceState.ShadowMaps()->Value (aShadowIter);
+        myShadowMatArray[aShadowIter] = aShadow->LightSourceMatrix();
+      }
+    }
+
+    theProgram->SetUniform (myContext, aShadowMatLoc, theProgram->NbShadowMaps(), &myShadowMatArray.First());
+    theProgram->SetUniform (myContext, theProgram->GetStateLocation (OpenGl_OCC_LIGHT_SHADOWMAP_SIZE_BIAS), aSizeBias);
+  }
 }
 
 // =======================================================================
@@ -1491,6 +1526,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramFont()
   defaultGlslVersion (aProgramSrc, "font", 0);
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (0);
+  aProgramSrc->SetNbShadowMaps (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcFrag, Graphic3d_TOS_FRAGMENT, aUniforms, aStageInOuts));
@@ -1636,6 +1672,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramFboBlit (Handle(OpenGl_S
   aProgramSrc->SetId (anId);
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (0);
+  aProgramSrc->SetNbShadowMaps (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcFrag, Graphic3d_TOS_FRAGMENT, aUniforms, aStageInOuts));
@@ -1728,6 +1765,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramOitCompositing (const St
   aProgramSrc->SetId (theMsaa ? "occt_weight-oit-msaa" : "occt_weight-oit");
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (0);
+  aProgramSrc->SetNbShadowMaps (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcFrag, Graphic3d_TOS_FRAGMENT, aUniforms, aStageInOuts));
@@ -1930,8 +1968,18 @@ TCollection_AsciiString OpenGl_ShaderManager::prepareGeomMainSrc (OpenGl_ShaderO
       if (aVarListIter.Value().Stages == (Graphic3d_TOS_VERTEX | Graphic3d_TOS_FRAGMENT))
       {
         const TCollection_AsciiString aVarName = aVarListIter.Value().Name.Token (" ", 2);
-        aSrcMainGeom += TCollection_AsciiString()
-         + EOL"  geomOut." + aVarName + " = geomIn[" + aVertIndex + "]." + aVarName + ";";
+        if (aVarName.Value (aVarName.Length()) == ']')
+        {
+          // copy the whole array
+          const TCollection_AsciiString aVarName2 = aVarName.Token ("[", 1);
+          aSrcMainGeom += TCollection_AsciiString()
+            + EOL"  geomOut." + aVarName2 + " = geomIn[" + aVertIndex + "]." + aVarName2 + ";";
+        }
+        else
+        {
+          aSrcMainGeom += TCollection_AsciiString()
+           + EOL"  geomOut." + aVarName + " = geomIn[" + aVertIndex + "]." + aVarName + ";";
+         }
       }
     }
 
@@ -2179,6 +2227,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramUnlit (Handle(OpenGl_Sha
   defaultGlslVersion (aProgramSrc, theIsOutline ? "outline" : "unlit", theBits);
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (0);
+  aProgramSrc->SetNbShadowMaps (0);
   aProgramSrc->SetNbClipPlanesMax (aNbClipPlanes);
   aProgramSrc->SetAlphaTest ((theBits & OpenGl_PO_AlphaTest) != 0);
   const Standard_Integer aNbGeomInputVerts = !aSrcGeom.IsEmpty() ? 3 : 0;
@@ -2235,34 +2284,65 @@ TCollection_AsciiString OpenGl_ShaderManager::pointSpriteShadingSrc (const TColl
 TCollection_AsciiString OpenGl_ShaderManager::stdComputeLighting (Standard_Integer& theNbLights,
                                                                   Standard_Boolean  theHasVertColor,
                                                                   Standard_Boolean  theIsPBR,
-                                                                  Standard_Boolean  theHasEmissive)
+                                                                  Standard_Boolean  theHasEmissive,
+                                                                  Standard_Boolean  theHasShadowMap)
 {
   TCollection_AsciiString aLightsFunc, aLightsLoop;
   theNbLights = 0;
   const Handle(Graphic3d_LightSet)& aLights = myLightSourceState.LightSources();
   if (!aLights.IsNull())
   {
+    const bool hasShadowMap = theHasShadowMap && myLightSourceState.HasShadowMaps();
     theNbLights = aLights->NbEnabled();
     if (theNbLights <= THE_NB_UNROLLED_LIGHTS_MAX)
     {
       Standard_Integer anIndex = 0;
+      if (hasShadowMap)
+      {
+        for (Graphic3d_LightSet::Iterator aLightIter (aLights, Graphic3d_LightSet::IterationFilter_ExcludeDisabledAndAmbient);
+             aLightIter.More(); aLightIter.Next())
+        {
+          if (aLightIter.Value()->Type() == Graphic3d_TOLS_DIRECTIONAL
+           && aLightIter.Value()->ToCastShadows())
+          {
+            aLightsLoop = aLightsLoop + EOL"    directionalLight (" + anIndex + ", theNormal, theView, theIsFront,"
+                                        EOL"                      occDirectionalLightShadow (" + anIndex + ", theNormal));";
+            ++anIndex;
+          }
+        }
+      }
       for (Graphic3d_LightSet::Iterator aLightIter (aLights, Graphic3d_LightSet::IterationFilter_ExcludeDisabledAndAmbient);
-           aLightIter.More(); aLightIter.Next(), ++anIndex)
+           aLightIter.More(); aLightIter.Next())
       {
         switch (aLightIter.Value()->Type())
         {
           case Graphic3d_TOLS_AMBIENT:
-            --anIndex;
+          {
             break; // skip ambient
+          }
           case Graphic3d_TOLS_DIRECTIONAL:
-            aLightsLoop = aLightsLoop + EOL"    directionalLight (" + anIndex + ", theNormal, theView, theIsFront);";
+          {
+            if (hasShadowMap
+             && aLightIter.Value()->ToCastShadows())
+            {
+              break;
+            }
+            aLightsLoop = aLightsLoop + EOL"    directionalLight (" + anIndex + ", theNormal, theView, theIsFront, 1.0);";
+            ++anIndex;
             break;
+          }
           case Graphic3d_TOLS_POSITIONAL:
+          {
             aLightsLoop = aLightsLoop + EOL"    pointLight (" + anIndex + ", theNormal, theView, aPoint, theIsFront);";
+            ++anIndex;
             break;
+          }
           case Graphic3d_TOLS_SPOT:
+          {
             aLightsLoop = aLightsLoop + EOL"    spotLight (" + anIndex + ", theNormal, theView, aPoint, theIsFront);";
+            ++anIndex;
             break;
+          }
         }
       }
     }
@@ -2280,7 +2360,7 @@ TCollection_AsciiString OpenGl_ShaderManager::stdComputeLighting (Standard_Integ
         aLightsLoop +=
           EOL"      if (aType == OccLightType_Direct)"
           EOL"      {"
-          EOL"        directionalLight (anIndex, theNormal, theView, theIsFront);"
+          EOL"        directionalLight (anIndex, theNormal, theView, theIsFront, 1.0);"
           EOL"      }";
       }
       if (aLights->NbEnabledLightsOfType (Graphic3d_TOLS_POSITIONAL) > 0)
@@ -2323,14 +2403,19 @@ TCollection_AsciiString OpenGl_ShaderManager::stdComputeLighting (Standard_Integ
 
     if (aLights->NbEnabledLightsOfType (Graphic3d_TOLS_DIRECTIONAL) == 1
      && theNbLights == 1
-     && !theIsPBR)
+     && !theIsPBR
+     && !hasShadowMap)
     {
       // use the version with hard-coded first index
-      aLightsLoop = EOL"    directionalLightFirst(theNormal, theView, theIsFront);";
+      aLightsLoop = EOL"    directionalLightFirst(theNormal, theView, theIsFront, 1.0);";
       aLightsFunc += THE_FUNC_directionalLightFirst;
     }
     else if (aLights->NbEnabledLightsOfType (Graphic3d_TOLS_DIRECTIONAL) > 0)
     {
+      if (hasShadowMap)
+      {
+        aLightsFunc += Shaders_DirectionalLightShadow_glsl;
+      }
       aLightsFunc += theIsPBR ? THE_FUNC_PBR_directionalLight : THE_FUNC_directionalLight;
     }
     if (aLights->NbEnabledLightsOfType (Graphic3d_TOLS_POSITIONAL) > 0)
@@ -2521,7 +2606,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramGouraud (Handle(OpenGl_S
   aStageInOuts.Append (OpenGl_ShaderObject::ShaderVariable ("vec4 BackColor",  Graphic3d_TOS_VERTEX | Graphic3d_TOS_FRAGMENT));
 
   Standard_Integer aNbLights = 0;
-  const TCollection_AsciiString aLights = stdComputeLighting (aNbLights, !aSrcVertColor.IsEmpty(), false, true);
+  const TCollection_AsciiString aLights = stdComputeLighting (aNbLights, !aSrcVertColor.IsEmpty(), false, true, false);
   aSrcVert = TCollection_AsciiString()
     + THE_FUNC_transformNormal_view
     + EOL
@@ -2533,8 +2618,8 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramGouraud (Handle(OpenGl_S
       EOL"  vec4 aPosition      = occWorldViewMatrix * aPositionWorld;"
       EOL"  vec3 aNormal        = transformNormal (occNormal);"
       EOL"  vec3 aView          = vec3 (0.0, 0.0, 1.0);"
-      EOL"  FrontColor  = computeLighting (normalize (aNormal), normalize (aView), aPosition, true);"
-      EOL"  BackColor   = computeLighting (normalize (aNormal), normalize (aView), aPosition, false);"
+      EOL"  FrontColor  = computeLighting (aNormal, aView, aPosition, true);"
+      EOL"  BackColor   = computeLighting (aNormal, aView, aPosition, false);"
     + aSrcVertExtraMain
     + THE_VERT_gl_Position
     + EOL"}";
@@ -2552,10 +2637,11 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramGouraud (Handle(OpenGl_S
     + EOL"  occSetFragColor (getFinalColor());"
     + EOL"}";
 
-  const TCollection_AsciiString aProgId = TCollection_AsciiString ("gouraud-") + genLightKey (myLightSourceState.LightSources()) + "-";
+  const TCollection_AsciiString aProgId = TCollection_AsciiString ("gouraud-") + genLightKey (myLightSourceState.LightSources(), false) + "-";
   defaultGlslVersion (aProgramSrc, aProgId, theBits);
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (aNbLights);
+  aProgramSrc->SetNbShadowMaps (0);
   aProgramSrc->SetNbClipPlanesMax (aNbClipPlanes);
   aProgramSrc->SetAlphaTest ((theBits & OpenGl_PO_AlphaTest) != 0);
   const Standard_Integer aNbGeomInputVerts = !aSrcGeom.IsEmpty() ? 3 : 0;
@@ -2760,6 +2846,19 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_Sha
   aStageInOuts.Append (OpenGl_ShaderObject::ShaderVariable ("vec4 PositionWorld", Graphic3d_TOS_VERTEX | Graphic3d_TOS_FRAGMENT));
   aStageInOuts.Append (OpenGl_ShaderObject::ShaderVariable ("vec4 Position",      Graphic3d_TOS_VERTEX | Graphic3d_TOS_FRAGMENT));
   aStageInOuts.Append (OpenGl_ShaderObject::ShaderVariable ("vec3 View",          Graphic3d_TOS_VERTEX | Graphic3d_TOS_FRAGMENT));
+  if (myLightSourceState.HasShadowMaps())
+  {
+    aUniforms.Append (OpenGl_ShaderObject::ShaderVariable ("mat4      occShadowMapMatrices[THE_NB_SHADOWMAPS]", Graphic3d_TOS_VERTEX));
+    aUniforms.Append (OpenGl_ShaderObject::ShaderVariable ("sampler2D occShadowMapSamplers[THE_NB_SHADOWMAPS]", Graphic3d_TOS_FRAGMENT));
+    aUniforms.Append (OpenGl_ShaderObject::ShaderVariable ("vec2      occShadowMapSizeBias",                    Graphic3d_TOS_FRAGMENT));
+
+    aStageInOuts.Append (OpenGl_ShaderObject::ShaderVariable ("vec4 PosLightSpace[THE_NB_SHADOWMAPS]", Graphic3d_TOS_VERTEX | Graphic3d_TOS_FRAGMENT));
+    aSrcVertExtraMain +=
+      EOL"  for (int aShadowIter = 0; aShadowIter < THE_NB_SHADOWMAPS; ++aShadowIter)"
+      EOL"  {"
+      EOL"    PosLightSpace[aShadowIter] = occShadowMapMatrices[aShadowIter] * PositionWorld;"
+      EOL"  }";
+  }
 
   aSrcVert = TCollection_AsciiString()
     + aSrcVertExtraFunc
@@ -2786,9 +2885,13 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_Sha
     : EOL"#define getFinalColor getColor";
 
   Standard_Integer aNbLights = 0;
+  Standard_Integer aNbShadowMaps = myLightSourceState.HasShadowMaps()
+                                 ? myLightSourceState.LightSources()->NbCastShadows()
+                                 : 0;
   const TCollection_AsciiString aLights = stdComputeLighting (aNbLights, !aSrcFragGetVertColor.IsEmpty(), theIsPBR,
                                                               (theBits & OpenGl_PO_TextureRGB) == 0
-                                                           || (theBits & OpenGl_PO_IsPoint) != 0);
+                                                           || (theBits & OpenGl_PO_IsPoint) != 0,
+                                                              myLightSourceState.HasShadowMaps());
   aSrcFrag += TCollection_AsciiString()
     + EOL
     + aSrcFragGetVertColor
@@ -2802,10 +2905,12 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_Sha
     + EOL"  occSetFragColor (getFinalColor());"
     + EOL"}";
 
-  const TCollection_AsciiString aProgId = TCollection_AsciiString (theIsFlatNormal ? "flat-" : "phong-") + (theIsPBR ? "pbr-" : "") + genLightKey (myLightSourceState.LightSources()) + "-";
+  const TCollection_AsciiString aProgId = TCollection_AsciiString (theIsFlatNormal ? "flat-" : "phong-") + (theIsPBR ? "pbr-" : "")
+                                        + genLightKey (myLightSourceState.LightSources(), aNbShadowMaps > 0) + "-";
   defaultGlslVersion (aProgramSrc, aProgId, theBits, isFlatNormal);
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (aNbLights);
+  aProgramSrc->SetNbShadowMaps (aNbShadowMaps);
   aProgramSrc->SetNbClipPlanesMax (aNbClipPlanes);
   aProgramSrc->SetAlphaTest ((theBits & OpenGl_PO_AlphaTest) != 0);
 
@@ -3002,6 +3107,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramStereo (Handle(OpenGl_Sh
   defaultGlslVersion (aProgramSrc, aName, 0);
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (0);
+  aProgramSrc->SetNbShadowMaps (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcFrag, Graphic3d_TOS_FRAGMENT, aUniforms, aStageInOuts));
@@ -3048,6 +3154,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramBoundBox()
   defaultGlslVersion (aProgramSrc, "bndbox", 0);
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (0);
+  aProgramSrc->SetNbShadowMaps (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcFrag, Graphic3d_TOS_FRAGMENT, aUniforms, aStageInOuts));
@@ -3126,6 +3233,7 @@ Standard_Boolean OpenGl_ShaderManager::preparePBREnvBakingProgram()
   defaultGlslVersion (aProgramSrc, "pbr_env_baking", 0);
   aProgramSrc->SetDefaultSampler (false);
   aProgramSrc->SetNbLightsMax (0);
+  aProgramSrc->SetNbShadowMaps (0);
   aProgramSrc->SetNbClipPlanesMax (0);
   aProgramSrc->SetPBR (true);
   aProgramSrc->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX,   aUniforms, aStageInOuts));
@@ -3200,6 +3308,7 @@ const Handle(Graphic3d_ShaderProgram)& OpenGl_ShaderManager::GetBgCubeMapProgram
     defaultGlslVersion (myBgCubeMapProgram, "background_cubemap", 0);
     myBgCubeMapProgram->SetDefaultSampler (false);
     myBgCubeMapProgram->SetNbLightsMax (0);
+    myBgCubeMapProgram->SetNbShadowMaps (0);
     myBgCubeMapProgram->SetNbClipPlanesMax (0);
     myBgCubeMapProgram->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcVert, Graphic3d_TOS_VERTEX, aUniforms, aStageInOuts));
     myBgCubeMapProgram->AttachShader (OpenGl_ShaderObject::CreateFromSource (aSrcFrag, Graphic3d_TOS_FRAGMENT, aUniforms, aStageInOuts));
index f6280fd..36558c9 100644 (file)
@@ -287,7 +287,24 @@ public:
 
   //! Updates state of OCCT light sources.
   Standard_EXPORT void UpdateLightSourceStateTo (const Handle(Graphic3d_LightSet)& theLights,
-                                                 Standard_Integer                  theSpecIBLMapLevels = 0);
+                                                 Standard_Integer theSpecIBLMapLevels,
+                                                 const Handle(OpenGl_ShadowMapArray)& theShadowMaps);
+
+  //! Updates state of OCCT light sources to dynamically enable/disable shadowmap.
+  //! @param theToCast [in] flag to enable/disable shadowmap
+  //! @return previous flag state
+  bool SetCastShadows (const bool theToCast)
+  {
+    if (myLightSourceState.ShadowMaps().IsNull()
+     || myLightSourceState.ToCastShadows() == theToCast)
+       {
+      return myLightSourceState.ToCastShadows();
+    }
+
+    myLightSourceState.SetCastShadows (theToCast);
+    switchLightPrograms();
+    return !theToCast;
+  }
 
   //! Invalidate state of OCCT light sources.
   Standard_EXPORT void UpdateLightSourceState();
@@ -709,10 +726,12 @@ protected:
   //! @param theHasVertColor [in]  flag to use getVertColor() instead of Ambient and Diffuse components of active material
   //! @param theIsPBR        [in]  flag to activate PBR pipeline
   //! @param theHasEmissive  [in]  flag to include emissive
+  //! @param theHasShadowMap [in]  flag to include shadow map
   Standard_EXPORT TCollection_AsciiString stdComputeLighting (Standard_Integer& theNbLights,
                                                               Standard_Boolean  theHasVertColor,
                                                               Standard_Boolean  theIsPBR,
-                                                              Standard_Boolean  theHasEmissive = true);
+                                                              Standard_Boolean  theHasEmissive,
+                                                              Standard_Boolean  theHasShadowMap);
 
   //! Bind specified program to current context and apply state.
   Standard_EXPORT Standard_Boolean bindProgramWithState (const Handle(OpenGl_ShaderProgram)& theProgram,
@@ -834,6 +853,7 @@ protected:
 
   mutable NCollection_Array1<Standard_Integer>             myLightTypeArray;
   mutable NCollection_Array1<OpenGl_ShaderLightParameters> myLightParamsArray;
+  mutable NCollection_Array1<Graphic3d_Mat4>               myShadowMatArray;
   mutable NCollection_Array1<OpenGl_Vec4>                  myClipPlaneArray;
   mutable NCollection_Array1<OpenGl_Vec4d>                 myClipPlaneArrayFfp;
   mutable NCollection_Array1<Standard_Integer>             myClipChainArray;
index b858767..c7ebf41 100755 (executable)
@@ -64,6 +64,9 @@ Standard_CString OpenGl_ShaderProgram::PredefinedKeywords[] =
   "occLightSourcesTypes",   // OpenGl_OCC_LIGHT_SOURCE_TYPES
   "occLightSources",        // OpenGl_OCC_LIGHT_SOURCE_PARAMS
   "occLightAmbient",        // OpenGl_OCC_LIGHT_AMBIENT
+  "occShadowMapSizeBias",   // OpenGl_OCC_LIGHT_SHADOWMAP_SIZE_BIAS
+  "occShadowMapSamplers",   // OpenGl_OCC_LIGHT_SHADOWMAP_SAMPLERS,
+  "occShadowMapMatrices",   // OpenGl_OCC_LIGHT_SHADOWMAP_MATRICES,
 
   "occTextureEnable",       // OpenGl_OCCT_TEXTURE_ENABLE
   "occDistinguishingMode",  // OpenGl_OCCT_DISTINGUISH_MODE
@@ -169,6 +172,7 @@ OpenGl_ShaderProgram::OpenGl_ShaderProgram (const Handle(Graphic3d_ShaderProgram
   myProxy     (theProxy),
   myShareCount(1),
   myNbLightsMax (0),
+  myNbShadowMaps (0),
   myNbClipPlanesMax (0),
   myNbFragOutputs (1),
   myTextureSetBits (Graphic3d_TextureSetBits_NONE),
@@ -406,10 +410,15 @@ Standard_Boolean OpenGl_ShaderProgram::Initialize (const Handle(OpenGl_Context)&
 
     TCollection_AsciiString aHeaderConstants;
     myNbLightsMax     = !myProxy.IsNull() ? myProxy->NbLightsMax() : 0;
+    myNbShadowMaps    = !myProxy.IsNull() ? myProxy->NbShadowMaps() : 0;
     myNbClipPlanesMax = !myProxy.IsNull() ? myProxy->NbClipPlanesMax() : 0;
     aHeaderConstants += TCollection_AsciiString("#define THE_MAX_LIGHTS ") + myNbLightsMax + "\n";
     aHeaderConstants += TCollection_AsciiString("#define THE_MAX_CLIP_PLANES ") + myNbClipPlanesMax + "\n";
     aHeaderConstants += TCollection_AsciiString("#define THE_NB_FRAG_OUTPUTS ") + myNbFragOutputs + "\n";
+    if (myNbShadowMaps > 0)
+    {
+      aHeaderConstants += TCollection_AsciiString("#define THE_NB_SHADOWMAPS ") + myNbShadowMaps + "\n";
+    }
     if (!myProxy.IsNull()
       && myProxy->HasDefaultSampler())
     {
@@ -556,6 +565,16 @@ Standard_Boolean OpenGl_ShaderProgram::Initialize (const Handle(OpenGl_Context)&
   {
     SetUniform (theCtx, aLocSampler, GLint(theCtx->PBREnvLUTTexUnit()));
   }
+  if (const OpenGl_ShaderUniformLocation aLocSampler = GetUniformLocation (theCtx, "occShadowMapSamplers"))
+  {
+    std::vector<GLint> aShadowSamplers (myNbShadowMaps);
+    const GLint aSamplFrom = GLint(theCtx->ShadowMapTexUnit()) - myNbShadowMaps + 1;
+    for (Standard_Integer aSamplerIter = 0; aSamplerIter < myNbShadowMaps; ++aSamplerIter)
+    {
+      aShadowSamplers[aSamplerIter] = aSamplFrom + aSamplerIter;
+    }
+    SetUniform (theCtx, aLocSampler, myNbShadowMaps, &aShadowSamplers.front());
+  }
 
   const TCollection_AsciiString aSamplerNamePrefix ("occSampler");
   const Standard_Integer aNbUnitsMax = Max (theCtx->MaxCombinedTextureUnits(), Graphic3d_TextureUnit_NB);
@@ -1369,6 +1388,24 @@ Standard_Boolean OpenGl_ShaderProgram::SetUniform (const Handle(OpenGl_Context)&
   return SetUniform (theCtx, theLocation, OpenGl_Mat4::Map (*theValue.mat), theTranspose);
 }
 
+// =======================================================================
+// function : SetUniform
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_ShaderProgram::SetUniform (const Handle(OpenGl_Context)& theCtx,
+                                                   GLint                         theLocation,
+                                                   GLuint                        theCount,
+                                                   const OpenGl_Mat4*            theData)
+{
+  if (myProgramID == NO_PROGRAM || theLocation == INVALID_LOCATION)
+  {
+    return Standard_False;
+  }
+
+  theCtx->core20fwd->glUniformMatrix4fv (theLocation, theCount, GL_FALSE, theData->GetData());
+  return Standard_True;
+}
+
 // =======================================================================
 // function : SetUniform
 // purpose  : Specifies the value of the float uniform array
index 7ed83ff..f88eb8a 100755 (executable)
@@ -59,6 +59,9 @@ enum OpenGl_StateVariable
   OpenGl_OCC_LIGHT_SOURCE_TYPES,
   OpenGl_OCC_LIGHT_SOURCE_PARAMS,
   OpenGl_OCC_LIGHT_AMBIENT,
+  OpenGl_OCC_LIGHT_SHADOWMAP_SIZE_BIAS,// occShadowMapSizeBias
+  OpenGl_OCC_LIGHT_SHADOWMAP_SAMPLERS, // occShadowMapSamplers
+  OpenGl_OCC_LIGHT_SHADOWMAP_MATRICES, // occShadowMapMatrices
 
   // Material state
   OpenGl_OCCT_TEXTURE_ENABLE,
@@ -284,6 +287,9 @@ public:
   //! to be used for initialization occLightSources (OpenGl_OCC_LIGHT_SOURCE_PARAMS).
   Standard_Integer NbLightsMax() const { return myNbLightsMax; }
 
+  //! Return the length of array of shadow maps (THE_NB_SHADOWMAPS); 0 by default.
+  Standard_Integer NbShadowMaps() const { return myNbShadowMaps; }
+
   //! Return the length of array of clipping planes (THE_MAX_CLIP_PLANES),
   //! to be used for initialization occClipPlaneEquations (OpenGl_OCC_CLIP_PLANE_EQUATIONS) and occClipPlaneChains (OpenGl_OCC_CLIP_PLANE_CHAINS).
   Standard_Integer NbClipPlanesMax() const { return myNbClipPlanesMax; }
@@ -546,6 +552,13 @@ public:
                                                const OpenGl_Mat4&            theValue,
                                                GLboolean                     theTranspose = GL_FALSE);
 
+  //! Specifies the value of the array of float uniform 4x4 matrices.
+  //! Wrapper over glUniformMatrix4fv().
+  Standard_EXPORT Standard_Boolean SetUniform (const Handle(OpenGl_Context)& theCtx,
+                                               GLint                         theLocation,
+                                               GLuint                        theCount,
+                                               const OpenGl_Mat4*            theData);
+
   //! Specifies the value of the float uniform 4x4 matrix.
   Standard_EXPORT Standard_Boolean SetUniform (const Handle(OpenGl_Context)& theCtx,
                                                const GLchar*                 theName,
@@ -661,6 +674,7 @@ protected:
   Handle(Graphic3d_ShaderProgram) myProxy;         //!< Proxy shader program (from application layer)
   Standard_Integer                myShareCount;    //!< program users count, initialized with 1 (already shared by one user)
   Standard_Integer                myNbLightsMax;   //!< length of array of light sources (THE_MAX_LIGHTS)
+  Standard_Integer                myNbShadowMaps;  //!< length of array of shadow maps (THE_NB_SHADOWMAPS)
   Standard_Integer                myNbClipPlanesMax; //!< length of array of clipping planes (THE_MAX_CLIP_PLANES)
   Standard_Integer                myNbFragOutputs; //!< length of array of Fragment Shader outputs (THE_NB_FRAG_OUTPUTS)
   Standard_Integer                myTextureSetBits;//!< texture units declared within the program, @sa Graphic3d_TextureSetBits
index 9f25334..d4fcc96 100755 (executable)
@@ -21,6 +21,8 @@
 #include <OpenGl_Element.hxx>
 #include <OpenGl_Vec.hxx>
 
+class OpenGl_ShadowMapArray;
+
 //! Defines interface for OpenGL state.
 class OpenGl_StateInterface
 {
@@ -122,7 +124,7 @@ class OpenGl_LightSourceState : public OpenGl_StateInterface
 public:
 
   //! Creates uninitialized state of light sources.
-  OpenGl_LightSourceState() : mySpecIBLMapLevels (0) {}
+  OpenGl_LightSourceState() : mySpecIBLMapLevels (0), myToCastShadows (Standard_True) {}
 
   //! Sets new light sources.
   void Set (const Handle(Graphic3d_LightSet)& theLightSources) { myLightSources = theLightSources; }
@@ -137,10 +139,27 @@ public:
   //! Sets number of mipmap levels used in specular IBL map.
   void SetSpecIBLMapLevels(Standard_Integer theSpecIBLMapLevels) { mySpecIBLMapLevels = theSpecIBLMapLevels; }
 
+  //! Returns TRUE if shadowmap is set.
+  bool HasShadowMaps() const { return myToCastShadows && !myShadowMaps.IsNull(); }
+
+  //! Returns shadowmap.
+  const Handle(OpenGl_ShadowMapArray)& ShadowMaps() const { return myShadowMaps; }
+
+  //! Sets shadowmap.
+  void SetShadowMaps (const Handle(OpenGl_ShadowMapArray)& theMap) { myShadowMaps = theMap; }
+
+  //! Returns TRUE if shadowmap should be enabled when available; TRUE by default.
+  bool ToCastShadows() const { return myToCastShadows; }
+
+  //! Set if shadowmap should be enabled when available.
+  void SetCastShadows (bool theToCast) { myToCastShadows = theToCast; }
+
 private:
 
   Handle(Graphic3d_LightSet) myLightSources;     //!< List of OCCT light sources
   Standard_Integer           mySpecIBLMapLevels; //!< Number of mipmap levels used in specular IBL map (0 by default or in case of using non-PBR shading model)
+  Handle(OpenGl_ShadowMapArray) myShadowMaps;    //!< active shadowmap
+  Standard_Boolean           myToCastShadows;    //!< enable/disable shadowmap
 
 };
 
diff --git a/src/OpenGl/OpenGl_ShadowMap.cxx b/src/OpenGl/OpenGl_ShadowMap.cxx
new file mode 100644 (file)
index 0000000..53d0a8b
--- /dev/null
@@ -0,0 +1,171 @@
+// Copyright (c) 2021 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_ShadowMap.hxx>
+
+#include <OpenGl_ArbFBO.hxx>
+#include <OpenGl_FrameBuffer.hxx>
+#include <OpenGl_ShaderManager.hxx>
+#include <Graphic3d_Camera.hxx>
+#include <Graphic3d_CView.hxx>
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShadowMap, OpenGl_NamedResource)
+
+// =======================================================================
+// function : OpenGl_ShadowMap
+// purpose  :
+// =======================================================================
+OpenGl_ShadowMap::OpenGl_ShadowMap()
+: OpenGl_NamedResource ("shadow_map"),
+  myShadowMapFbo (new OpenGl_FrameBuffer()),
+  myShadowCamera (new Graphic3d_Camera()),
+  myShadowMapBias (0.0f)
+{
+  //
+}
+
+// =======================================================================
+// function : Release
+// purpose  :
+// =======================================================================
+void OpenGl_ShadowMap::Release (OpenGl_Context* theCtx)
+{
+  myShadowMapFbo->Release (theCtx);
+}
+
+// =======================================================================
+// function : ~OpenGl_ShadowMap
+// purpose  :
+// =======================================================================
+OpenGl_ShadowMap::~OpenGl_ShadowMap()
+{
+  Release (NULL);
+}
+
+// =======================================================================
+// function : EstimatedDataSize
+// purpose  :
+// =======================================================================
+Standard_Size OpenGl_ShadowMap::EstimatedDataSize() const
+{
+  return myShadowMapFbo->EstimatedDataSize();
+}
+
+// =======================================================================
+// function : IsValid
+// purpose  :
+// =======================================================================
+bool OpenGl_ShadowMap::IsValid() const
+{
+  return myShadowMapFbo->IsValid();
+}
+
+// =======================================================================
+// function : Texture
+// purpose  :
+// =======================================================================
+const Handle(OpenGl_Texture)& OpenGl_ShadowMap::Texture() const
+{
+  return myShadowMapFbo->DepthStencilTexture();
+}
+
+// =======================================================================
+// function : UpdateCamera
+// purpose  :
+// =======================================================================
+bool OpenGl_ShadowMap::UpdateCamera (const Graphic3d_CView& theView)
+{
+  const Bnd_Box aMinMaxBox  = theView.MinMaxValues (false); // applicative min max boundaries
+  const Bnd_Box aGraphicBox = theView.MinMaxValues (true);  // real graphical boundaries (not accounting infinite flag).
+
+  switch (myShadowLight->Type())
+  {
+    case Graphic3d_TOLS_AMBIENT:
+    {
+      return false; // not applicable
+    }
+    case Graphic3d_TOLS_DIRECTIONAL:
+    {
+      Graphic3d_Vec4d aDir (myShadowLight->Direction().X(), myShadowLight->Direction().Y(), myShadowLight->Direction().Z(), 0.0);
+      if (myShadowLight->IsHeadlight())
+      {
+        Graphic3d_Mat4d anOrientInv;
+        theView.Camera()->OrientationMatrix().Inverted (anOrientInv);
+        aDir = anOrientInv * aDir;
+      }
+      myShadowCamera->SetProjectionType (Graphic3d_Camera::Projection_Orthographic);
+      myShadowCamera->SetDirection (gp_Dir (aDir.x(), aDir.y(), aDir.z()));
+      myShadowCamera->SetUp (!myShadowCamera->Direction().IsParallel (gp::DY(), Precision::Angular())
+                            ? gp::DY()
+                            : gp::DX());
+      myShadowCamera->OrthogonalizeUp();
+
+      // Fitting entire scene to the light might produce a shadow map of too low resolution.
+      // More reliable approach would be putting a center to a current eye position and limiting maximum range,
+      // so that shadow range will be limited to some reasonable distance from current eye.
+      if (myShadowCamera->FitMinMax (aMinMaxBox, 10.0 * Precision::Confusion(), false))
+      {
+        myShadowCamera->SetScale (Max (myShadowCamera->ViewDimensions().X() * 1.1, myShadowCamera->ViewDimensions().Y() * 1.1)); // add margin
+      }
+      myShadowCamera->ZFitAll (1.0, aMinMaxBox, aGraphicBox);
+      myLightMatrix = myShadowCamera->ProjectionMatrixF() * myShadowCamera->OrientationMatrixF();
+      return true;
+    }
+    case Graphic3d_TOLS_POSITIONAL:
+    {
+      // render into cubemap shadowmap texture
+      return false; // not implemented
+    }
+    case Graphic3d_TOLS_SPOT:
+    {
+      //myShadowCamera->SetProjectionType (Graphic3d_Camera::Projection_Perspective);
+      //myShadowCamera->SetEye (theCastShadowLight->Position());
+      return false; // not implemented
+    }
+  }
+  return false;
+}
+
+// =======================================================================
+// function : Release
+// purpose  :
+// =======================================================================
+void OpenGl_ShadowMapArray::Release (OpenGl_Context* theCtx)
+{
+  for (Standard_Integer anIter = Lower(); anIter <= Upper(); ++anIter)
+  {
+    if (const Handle(OpenGl_ShadowMap)& aShadow = ChangeValue (anIter))
+    {
+      aShadow->Release (theCtx);
+    }
+  }
+}
+
+// =======================================================================
+// function : EstimatedDataSize
+// purpose  :
+// =======================================================================
+Standard_Size OpenGl_ShadowMapArray::EstimatedDataSize() const
+{
+  Standard_Size aSize = 0;
+  for (Standard_Integer anIter = Lower(); anIter <= Upper(); ++anIter)
+  {
+    if (const Handle(OpenGl_ShadowMap)& aShadow = Value (anIter))
+    {
+      aSize += aShadow->EstimatedDataSize();
+    }
+  }
+  return aSize;
+}
diff --git a/src/OpenGl/OpenGl_ShadowMap.hxx b/src/OpenGl/OpenGl_ShadowMap.hxx
new file mode 100644 (file)
index 0000000..d192c7a
--- /dev/null
@@ -0,0 +1,115 @@
+// Copyright (c) 2021 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_ShadowMap_HeaderFile
+#define _OpenGl_ShadowMap_HeaderFile
+
+#include <Graphic3d_Mat4.hxx>
+#include <NCollection_Array1.hxx>
+#include <NCollection_Shared.hxx>
+#include <OpenGl_NamedResource.hxx>
+
+class Graphic3d_Camera;
+class Graphic3d_CLight;
+class Graphic3d_CView;
+class OpenGl_FrameBuffer;
+class OpenGl_Texture;
+
+//! This class contains shadow mapping resources.
+class OpenGl_ShadowMap : public OpenGl_NamedResource
+{
+  DEFINE_STANDARD_RTTIEXT(OpenGl_ShadowMap, OpenGl_NamedResource)
+public:
+
+  //! Empty constructor.
+  OpenGl_ShadowMap();
+
+  //! Releases all OpenGL resources.
+  Standard_EXPORT virtual void Release (OpenGl_Context* theCtx) Standard_OVERRIDE;
+
+  //! Returns estimated GPU memory usage for holding data without considering overheads and allocation alignment rules.
+  Standard_EXPORT virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE;
+
+  //! Destructor.
+  Standard_EXPORT virtual ~OpenGl_ShadowMap();
+
+  //! Return TRUE if defined.
+  Standard_EXPORT bool IsValid() const;
+
+  //! Return framebuffer.
+  const Handle(OpenGl_FrameBuffer)& FrameBuffer() const { return myShadowMapFbo; }
+
+  //! Return depth texture.
+  Standard_EXPORT const Handle(OpenGl_Texture)& Texture() const;
+
+  //! Return light source casting the shadow or NULL if undefined.
+  const Handle(Graphic3d_CLight)& LightSource() const { return myShadowLight; }
+
+  //! Set light source casting the shadow.
+  void SetLightSource (const Handle(Graphic3d_CLight)& theLight) { myShadowLight = theLight; }
+
+  //! Return rendering camera.
+  const Handle(Graphic3d_Camera)& Camera() const { return myShadowCamera; }
+
+  //! Return light source mapping matrix.
+  const Graphic3d_Mat4& LightSourceMatrix() const { return myLightMatrix; }
+
+  //! Set light source mapping matrix.
+  void SetLightSourceMatrix (const Graphic3d_Mat4& theMat) { myLightMatrix = theMat; }
+
+  //! Returns shadowmap bias.
+  Standard_ShortReal ShadowMapBias() const { return myShadowMapBias; }
+
+  //! Sets shadowmap bias.
+  void SetShadowMapBias (Standard_ShortReal theBias) { myShadowMapBias = theBias; }
+
+  //! Compute camera.
+  Standard_EXPORT bool UpdateCamera (const Graphic3d_CView& theView);
+
+private:
+
+  Handle(OpenGl_FrameBuffer) myShadowMapFbo;  //!< frame buffer for rendering shadow map
+  Handle(Graphic3d_CLight)   myShadowLight;   //!< light source to render shadow map
+  Handle(Graphic3d_Camera)   myShadowCamera;  //!< rendering camera
+  Graphic3d_Mat4             myLightMatrix;   //!< light source matrix
+  Standard_ShortReal         myShadowMapBias; //!< shadowmap bias
+
+};
+
+//! Array of shadow maps.
+class OpenGl_ShadowMapArray : public Standard_Transient, public NCollection_Array1<Handle(OpenGl_ShadowMap)>
+{
+public:
+  //! Empty constructor.
+  OpenGl_ShadowMapArray() {}
+
+  //! Releases all OpenGL resources.
+  Standard_EXPORT void Release (OpenGl_Context* theCtx);
+
+  //! Return TRUE if defined.
+  bool IsValid() const
+  {
+    return !IsEmpty()
+         && First()->IsValid();
+  }
+
+  //! Returns estimated GPU memory usage for holding data without considering overheads and allocation alignment rules.
+  Standard_EXPORT Standard_Size EstimatedDataSize() const;
+
+public:
+  DEFINE_STANDARD_ALLOC
+  DEFINE_NCOLLECTION_ALLOC
+
+};
+
+#endif // _OpenGl_ShadowMap_HeaderFile
index b49bced..ac9e12d 100644 (file)
@@ -440,8 +440,12 @@ void OpenGl_Structure::Render (const Handle(OpenGl_Workspace) &theWorkspace) con
   }
 #endif
 
+  bool anOldCastShadows = false;
   if (!myTrsfPers.IsNull())
   {
+    // temporarily disable shadows on non-3d objects
+    anOldCastShadows = aCtx->ShaderManager()->SetCastShadows (false);
+
     aCtx->WorldViewState.Push();
     OpenGl_Mat4& aWorldView = aCtx->WorldViewState.ChangeCurrent();
     myTrsfPers->Apply (aCtx->Camera(),
@@ -608,6 +612,7 @@ void OpenGl_Structure::Render (const Handle(OpenGl_Workspace) &theWorkspace) con
   if (!myTrsfPers.IsNull())
   {
     aCtx->WorldViewState.Pop();
+    aCtx->ShaderManager()->SetCastShadows (anOldCastShadows);
   }
 
   // Restore named status
index 449d58f..059c663 100644 (file)
@@ -30,6 +30,7 @@
 #include <OpenGl_GraduatedTrihedron.hxx>
 #include <OpenGl_GraphicDriver.hxx>
 #include <OpenGl_ShaderManager.hxx>
+#include <OpenGl_ShadowMap.hxx>
 #include <OpenGl_Texture.hxx>
 #include <OpenGl_Window.hxx>
 #include <OpenGl_Workspace.hxx>
@@ -149,6 +150,7 @@ OpenGl_View::OpenGl_View (const Handle(Graphic3d_StructureManager)& theMgr,
   myRaytraceFBO1[1]          = new OpenGl_FrameBuffer();
   myRaytraceFBO2[0]          = new OpenGl_FrameBuffer();
   myRaytraceFBO2[1]          = new OpenGl_FrameBuffer();
+  myShadowMaps = new OpenGl_ShadowMapArray();
 }
 
 // =======================================================================
@@ -219,6 +221,7 @@ void OpenGl_View::releaseSrgbResources (const Handle(OpenGl_Context)& theCtx)
   myOpenGlFBO2              ->Release (theCtx.get());
   myFullScreenQuad           .Release (theCtx.get());
   myFullScreenQuadFlip       .Release (theCtx.get());
+  myShadowMaps->Release (theCtx.get());
 
   // Technically we should also re-initialize all sRGB/RGB8 color textures.
   // But for now consider this sRGB disabling/enabling to be done at application start-up
@@ -1302,6 +1305,53 @@ bool OpenGl_View::prepareFrameBuffers (Graphic3d_Camera::Projection& theProj)
     myImmediateSceneFbosOit[1]->ChangeViewport (0, 0);
   }
 
+  // allocate shadow maps
+  const Handle(Graphic3d_LightSet)& aLights = myShadingModel == Graphic3d_TOSM_UNLIT ? myNoShadingLight : myLights;
+  if (!aLights.IsNull())
+  {
+    aLights->UpdateRevision();
+  }
+  bool toUseShadowMap = myRenderParams.IsShadowEnabled
+                     && myRenderParams.ShadowMapResolution > 0
+                     && !myLights.IsNull()
+                     && myLights->NbCastShadows() > 0
+                     && myRenderParams.Method != Graphic3d_RM_RAYTRACING;
+  if (toUseShadowMap)
+  {
+    if (myShadowMaps->Size() != myLights->NbCastShadows())
+    {
+      myShadowMaps->Release (aCtx.get());
+      myShadowMaps->Resize (0, myLights->NbCastShadows() - 1, true);
+    }
+
+    const GLint aSamplFrom = GLint(aCtx->ShadowMapTexUnit()) - myLights->NbCastShadows() + 1;
+    for (Standard_Integer aShadowIter = 0; aShadowIter < myShadowMaps->Size(); ++aShadowIter)
+    {
+      Handle(OpenGl_ShadowMap)& aShadow = myShadowMaps->ChangeValue (aShadowIter);
+      if (aShadow.IsNull())
+      {
+        aShadow = new OpenGl_ShadowMap();
+      }
+      aShadow->SetShadowMapBias (myRenderParams.ShadowMapBias);
+      aShadow->Texture()->Sampler()->Parameters()->SetTextureUnit ((Graphic3d_TextureUnit )(aSamplFrom + aShadowIter));
+
+      const Handle(OpenGl_FrameBuffer)& aShadowFbo = aShadow->FrameBuffer();
+      if (aShadowFbo->GetVPSizeX() != myRenderParams.ShadowMapResolution
+       && toUseShadowMap)
+      {
+        OpenGl_ColorFormats aDummy;
+        if (!aShadowFbo->Init (aCtx, myRenderParams.ShadowMapResolution, myRenderParams.ShadowMapResolution, aDummy, myFboDepthFormat, 0))
+        {
+          toUseShadowMap = false;
+        }
+      }
+    }
+  }
+  if (!toUseShadowMap && myShadowMaps->IsValid())
+  {
+    myShadowMaps->Release (aCtx.get());
+  }
+
   return true;
 }
 
@@ -1361,6 +1411,28 @@ void OpenGl_View::Redraw()
     return;
   }
 
+  // draw shadow maps
+  if (myShadowMaps->IsValid())
+  {
+    Standard_Integer aShadowIndex = myShadowMaps->Lower();
+    for (Graphic3d_LightSet::Iterator aLightIter (myLights, Graphic3d_LightSet::IterationFilter_ActiveShadowCasters);
+         aLightIter.More(); aLightIter.Next())
+    {
+      const Handle(Graphic3d_CLight)& aLight = aLightIter.Value();
+      if (aLight->ToCastShadows())
+      {
+        const Handle(OpenGl_ShadowMap)& aShadowMap = myShadowMaps->ChangeValue (aShadowIndex);
+        aShadowMap->SetLightSource (aLight);
+        renderShadowMap (aShadowMap);
+        ++aShadowIndex;
+      }
+    }
+    for (; aShadowIndex <= myShadowMaps->Upper(); ++aShadowIndex)
+    {
+      myShadowMaps->ChangeValue (aShadowIndex)->SetLightSource (Handle(Graphic3d_CLight)());
+    }
+  }
+
   OpenGl_FrameBuffer* aFrameBuffer = myFBO.get();
   bool toSwap = aCtx->IsRender()
             && !aCtx->caps->buffersNoSwap
@@ -1807,7 +1879,7 @@ void OpenGl_View::redraw (const Graphic3d_Camera::Projection theProjection,
 }
 
 // =======================================================================
-// function : redrawMonoImmediate
+// function : redrawImmediate
 // purpose  :
 // =======================================================================
 bool OpenGl_View::redrawImmediate (const Graphic3d_Camera::Projection theProjection,
@@ -1882,6 +1954,60 @@ bool OpenGl_View::redrawImmediate (const Graphic3d_Camera::Projection theProject
   return !toCopyBackToFront;
 }
 
+//=======================================================================
+//function : renderShadowMap
+//purpose  :
+//=======================================================================
+void OpenGl_View::renderShadowMap (const Handle(OpenGl_ShadowMap)& theShadowMap)
+{
+  const Handle(OpenGl_Context)& aCtx = myWorkspace->GetGlContext();
+  if (!theShadowMap->UpdateCamera (*this))
+  {
+    return;
+  }
+
+  myBVHSelector.SetViewVolume (theShadowMap->Camera());
+  myBVHSelector.SetViewportSize (myWindow->Width(), myWindow->Height(), myRenderParams.ResolutionRatio());
+  myBVHSelector.CacheClipPtsProjections();
+
+  myLocalOrigin.SetCoord (0.0, 0.0, 0.0);
+  aCtx->SetCamera (theShadowMap->Camera());
+  aCtx->ProjectionState.SetCurrent (theShadowMap->Camera()->ProjectionMatrixF());
+  aCtx->ApplyProjectionMatrix();
+
+  aCtx->ShaderManager()->UpdateMaterialState();
+  aCtx->ShaderManager()->UpdateModelWorldStateTo (OpenGl_Mat4());
+  aCtx->ShaderManager()->SetShadingModel (Graphic3d_TOSM_UNLIT);
+
+  const Handle(OpenGl_FrameBuffer)& aShadowBuffer = theShadowMap->FrameBuffer();
+  aShadowBuffer->BindBuffer    (aCtx);
+  aShadowBuffer->SetupViewport (aCtx);
+
+  aCtx->SetColorMask (false);
+  aCtx->SetAllowSampleAlphaToCoverage (false);
+  aCtx->SetSampleAlphaToCoverage (false);
+
+  myWorkspace->UseZBuffer()    = true;
+  myWorkspace->UseDepthWrite() = true;
+  aCtx->core11fwd->glDepthFunc (GL_LEQUAL);
+  aCtx->core11fwd->glDepthMask (GL_TRUE);
+  aCtx->core11fwd->glEnable (GL_DEPTH_TEST);
+  aCtx->core11fwd->glClearDepth (1.0);
+  aCtx->core11fwd->glClear (GL_DEPTH_BUFFER_BIT);
+
+  renderScene (Graphic3d_Camera::Projection_Orthographic, aShadowBuffer.get(), NULL, false);
+
+  aCtx->SetColorMask (true);
+  myWorkspace->ResetAppliedAspect();
+  aCtx->BindProgram (Handle(OpenGl_ShaderProgram)());
+
+//Image_AlienPixMap anImage; anImage.InitZero (Image_Format_Gray, aShadowBuffer->GetVPSizeX(), aShadowBuffer->GetVPSizeY());
+//OpenGl_FrameBuffer::BufferDump (aCtx, aShadowBuffer, anImage, Graphic3d_BT_Depth);
+//anImage.Save (TCollection_AsciiString ("shadow") + theShadowMap->Texture()->Sampler()->Parameters()->TextureUnit() + ".png");
+
+  bindDefaultFbo();
+}
+
 //=======================================================================
 //function : Render
 //purpose  :
@@ -1929,7 +2055,7 @@ void OpenGl_View::render (Graphic3d_Camera::Projection theProjection,
    || aLightsRevision != myLightsRevision)
   {
     myLightsRevision = aLightsRevision;
-    aManager->UpdateLightSourceStateTo (aLights, SpecIBLMapLevels());
+    aManager->UpdateLightSourceStateTo (aLights, SpecIBLMapLevels(), myShadowMaps->IsValid() ? myShadowMaps : Handle(OpenGl_ShadowMapArray)());
     myLastLightSourceState = StateInfo (myCurrLightSourceState, aManager->LightSourceState().Index());
   }
 
@@ -2017,8 +2143,31 @@ void OpenGl_View::render (Graphic3d_Camera::Projection theProjection,
 
   myWorkspace->SetEnvironmentTexture (myTextureEnv);
 
+  const bool hasShadowMap = aContext->ShaderManager()->LightSourceState().HasShadowMaps();
+  if (hasShadowMap)
+  {
+    for (Standard_Integer aShadowIter = myShadowMaps->Lower(); aShadowIter <= myShadowMaps->Upper(); ++aShadowIter)
+    {
+      const Handle(OpenGl_ShadowMap)& aShadow = myShadowMaps->Value (aShadowIter);
+      aShadow->Texture()->Bind (aContext);
+    }
+  }
+
   renderScene (theProjection, theOutputFBO, theOitAccumFbo, theToDrawImmediate);
 
+  if (hasShadowMap)
+  {
+    for (Standard_Integer aShadowIter = myShadowMaps->Lower(); aShadowIter <= myShadowMaps->Upper(); ++aShadowIter)
+    {
+      const Handle(OpenGl_ShadowMap)& aShadow = myShadowMaps->Value (aShadowIter);
+      aShadow->Texture()->Unbind (aContext);
+    }
+    if (aContext->core15fwd != NULL)
+    {
+      aContext->core15fwd->glActiveTexture (GL_TEXTURE0);
+    }
+  }
+
   myWorkspace->SetEnvironmentTexture (Handle(OpenGl_TextureSet)());
 
   // ===============================
index fcac9d3..ea421e5 100644 (file)
@@ -59,9 +59,12 @@ class Graphic3d_StructureManager;
 class OpenGl_GraphicDriver;
 class OpenGl_PBREnvironment;
 class OpenGl_StateCounter;
+class OpenGl_ShadowMap;
+class OpenGl_ShadowMapArray;
 class OpenGl_TriangleSet;
 class OpenGl_Workspace;
 class OpenGl_View;
+
 DEFINE_STANDARD_HANDLE(OpenGl_View,Graphic3d_CView)
 
 //! Implementation of OpenGl view.
@@ -390,6 +393,10 @@ protected: //! @name low-level redrawing sub-routines
 
 protected: //! @name Rendering of GL graphics (with prepared drawing buffer).
 
+  //! Renders the graphical contents of the view into the preprepared shadowmap framebuffer.
+  //! @param theShadowMap [in] the framebuffer for rendering shadowmap.
+  Standard_EXPORT virtual void renderShadowMap (const Handle(OpenGl_ShadowMap)& theShadowMap);
+
   //! Renders the graphical contents of the view into the preprepared window or framebuffer.
   //! @param theProjection [in] the projection that should be used for rendering.
   //! @param theReadDrawFbo [in] the framebuffer for rendering graphics.
@@ -527,6 +534,7 @@ protected: //! @name Rendering properties
   Handle(OpenGl_FrameBuffer) myImmediateSceneFbos[2];    //!< Additional buffers for immediate layer in stereo mode.
   Handle(OpenGl_FrameBuffer) myImmediateSceneFbosOit[2]; //!< Additional buffers for transparency draw of immediate layer.
   Handle(OpenGl_FrameBuffer) myXrSceneFbo;            //!< additional FBO (without MSAA) for submitting to XR
+  Handle(OpenGl_ShadowMapArray) myShadowMaps;         //!< additional FBOs for shadow map rendering
   OpenGl_VertexBuffer        myFullScreenQuad;        //!< Vertices for full-screen quad rendering.
   OpenGl_VertexBuffer        myFullScreenQuadFlip;
   Standard_Boolean           myToFlipOutput;          //!< Flag to draw result image upside-down
diff --git a/src/Shaders/DirectionalLightShadow.glsl b/src/Shaders/DirectionalLightShadow.glsl
new file mode 100644 (file)
index 0000000..555de1e
--- /dev/null
@@ -0,0 +1,27 @@
+//! Coefficients for gathering close samples.
+const vec2 occPoissonDisk16[16] = vec2[](
+ vec2(-0.94201624,-0.39906216), vec2( 0.94558609,-0.76890725), vec2(-0.09418410,-0.92938870), vec2( 0.34495938, 0.29387760),
+ vec2(-0.91588581, 0.45771432), vec2(-0.81544232,-0.87912464), vec2(-0.38277543, 0.27676845), vec2( 0.97484398, 0.75648379),
+ vec2( 0.44323325,-0.97511554), vec2( 0.53742981,-0.47373420), vec2(-0.26496911,-0.41893023), vec2( 0.79197514, 0.19090188),
+ vec2(-0.24188840, 0.99706507), vec2(-0.81409955, 0.91437590), vec2( 0.19984126, 0.78641367), vec2( 0.14383161,-0.14100790)
+);
+
+//! Function computes directional light shadow attenuation (1.0 means no shadow).
+float occDirectionalLightShadow (in int  theId,
+                                 in vec3 theNormal)
+{
+  vec4 aPosLightSpace = PosLightSpace[theId];
+  vec3 aLightDir = vec3 (occWorldViewMatrix * vec4 (occLight_Position (theId), 0.0));
+  vec3 aProjCoords = (aPosLightSpace.xyz / aPosLightSpace.w) * 0.5 + vec3 (0.5);
+  float aCurrentDepth = aProjCoords.z;
+  if (abs(aProjCoords.x) > 1.0 || abs(aProjCoords.y) > 1.0 || aCurrentDepth > 1.0) { return 1.0; }
+  vec2 aTexelSize = vec2 (occShadowMapSizeBias.x);
+  float aBias = max (occShadowMapSizeBias.y * (1.0 - dot (theNormal, aLightDir)), occShadowMapSizeBias.y * 0.1);
+  float aShadow = 0.0;
+  for (int aPosIter = 0; aPosIter < 16; ++aPosIter)
+  {
+    float aClosestDepth = occTexture2D (occShadowMapSamplers[theId], aProjCoords.xy + occPoissonDisk16[aPosIter] * aTexelSize).r;
+    aShadow += (aCurrentDepth - aBias) > aClosestDepth ? 1.0 : 0.0;
+  }
+  return 1.0 - aShadow / 16.0;
+}
index 7c7ada0..ada8209 100644 (file)
@@ -1,5 +1,6 @@
 srcinc:::Declarations.glsl
 srcinc:::DeclarationsImpl.glsl
+srcinc:::DirectionalLightShadow.glsl
 srcinc:::PBRCookTorrance.glsl
 srcinc:::PBRDistribution.glsl
 srcinc:::PBREnvBaking.fs
@@ -19,6 +20,7 @@ srcinc:::RaytraceSmooth.fs
 srcinc:::TangentSpaceNormal.glsl
 Shaders_Declarations_glsl.pxx
 Shaders_DeclarationsImpl_glsl.pxx
+Shaders_DirectionalLightShadow_glsl.pxx
 Shaders_Display_fs.pxx
 Shaders_PBRCookTorrance_glsl.pxx
 Shaders_PBRDistribution_glsl.pxx
diff --git a/src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx b/src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx
new file mode 100644 (file)
index 0000000..58f5a96
--- /dev/null
@@ -0,0 +1,30 @@
+// This file has been automatically generated from resource file src/Shaders/DirectionalLightShadow.glsl
+
+static const char Shaders_DirectionalLightShadow_glsl[] =
+  "//! Coefficients for gathering close samples.\n"
+  "const vec2 occPoissonDisk16[16] = vec2[](\n"
+  " vec2(-0.94201624,-0.39906216), vec2( 0.94558609,-0.76890725), vec2(-0.09418410,-0.92938870), vec2( 0.34495938, 0.29387760),\n"
+  " vec2(-0.91588581, 0.45771432), vec2(-0.81544232,-0.87912464), vec2(-0.38277543, 0.27676845), vec2( 0.97484398, 0.75648379),\n"
+  " vec2( 0.44323325,-0.97511554), vec2( 0.53742981,-0.47373420), vec2(-0.26496911,-0.41893023), vec2( 0.79197514, 0.19090188),\n"
+  " vec2(-0.24188840, 0.99706507), vec2(-0.81409955, 0.91437590), vec2( 0.19984126, 0.78641367), vec2( 0.14383161,-0.14100790)\n"
+  ");\n"
+  "\n"
+  "//! Function computes directional light shadow attenuation (1.0 means no shadow).\n"
+  "float occDirectionalLightShadow (in int  theId,\n"
+  "                                 in vec3 theNormal)\n"
+  "{\n"
+  "  vec4 aPosLightSpace = PosLightSpace[theId];\n"
+  "  vec3 aLightDir = vec3 (occWorldViewMatrix * vec4 (occLight_Position (theId), 0.0));\n"
+  "  vec3 aProjCoords = (aPosLightSpace.xyz / aPosLightSpace.w) * 0.5 + vec3 (0.5);\n"
+  "  float aCurrentDepth = aProjCoords.z;\n"
+  "  if (abs(aProjCoords.x) > 1.0 || abs(aProjCoords.y) > 1.0 || aCurrentDepth > 1.0) { return 1.0; }\n"
+  "  vec2 aTexelSize = vec2 (occShadowMapSizeBias.x);\n"
+  "  float aBias = max (occShadowMapSizeBias.y * (1.0 - dot (theNormal, aLightDir)), occShadowMapSizeBias.y * 0.1);\n"
+  "  float aShadow = 0.0;\n"
+  "  for (int aPosIter = 0; aPosIter < 16; ++aPosIter)\n"
+  "  {\n"
+  "    float aClosestDepth = occTexture2D (occShadowMapSamplers[theId], aProjCoords.xy + occPoissonDisk16[aPosIter] * aTexelSize).r;\n"
+  "    aShadow += (aCurrentDepth - aBias) > aClosestDepth ? 1.0 : 0.0;\n"
+  "  }\n"
+  "  return 1.0 - aShadow / 16.0;\n"
+  "}\n";
index fd09f89..2bdabea 100644 (file)
@@ -10771,6 +10771,7 @@ static int VLight (Draw_Interpretor& theDi,
           theDi << "  Type:       Directional\n";
           theDi << "  Intensity:  " << aLight->Intensity() << "\n";
           theDi << "  Headlight:  " << (aLight->Headlight() ? "TRUE" : "FALSE") << "\n";
+          theDi << "  CastShadows:" << (aLight->ToCastShadows() ? "TRUE" : "FALSE") << "\n";
           theDi << "  Smoothness: " << aLight->Smoothness() << "\n";
           aLight->Direction (anXYZ[0], anXYZ[1], anXYZ[2]);
           theDi << "  Direction:  " << anXYZ[0] << ", " << anXYZ[1] << ", " << anXYZ[2] << "\n";
@@ -10781,6 +10782,7 @@ static int VLight (Draw_Interpretor& theDi,
           theDi << "  Type:       Positional\n";
           theDi << "  Intensity:  " << aLight->Intensity() << "\n";
           theDi << "  Headlight:  " << (aLight->Headlight() ? "TRUE" : "FALSE") << "\n";
+          theDi << "  CastShadows:" << (aLight->ToCastShadows() ? "TRUE" : "FALSE") << "\n";
           theDi << "  Smoothness: " << aLight->Smoothness() << "\n";
           aLight->Position  (anXYZ[0], anXYZ[1], anXYZ[2]);
           theDi << "  Position:   " << anXYZ[0] << ", " << anXYZ[1] << ", " << anXYZ[2] << "\n";
@@ -10794,6 +10796,7 @@ static int VLight (Draw_Interpretor& theDi,
           theDi << "  Type:       Spot\n";
           theDi << "  Intensity:  " << aLight->Intensity() << "\n";
           theDi << "  Headlight:  " << (aLight->Headlight() ? "TRUE" : "FALSE") << "\n";
+          theDi << "  CastShadows:" << (aLight->ToCastShadows() ? "TRUE" : "FALSE") << "\n";
           aLight->Position  (anXYZ[0], anXYZ[1], anXYZ[2]);
           theDi << "  Position:   " << anXYZ[0] << ", " << anXYZ[1] << ", " << anXYZ[2] << "\n";
           aLight->Direction (anXYZ[0], anXYZ[1], anXYZ[2]);
@@ -11295,6 +11298,25 @@ static int VLight (Draw_Interpretor& theDi,
       }
       aLightCurr->SetHeadlight (isHeadLight);
     }
+    else if (anArgCase.IsEqual ("-CASTSHADOW")
+          || anArgCase.IsEqual ("-CASTSHADOWS")
+          || anArgCase.IsEqual ("-SHADOWS"))
+    {
+      if (aLightCurr.IsNull()
+       || aLightCurr->Type() == Graphic3d_TOLS_AMBIENT)
+      {
+        Message::SendFail() << "Syntax error at argument '" << anArg << "'";
+        return 1;
+      }
+
+      bool toCastShadows = true;
+      if (anArgIt + 1 < theArgsNb
+       && Draw::ParseOnOff (theArgVec[anArgIt + 1], toCastShadows))
+      {
+        ++anArgIt;
+      }
+      aLightCurr->SetCastShadows (toCastShadows);
+    }
     else
     {
       Message::SendFail() << "Warning: unknown argument '" << anArg << "'";
@@ -11541,6 +11563,8 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
     theDI << "rayDepth:       " <<  aParams.RaytracingDepth                             << "\n";
     theDI << "fsaa:           " << (aParams.IsAntialiasingEnabled       ? "on" : "off") << "\n";
     theDI << "shadows:        " << (aParams.IsShadowEnabled             ? "on" : "off") << "\n";
+    theDI << "shadowMapRes:   " <<  aParams.ShadowMapResolution                         << "\n";
+    theDI << "shadowMapBias:  " <<  aParams.ShadowMapBias                               << "\n";
     theDI << "reflections:    " << (aParams.IsReflectionEnabled         ? "on" : "off") << "\n";
     theDI << "gleam:          " << (aParams.IsTransparentShadowEnabled  ? "on" : "off") << "\n";
     theDI << "GI:             " << (aParams.IsGlobalIlluminationEnabled ? "on" : "off") << "\n";
@@ -11912,6 +11936,37 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       }
       aParams.IsShadowEnabled = toEnable;
     }
+    else if (aFlag == "-shadowmapresolution"
+          || aFlag == "-shadowmap")
+    {
+      if (toPrint)
+      {
+        theDI << aParams.ShadowMapResolution << " ";
+        continue;
+      }
+      else if (++anArgIter >= theArgNb)
+      {
+        Message::SendFail() << "Syntax error at argument '" << anArg << "'";
+        return 1;
+      }
+
+      aParams.ShadowMapResolution = Draw::Atoi (theArgVec[anArgIter]);
+    }
+    else if (aFlag == "-shadowmapbias")
+    {
+      if (toPrint)
+      {
+        theDI << aParams.ShadowMapBias << " ";
+        continue;
+      }
+      else if (++anArgIter >= theArgNb)
+      {
+        Message::SendFail() << "Syntax error at argument '" << anArg << "'";
+        return 1;
+      }
+
+      aParams.ShadowMapBias = (float )Draw::Atof (theArgVec[anArgIter]);
+    }
     else if (aFlag == "-refl"
           || aFlag == "-reflections")
     {
@@ -14824,6 +14879,7 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n        -{dir}ection X Y Z (for directional light or for spotlight)"
     "\n        -color colorName"
     "\n        -{head}light 0|1"
+    "\n        -castShadows 0|1"
     "\n        -{sm}oothness value"
     "\n        -{int}ensity value"
     "\n        -{constAtten}uation value"
@@ -14854,6 +14910,7 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n\t\t: vrenderparams [-raster] [-shadingModel {unlit|facet|gouraud|phong|pbr|pbr_facet}=gouraud]"
     "\n\t\t:               [-msaa 0..8=0] [-rendScale scale=1] [-resolution value=72]"
     "\n\t\t:               [-oit {off|0.0-1.0}=off]"
+    "\n\t\t:               [-shadows {on|off}=on] [-shadowMapResolution value=1024] [-shadowMapBias value=0.005]"
     "\n\t\t:               [-depthPrePass {on|off}=off] [-alphaToCoverage {on|off}=on]"
     "\n\t\t:               [-frustumCulling {on|off|noupdate}=on] [-lineFeather width=1.0]"
     "\n\t\t:               [-sync {default|views}] [-reset]"
@@ -14867,6 +14924,9 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n\t\t:   -oit             Enables/disables order-independent transparency (OIT) rendering;"
     "\n\t\t:                    weight OIT fixes transparency artifacts at the cost of blurry result,"
     "\n\t\t:                    it is managed by depth weight factor (0.0 value also enables weight OIT)."
+    "\n\t\t:   -shadows         Enables/disables shadows rendering."
+    "\n\t\t:   -shadowMapResolution Shadow texture map resolution."
+    "\n\t\t:   -shadowMapBias   Shadow map bias."
     "\n\t\t:   -depthPrePass    Enables/disables depth pre-pass."
     "\n\t\t:   -frustumCulling  Enables/disables objects frustum clipping or"
     "\n\t\t:                    sets state to check structures culled previously."
@@ -14881,7 +14941,7 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n\t\t:   -perfChart          Show frame timers chart limited by specified number of frames."
     "\n\t\t:   -perfChartMax       Maximum time in seconds with the chart."
     "\n\t\t: Ray-Tracing options:"
-    "\n\t\t: vrenderparams [-rayTrace] [-rayDepth {0..10}=3] [-shadows {on|off}=on] [-reflections {on|off}=off]"
+    "\n\t\t: vrenderparams [-rayTrace] [-rayDepth {0..10}=3] [-reflections {on|off}=off]"
     "\n\t\t:               [-fsaa {on|off}=off] [-gleam {on|off}=off] [-env {on|off}=off]"
     "\n\t\t:               [-gi {on|off}=off] [-brng {on|off}=off]"
     "\n\t\t:               [-iss {on|off}=off] [-tileSize {1..4096}=32] [-nbTiles {64..1024}=256]"
@@ -14891,7 +14951,6 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n\t\t:               [-exposure value=0.0] [-whitePoint value=1.0] [-toneMapping {disabled|filmic}=disabled]"
     "\n\t\t:   -rayTrace     Enables  GPU ray-tracing."
     "\n\t\t:   -rayDepth     Defines maximum ray-tracing depth."
-    "\n\t\t:   -shadows      Enables/disables shadows rendering."
     "\n\t\t:   -reflections  Enables/disables specular reflections."
     "\n\t\t:   -fsaa         Enables/disables adaptive anti-aliasing."
     "\n\t\t:   -gleam        Enables/disables transparency shadow effects."
index 27ea80d..bd75d24 100755 (executable)
@@ -22,3 +22,4 @@
 023 viewcube
 024 colors
 025 quadric
+026 shadows
diff --git a/tests/v3d/shadows/buggy b/tests/v3d/shadows/buggy
new file mode 100644 (file)
index 0000000..a5806f6
--- /dev/null
@@ -0,0 +1,30 @@
+puts "========"
+puts "0032039: Visualization, TKOpenGl - implement simple shadow mapping for a direct light source"
+puts "Test shadow map from single directional light source on a buggy."
+puts "========"
+
+pload MODELING VISUALIZATION XDE OCAF
+if { $::tcl_platform(os) == "Darwin" } { vcaps -core }
+catch {Close D}
+ReadGltf D [locate_data_file bug30691_Buggy.glb]
+vclear
+vinit View1
+vzbufftrihedron
+XDisplay -dispMode 1 D
+vfit
+vzoom 0.75
+box bb -500000 -500000 -10875 1000000 1000000 0 -preview
+vdisplay -dispMode 1 bb
+vaspects bb -material STONE
+vlight -change 0 -head 0 -dir -1 -1 -1 -castShadows 1
+
+vraytrace 1
+vdump $::imagedir/${::casename}_raytrace.png
+
+vraytrace 0
+vrenderparams -shadingModel phong
+vrenderparams -shadowMapResolution 2048
+vdump $::imagedir/${::casename}_phong.png
+
+vrenderparams -shadingModel pbr
+vdump $::imagedir/${::casename}_pbr.png
diff --git a/tests/v3d/shadows/dir1 b/tests/v3d/shadows/dir1
new file mode 100644 (file)
index 0000000..2a58e52
--- /dev/null
@@ -0,0 +1,30 @@
+puts "========"
+puts "0032039: Visualization, TKOpenGl - implement simple shadow mapping for a direct light source"
+puts "Test shadow map from a single directional light source on a box geometry."
+puts "========"
+
+pload MODELING VISUALIZATION
+if { $::tcl_platform(os) == "Darwin" } { vcaps -core }
+box b 1 2 3
+box bb -5 -5 0 10 10 0 -preview
+vgldebug 1
+vcaps -core
+vcaps -vsync 0
+vclear
+vinit View1
+vrenderparams -shadingModel PHONG
+vdisplay -dispMode 1 b bb
+vaspects bb -material STONE
+vfit
+vlight -change 0 -castShadows 1 -direction 1 1 -1 -head 0
+
+vraytrace 1
+vdump $::imagedir/${::casename}_raytrace.png
+
+vraytrace 0
+vrenderparams -shadingModel phong
+vrenderparams -shadowMapBias 0.01
+vdump $::imagedir/${::casename}_phong.png
+
+vrenderparams -shadingModel pbr
+vdump $::imagedir/${::casename}_pbr.png
diff --git a/tests/v3d/shadows/dir2 b/tests/v3d/shadows/dir2
new file mode 100644 (file)
index 0000000..ceefcbb
--- /dev/null
@@ -0,0 +1,34 @@
+puts "========"
+puts "0032039: Visualization, TKOpenGl - implement simple shadow mapping for a direct light source"
+puts "Test shadow map from two directional light sources on a box geometry."
+puts "========"
+
+pload MODELING VISUALIZATION
+if { $::tcl_platform(os) == "Darwin" } { vcaps -core }
+box b 1 2 3
+box bb -5 -5 0 10 10 0 -preview
+vgldebug 1
+vcaps -core
+vcaps -vsync 0
+vclear
+vinit View1
+vrenderparams -shadingModel PHONG
+vdisplay -dispMode 1 b bb
+vaspects bb -material STONE
+vfit
+
+vlight -clear
+vlight -add AMBIENT
+vlight -add DIRECTIONAL -direction  0.2  0.2 -1 -head 0 -castShadows 1 -color RED
+vlight -add DIRECTIONAL -direction -0.2 -0.2 -1 -head 0 -castShadows 1 -color GREEN
+
+vraytrace 1
+vdump $::imagedir/${::casename}_raytrace.png
+
+vraytrace 0
+vrenderparams -shadingModel phong
+vrenderparams -shadowMapBias 0.01
+vdump $::imagedir/${::casename}_phong.png
+
+vrenderparams -shadingModel pbr
+vdump $::imagedir/${::casename}_pbr.png
diff --git a/tests/v3d/shadows/dirhead b/tests/v3d/shadows/dirhead
new file mode 100644 (file)
index 0000000..152673b
--- /dev/null
@@ -0,0 +1,31 @@
+puts "========"
+puts "0032039: Visualization, TKOpenGl - implement simple shadow mapping for a direct light source"
+puts "Test shadow map from a single directional light source with headlight flag."
+puts "========"
+
+pload MODELING VISUALIZATION
+if { $::tcl_platform(os) == "Darwin" } { vcaps -core }
+box b 1 2 3
+box bb -5 -5 0 10 10 0 -preview
+vgldebug 1
+vcaps -core
+vcaps -vsync 0
+vclear
+vinit View1
+vcamera -persp
+vrenderparams -shadingModel PHONG
+vdisplay -dispMode 1 b bb
+vaspects bb -material STONE
+vfit
+vlight -change 0 -castShadows 1 -direction -0.2 0.2 -1 -head 1
+
+vraytrace 1
+vdump $::imagedir/${::casename}_raytrace.png
+
+vraytrace 0
+vrenderparams -shadingModel phong
+vrenderparams -shadowMapBias 0.01
+vdump $::imagedir/${::casename}_phong.png
+
+vrenderparams -shadingModel pbr
+vdump $::imagedir/${::casename}_pbr.png