From d84e866973bad1be1c92c17371691a2c0d9a9532 Mon Sep 17 00:00:00 2001 From: kgv Date: Mon, 4 Jan 2021 12:17:44 +0300 Subject: [PATCH] 0032039: Visualization, TKOpenGl - implement simple shadow mapping for a direct light source 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. --- src/Graphic3d/Graphic3d_CLight.cxx | 18 +- src/Graphic3d/Graphic3d_CLight.hxx | 8 + src/Graphic3d/Graphic3d_LightSet.cxx | 12 +- src/Graphic3d/Graphic3d_LightSet.hxx | 12 ++ src/Graphic3d/Graphic3d_RenderingParams.hxx | 4 + src/Graphic3d/Graphic3d_ShaderProgram.cxx | 1 + src/Graphic3d/Graphic3d_ShaderProgram.hxx | 7 + src/Graphic3d/Graphic3d_TextureUnit.hxx | 4 + src/OpenGl/FILES | 2 + src/OpenGl/OpenGl_Context.cxx | 2 + src/OpenGl/OpenGl_Context.hxx | 4 + src/OpenGl/OpenGl_FrameBuffer.hxx | 4 +- src/OpenGl/OpenGl_FrameStats.cxx | 3 + src/OpenGl/OpenGl_LayerList.cxx | 32 +++- src/OpenGl/OpenGl_ShaderManager.cxx | 163 ++++++++++++++--- src/OpenGl/OpenGl_ShaderManager.hxx | 24 ++- src/OpenGl/OpenGl_ShaderProgram.cxx | 37 ++++ src/OpenGl/OpenGl_ShaderProgram.hxx | 14 ++ src/OpenGl/OpenGl_ShaderStates.hxx | 21 ++- src/OpenGl/OpenGl_ShadowMap.cxx | 171 ++++++++++++++++++ src/OpenGl/OpenGl_ShadowMap.hxx | 115 ++++++++++++ src/OpenGl/OpenGl_Structure.cxx | 5 + src/OpenGl/OpenGl_View.cxx | 153 +++++++++++++++- src/OpenGl/OpenGl_View.hxx | 8 + src/Shaders/DirectionalLightShadow.glsl | 27 +++ src/Shaders/FILES | 2 + .../Shaders_DirectionalLightShadow_glsl.pxx | 30 +++ src/ViewerTest/ViewerTest_ViewerCommands.cxx | 63 ++++++- tests/v3d/grids.list | 1 + tests/v3d/shadows/buggy | 30 +++ tests/v3d/shadows/dir1 | 30 +++ tests/v3d/shadows/dir2 | 34 ++++ tests/v3d/shadows/dirhead | 31 ++++ 33 files changed, 1025 insertions(+), 47 deletions(-) create mode 100644 src/OpenGl/OpenGl_ShadowMap.cxx create mode 100644 src/OpenGl/OpenGl_ShadowMap.hxx create mode 100644 src/Shaders/DirectionalLightShadow.glsl create mode 100644 src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx create mode 100644 tests/v3d/shadows/buggy create mode 100644 tests/v3d/shadows/dir1 create mode 100644 tests/v3d/shadows/dir2 create mode 100644 tests/v3d/shadows/dirhead diff --git a/src/Graphic3d/Graphic3d_CLight.cxx b/src/Graphic3d/Graphic3d_CLight.cxx index b46de002b6..182cbe06c1 100644 --- a/src/Graphic3d/Graphic3d_CLight.cxx +++ b/src/Graphic3d/Graphic3d_CLight.cxx @@ -14,6 +14,7 @@ #include #include +#include #include 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 : diff --git a/src/Graphic3d/Graphic3d_CLight.hxx b/src/Graphic3d/Graphic3d_CLight.hxx index b2218ff877..12fbd73923 100644 --- a/src/Graphic3d/Graphic3d_CLight.hxx +++ b/src/Graphic3d/Graphic3d_CLight.hxx @@ -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 }; diff --git a/src/Graphic3d/Graphic3d_LightSet.cxx b/src/Graphic3d/Graphic3d_LightSet.cxx index 0b51b700fd..61dcca9b0d 100644 --- a/src/Graphic3d/Graphic3d_LightSet.cxx +++ b/src/Graphic3d/Graphic3d_LightSet.cxx @@ -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 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'; diff --git a/src/Graphic3d/Graphic3d_LightSet.hxx b/src/Graphic3d/Graphic3d_LightSet.hxx index 418cdccb2a..32be5578d9 100644 --- a/src/Graphic3d/Graphic3d_LightSet.hxx +++ b/src/Graphic3d/Graphic3d_LightSet.hxx @@ -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 }; diff --git a/src/Graphic3d/Graphic3d_RenderingParams.hxx b/src/Graphic3d/Graphic3d_RenderingParams.hxx index 472311ff16..7469c9e66e 100644 --- a/src/Graphic3d/Graphic3d_RenderingParams.hxx +++ b/src/Graphic3d/Graphic3d_RenderingParams.hxx @@ -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 diff --git a/src/Graphic3d/Graphic3d_ShaderProgram.cxx b/src/Graphic3d/Graphic3d_ShaderProgram.cxx index aef9958a8b..28c8e7eac5 100755 --- a/src/Graphic3d/Graphic3d_ShaderProgram.cxx +++ b/src/Graphic3d/Graphic3d_ShaderProgram.cxx @@ -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), diff --git a/src/Graphic3d/Graphic3d_ShaderProgram.hxx b/src/Graphic3d/Graphic3d_ShaderProgram.hxx index 218d8a4b5d..fb612f15d9 100755 --- a/src/Graphic3d/Graphic3d_ShaderProgram.hxx +++ b/src/Graphic3d/Graphic3d_ShaderProgram.hxx @@ -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 diff --git a/src/Graphic3d/Graphic3d_TextureUnit.hxx b/src/Graphic3d/Graphic3d_TextureUnit.hxx index f554b8efc4..34d4aa026d 100644 --- a/src/Graphic3d/Graphic3d_TextureUnit.hxx +++ b/src/Graphic3d/Graphic3d_TextureUnit.hxx @@ -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. diff --git a/src/OpenGl/FILES b/src/OpenGl/FILES index f9b42b03f8..5c61112ef6 100755 --- a/src/OpenGl/FILES +++ b/src/OpenGl/FILES @@ -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 diff --git a/src/OpenGl/OpenGl_Context.cxx b/src/OpenGl/OpenGl_Context.cxx index 26bf048cf8..0e294b4721 100644 --- a/src/OpenGl/OpenGl_Context.cxx +++ b/src/OpenGl/OpenGl_Context.cxx @@ -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(myMaxTexCombined + Graphic3d_TextureUnit_PbrIblDiffuseSH); myPBRSpecIBLMapTexUnit = static_cast(myMaxTexCombined + Graphic3d_TextureUnit_PbrIblSpecular); } + myShadowMapTexUnit = static_cast(myMaxTexCombined + Graphic3d_TextureUnit_ShadowMap); } // ======================================================================= diff --git a/src/OpenGl/OpenGl_Context.hxx b/src/OpenGl/OpenGl_Context.hxx index 7c55ba1eba..8e7d65780d 100644 --- a/src/OpenGl/OpenGl_Context.hxx +++ b/src/OpenGl/OpenGl_Context.hxx @@ -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 diff --git a/src/OpenGl/OpenGl_FrameBuffer.hxx b/src/OpenGl/OpenGl_FrameBuffer.hxx index 1fef00a3db..d043967779 100644 --- a/src/OpenGl/OpenGl_FrameBuffer.hxx +++ b/src/OpenGl/OpenGl_FrameBuffer.hxx @@ -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; } diff --git a/src/OpenGl/OpenGl_FrameStats.cxx b/src/OpenGl/OpenGl_FrameStats.cxx index c751d512c9..d4bf258367 100644 --- a/src/OpenGl/OpenGl_FrameStats.cxx +++ b/src/OpenGl/OpenGl_FrameStats.cxx @@ -15,6 +15,7 @@ #include #include +#include #include 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 diff --git a/src/OpenGl/OpenGl_LayerList.cxx b/src/OpenGl/OpenGl_LayerList.cxx index e01146f464..12e2754e1d 100644 --- a/src/OpenGl/OpenGl_LayerList.cxx +++ b/src/OpenGl/OpenGl_LayerList.cxx @@ -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); diff --git a/src/OpenGl/OpenGl_ShaderManager.cxx b/src/OpenGl/OpenGl_ShaderManager.cxx index 857b5c692e..c9b9aad8c6 100644 --- a/src/OpenGl/OpenGl_ShaderManager.cxx +++ b/src/OpenGl/OpenGl_ShaderManager.cxx @@ -21,12 +21,14 @@ #include #include #include +#include #include #include #include #include #include +#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)); diff --git a/src/OpenGl/OpenGl_ShaderManager.hxx b/src/OpenGl/OpenGl_ShaderManager.hxx index f6280fde0a..36558c9c77 100644 --- a/src/OpenGl/OpenGl_ShaderManager.hxx +++ b/src/OpenGl/OpenGl_ShaderManager.hxx @@ -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 myLightTypeArray; mutable NCollection_Array1 myLightParamsArray; + mutable NCollection_Array1 myShadowMatArray; mutable NCollection_Array1 myClipPlaneArray; mutable NCollection_Array1 myClipPlaneArrayFfp; mutable NCollection_Array1 myClipChainArray; diff --git a/src/OpenGl/OpenGl_ShaderProgram.cxx b/src/OpenGl/OpenGl_ShaderProgram.cxx index b858767de0..c7ebf41cdb 100755 --- a/src/OpenGl/OpenGl_ShaderProgram.cxx +++ b/src/OpenGl/OpenGl_ShaderProgram.cxx @@ -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 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 diff --git a/src/OpenGl/OpenGl_ShaderProgram.hxx b/src/OpenGl/OpenGl_ShaderProgram.hxx index 7ed83ff91d..f88eb8a00e 100755 --- a/src/OpenGl/OpenGl_ShaderProgram.hxx +++ b/src/OpenGl/OpenGl_ShaderProgram.hxx @@ -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 diff --git a/src/OpenGl/OpenGl_ShaderStates.hxx b/src/OpenGl/OpenGl_ShaderStates.hxx index 9f25334615..d4fcc96970 100755 --- a/src/OpenGl/OpenGl_ShaderStates.hxx +++ b/src/OpenGl/OpenGl_ShaderStates.hxx @@ -21,6 +21,8 @@ #include #include +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 index 0000000000..53d0a8b792 --- /dev/null +++ b/src/OpenGl/OpenGl_ShadowMap.cxx @@ -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 + +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000000..d192c7a868 --- /dev/null +++ b/src/OpenGl/OpenGl_ShadowMap.hxx @@ -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 +#include +#include +#include + +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 +{ +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 diff --git a/src/OpenGl/OpenGl_Structure.cxx b/src/OpenGl/OpenGl_Structure.cxx index b49bced5ba..ac9e12d93b 100644 --- a/src/OpenGl/OpenGl_Structure.cxx +++ b/src/OpenGl/OpenGl_Structure.cxx @@ -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 diff --git a/src/OpenGl/OpenGl_View.cxx b/src/OpenGl/OpenGl_View.cxx index 449d58fdeb..059c663620 100644 --- a/src/OpenGl/OpenGl_View.cxx +++ b/src/OpenGl/OpenGl_View.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -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)()); // =============================== diff --git a/src/OpenGl/OpenGl_View.hxx b/src/OpenGl/OpenGl_View.hxx index fcac9d3d74..ea421e54aa 100644 --- a/src/OpenGl/OpenGl_View.hxx +++ b/src/OpenGl/OpenGl_View.hxx @@ -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 index 0000000000..555de1e651 --- /dev/null +++ b/src/Shaders/DirectionalLightShadow.glsl @@ -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; +} diff --git a/src/Shaders/FILES b/src/Shaders/FILES index 7c7ada0344..ada8209873 100644 --- a/src/Shaders/FILES +++ b/src/Shaders/FILES @@ -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 index 0000000000..58f5a96fad --- /dev/null +++ b/src/Shaders/Shaders_DirectionalLightShadow_glsl.pxx @@ -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"; diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index fd09f89439..2bdabea387 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -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." diff --git a/tests/v3d/grids.list b/tests/v3d/grids.list index 27ea80d638..bd75d240f6 100755 --- a/tests/v3d/grids.list +++ b/tests/v3d/grids.list @@ -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 index 0000000000..a5806f6f62 --- /dev/null +++ b/tests/v3d/shadows/buggy @@ -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 index 0000000000..2a58e5268b --- /dev/null +++ b/tests/v3d/shadows/dir1 @@ -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 index 0000000000..ceefcbb88e --- /dev/null +++ b/tests/v3d/shadows/dir2 @@ -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 index 0000000000..152673bd88 --- /dev/null +++ b/tests/v3d/shadows/dirhead @@ -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