diff --git a/gazebo/rendering/Heightmap.cc b/gazebo/rendering/Heightmap.cc index 6611ac4c54..dc1b913e8e 100644 --- a/gazebo/rendering/Heightmap.cc +++ b/gazebo/rendering/Heightmap.cc @@ -3341,27 +3341,34 @@ Ogre::MaterialPtr TerrainMaterial::Profile::generate( // set up shadow split points in a way that is consistent with the // default ogre terrain material generator - Ogre::PSSMShadowCameraSetup* pssm = - RTShaderSystem::Instance()->GetPSSMShadowCameraSetup(); - unsigned int numTextures = - static_cast(pssm->getSplitCount()); - Ogre::Vector4 splitPoints; - const Ogre::PSSMShadowCameraSetup::SplitPointList& splitPointList = - pssm->getSplitPoints(); - // populate from split point 1 not 0, and include shadowFarDistance - for (unsigned int t = 0u; t < numTextures; ++t) - splitPoints[t] = splitPointList[t+1]; - params->setNamedConstant("pssmSplitPoints", splitPoints); - - // set up uv transform - double xTrans = static_cast(gridCount / gridWidth) * factor; - double yTrans = (gridWidth - 1 - (gridCount % gridWidth)) * factor; - // explicitly set all matrix elements to avoid uninitialized values - Ogre::Matrix4 uvTransform(factor, 0.0, 0.0, xTrans, - 0.0, factor, 0.0, yTrans, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0); - params->setNamedConstant("uvTransform", uvTransform); + + if (params->_findNamedConstantDefinition("pssmSplitPoints")) + { + Ogre::PSSMShadowCameraSetup* pssm = + RTShaderSystem::Instance()->GetPSSMShadowCameraSetup(); + unsigned int numTextures = + static_cast(pssm->getSplitCount()); + Ogre::Vector4 splitPoints; + const Ogre::PSSMShadowCameraSetup::SplitPointList& splitPointList = + pssm->getSplitPoints(); + // populate from split point 1 not 0, and include shadowFarDistance + for (unsigned int t = 0u; t < numTextures; ++t) + splitPoints[t] = splitPointList[t+1]; + params->setNamedConstant("pssmSplitPoints", splitPoints); + } + + if (params->_findNamedConstantDefinition("uvTransform")) + { + // set up uv transform + double xTrans = static_cast(gridCount / gridWidth) * factor; + double yTrans = (gridWidth - 1 - (gridCount % gridWidth)) * factor; + // explicitly set all matrix elements to avoid uninitialized values + Ogre::Matrix4 uvTransform(factor, 0.0, 0.0, xTrans, + 0.0, factor, 0.0, yTrans, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); + params->setNamedConstant("uvTransform", uvTransform); + } } } gridCount++; diff --git a/gazebo/rendering/Light.cc b/gazebo/rendering/Light.cc index f1f45f3bde..3f31b06341 100644 --- a/gazebo/rendering/Light.cc +++ b/gazebo/rendering/Light.cc @@ -30,6 +30,7 @@ #include "gazebo/rendering/Visual.hh" #include "gazebo/rendering/Light.hh" #include "gazebo/rendering/LightPrivate.hh" +#include "gazebo/rendering/RTShaderSystem.hh" using namespace gazebo; using namespace rendering; @@ -573,12 +574,29 @@ void Light::SetRange(const double _range) ////////////////////////////////////////////////// void Light::SetCastShadows(const bool _cast) { + if (this->dataPtr->light->getType() == Ogre::Light::LT_DIRECTIONAL) { + // directional light uses PSSM shadow camera and should already be + // configured in RTShaderSystem + this->dataPtr->light->setCastShadows(_cast); + } + else if (this->dataPtr->light->getType() == Ogre::Light::LT_SPOTLIGHT) + { + // use different shadow camera for spot light this->dataPtr->light->setCastShadows(_cast); + if (_cast && this->dataPtr->shadowCameraSetup.isNull()) + { + this->dataPtr->shadowCameraSetup = + Ogre::ShadowCameraSetupPtr(new Ogre::FocusedShadowCameraSetup()); + this->dataPtr->light->setCustomShadowCameraSetup( + this->dataPtr->shadowCameraSetup); + RTShaderSystem::Instance()->UpdateShadows(); + } } else { + // todo(anyone) make point light casts shadows this->dataPtr->light->setCastShadows(false); } } diff --git a/gazebo/rendering/LightPrivate.hh b/gazebo/rendering/LightPrivate.hh index 15253379e5..0162a8c000 100644 --- a/gazebo/rendering/LightPrivate.hh +++ b/gazebo/rendering/LightPrivate.hh @@ -38,7 +38,7 @@ namespace gazebo class LightPrivate { /// \brief The ogre light source - public: Ogre::Light *light; + public: Ogre::Light *light = nullptr; /// \brief The visual used to visualize the light. public: VisualPtr visual; @@ -63,6 +63,9 @@ namespace gazebo /// \brief Counter used to generate unique light names. public: static unsigned int lightCounter; + + /// \brief Custom shadow camera setup for non-directional lights + public: Ogre::ShadowCameraSetupPtr shadowCameraSetup; }; } } diff --git a/gazebo/rendering/RTShaderSystem.cc b/gazebo/rendering/RTShaderSystem.cc index 132409c293..c2f8172af1 100644 --- a/gazebo/rendering/RTShaderSystem.cc +++ b/gazebo/rendering/RTShaderSystem.cc @@ -41,6 +41,7 @@ #include "gazebo/common/SystemPaths.hh" #include "gazebo/rendering/ogre_gazebo.h" #include "gazebo/rendering/CustomPSSMShadowCameraSetup.hh" +#include "gazebo/rendering/Light.hh" #include "gazebo/rendering/RenderEngine.hh" #include "gazebo/rendering/Scene.hh" #include "gazebo/rendering/Visual.hh" @@ -242,6 +243,7 @@ void RTShaderSystem::DetachViewport(Ogre::Viewport *_viewport, ScenePtr _scene) void RTShaderSystem::UpdateShaders() { // shaders will be updated in the Update call on pre-render event. + std::lock_guard lock(this->dataPtr->updateMutex); this->dataPtr->updateShaders = true; } @@ -520,57 +522,7 @@ void RTShaderSystem::ApplyShadows(ScenePtr _scene) this->dataPtr->shaderGenerator->getRenderState(_scene->Name() + Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME); - sceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_TEXTURE_ADDITIVE_INTEGRATED); - - // 3 textures per directional light - sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_DIRECTIONAL, 3); - sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_POINT, 0); - sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_SPOTLIGHT, 0); - sceneMgr->setShadowTextureCount(3); - - unsigned int texSize = this->dataPtr->shadowTextureSize; -#if defined(__APPLE__) - // workaround a weird but on OSX if texture size at 2 and 3 splits are not - // halved - texSize = this->dataPtr->shadowTextureSize/2; -#endif - sceneMgr->setShadowTextureConfig(0, - this->dataPtr->shadowTextureSize, this->dataPtr->shadowTextureSize, - Ogre::PF_FLOAT32_R); - sceneMgr->setShadowTextureConfig(1, texSize, texSize, Ogre::PF_FLOAT32_R); - sceneMgr->setShadowTextureConfig(2, texSize, texSize, Ogre::PF_FLOAT32_R); - -#if defined(HAVE_OPENGL) - // Enable shadow map comparison, so shader can use - // float texture(sampler2DShadow, vec3, [float]) instead of - // vec4 texture(sampler2D, vec2, [float]). - // NVidia, AMD, and Intel all take this as a cue to provide "hardware PCF", - // a driver hack that softens shadow edges with 4-sample interpolation. - for (size_t i = 0; i < sceneMgr->getShadowTextureCount(); ++i) - { - const Ogre::TexturePtr tex = sceneMgr->getShadowTexture(i); - // This will fail if not using OpenGL as the rendering backend. - GLuint texId; - tex->getCustomAttribute("GLID", &texId); - glBindTexture(GL_TEXTURE_2D, texId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, - GL_COMPARE_R_TO_TEXTURE); - } -#endif - - sceneMgr->setShadowTextureSelfShadow(false); - sceneMgr->setShadowCasterRenderBackFaces(true); - - // TODO: We have two different shadow caster materials, both taken from - // OGRE samples. They should be compared and tested. - // Set up caster material - this is just a standard depth/shadow map caster - // sceneMgr->setShadowTextureCasterMaterial("PSSM/shadow_caster"); -#if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 11 - sceneMgr->setShadowTextureCasterMaterial( - Ogre::MaterialManager::getSingleton().getByName("Gazebo/shadow_caster")); -#else - sceneMgr->setShadowTextureCasterMaterial("Gazebo/shadow_caster"); -#endif + this->UpdateShadows(_scene); // Disable fog on the caster pass. // Ogre::MaterialPtr passCaterMaterial = @@ -649,18 +601,39 @@ Ogre::PSSMShadowCameraSetup *RTShaderSystem::GetPSSMShadowCameraSetup() const void RTShaderSystem::Update() { IGN_PROFILE("rendering::RTShaderSystem::Update"); - if (!this->dataPtr->initialized || !this->dataPtr->updateShaders) + if (!this->dataPtr->initialized) + { return; + } + + bool updateShaders, updateShadows = false; + { + std::lock_guard lock(this->dataPtr->updateMutex); + updateShaders = this->dataPtr->updateShaders; + updateShadows = this->dataPtr->updateShadows; + this->dataPtr->updateShaders = false; + this->dataPtr->updateShadows = false; + } - for (const auto &scene : this->dataPtr->scenes) + if (updateShadows) { - VisualPtr vis = scene->WorldVisual(); - if (vis) + for (const auto &scene : this->dataPtr->scenes) { - this->UpdateShaders(vis); + this->UpdateShadows(scene); + } + } + + if (updateShaders) + { + for (const auto &scene : this->dataPtr->scenes) + { + VisualPtr vis = scene->WorldVisual(); + if (vis) + { + this->UpdateShaders(vis); + } } } - this->dataPtr->updateShaders = false; } ///////////////////////////////////////////////// @@ -728,3 +701,97 @@ double RTShaderSystem::ShadowSplitPadding() const { return this->dataPtr->shadowSplitPadding; } + +///////////////////////////////////////////////// +void RTShaderSystem::UpdateShadows() +{ + std::lock_guard lock(this->dataPtr->updateMutex); + this->dataPtr->updateShadows = true; +} + +///////////////////////////////////////////////// +void RTShaderSystem::UpdateShadows(ScenePtr _scene) +{ + if (!this->dataPtr->initialized) + return; + + Ogre::SceneManager *sceneMgr = _scene->OgreSceneManager(); + + sceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_TEXTURE_ADDITIVE_INTEGRATED); + + // directional: assume there can only be one dir light and we always create + // the shadow map for the dir light + // spot: update number of shadow textures based on number of shadow casting + // spot lights + // point: not working yet + unsigned int dirLightCount = 1u; + unsigned int spotLightCount = 0u; + for (unsigned int i = 0; i < _scene->LightCount(); ++i) + { + LightPtr light = _scene->LightByIndex(i); + + if (!light->CastShadows()) + continue; + + if (light->Type() == "spot") + spotLightCount++; + } + + // 3 textures per directional light + sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_DIRECTIONAL, 3); + + // spot light shadow count + sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_SPOTLIGHT, 1); + + // \todo(anyone) make point light shadows work + sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_POINT, 0); + + // \todo(anyone) include point light shadows when it is working + unsigned int dirShadowCount = 3 * dirLightCount; + unsigned int spotShadowCount = spotLightCount; + sceneMgr->setShadowTextureCount(dirShadowCount + spotShadowCount); + + unsigned int texSize = this->dataPtr->shadowTextureSize; +#if defined(__APPLE__) + // workaround a weird but on OSX if texture size at 2 and 3 splits are not + // halved + texSize = this->dataPtr->shadowTextureSize/2; +#endif + sceneMgr->setShadowTextureConfig(0, + this->dataPtr->shadowTextureSize, this->dataPtr->shadowTextureSize, + Ogre::PF_FLOAT32_R); + sceneMgr->setShadowTextureConfig(1, texSize, texSize, Ogre::PF_FLOAT32_R); + sceneMgr->setShadowTextureConfig(2, texSize, texSize, Ogre::PF_FLOAT32_R); + +#if defined(HAVE_OPENGL) + // Enable shadow map comparison, so shader can use + // float texture(sampler2DShadow, vec3, [float]) instead of + // vec4 texture(sampler2D, vec2, [float]). + // NVidia, AMD, and Intel all take this as a cue to provide "hardware PCF", + // a driver hack that softens shadow edges with 4-sample interpolation. + for (size_t i = 0u; i < dirShadowCount; ++i) + { + const Ogre::TexturePtr tex = sceneMgr->getShadowTexture(i); + // This will fail if not using OpenGL as the rendering backend. + GLuint texId; + tex->getCustomAttribute("GLID", &texId); + glBindTexture(GL_TEXTURE_2D, texId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, + GL_COMPARE_R_TO_TEXTURE); + } +#endif + + sceneMgr->setShadowTextureSelfShadow(false); + sceneMgr->setShadowCasterRenderBackFaces(true); + + // TODO: We have two different shadow caster materials, both taken from + // OGRE samples. They should be compared and tested. + // Set up caster material - this is just a standard depth/shadow map caster + // sceneMgr->setShadowTextureCasterMaterial("PSSM/shadow_caster"); +#if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 11 + sceneMgr->setShadowTextureCasterMaterial( + Ogre::MaterialManager::getSingleton().getByName("Gazebo/shadow_caster")); +#else + sceneMgr->setShadowTextureCasterMaterial("Gazebo/shadow_caster"); +#endif +} diff --git a/gazebo/rendering/RTShaderSystem.hh b/gazebo/rendering/RTShaderSystem.hh index ffd292ca43..61da4d25ee 100644 --- a/gazebo/rendering/RTShaderSystem.hh +++ b/gazebo/rendering/RTShaderSystem.hh @@ -91,6 +91,9 @@ namespace gazebo /// \brief Queue a call to update the shaders. public: void UpdateShaders(); + /// \brief Queue a call to update the shadows. + public: void UpdateShadows(); + /// \brief Set a viewport to use shaders. /// \param[in] _viewport The viewport to add. /// \param[in] _scene The scene that the viewport uses. @@ -177,6 +180,10 @@ namespace gazebo /// \param[in] _vis Pointer to the visual to update. private: void UpdateShaders(VisualPtr _vis); + /// \brief Update the shadows for a scene + /// \param[in] _scene Pointer to the scene to update + private: void UpdateShadows(ScenePtr _scene); + /// \brief Re-apply shadows. Call this if a shadow paramenter is changed. private: void ReapplyShadows(); diff --git a/gazebo/rendering/RTShaderSystemPrivate.hh b/gazebo/rendering/RTShaderSystemPrivate.hh index 7899d444a3..15d1b277fa 100644 --- a/gazebo/rendering/RTShaderSystemPrivate.hh +++ b/gazebo/rendering/RTShaderSystemPrivate.hh @@ -18,6 +18,7 @@ #ifndef _GAZEBO_RTSHADERSYSTEM_PRIVATE_HH_ #define _GAZEBO_RTSHADERSYSTEM_PRIVATE_HH_ +#include #include #include @@ -56,6 +57,9 @@ namespace gazebo /// \brief Flag to indicate that shaders need to be updated. public: bool updateShaders; + /// \brief Flag to indicate that shadows need to be updated. + public: bool updateShadows = false; + /// \brief Size of the Parallel Split Shadow Map (PSSM) shadow texture /// at closest layer. public: unsigned int shadowTextureSize = 2048u; @@ -79,6 +83,9 @@ namespace gazebo /// \brief Flag to indicate if normal map should be enabled public: bool enableNormalMap = true; + + /// \brief Mutex to protect shaders and shadows update + public: std::mutex updateMutex; }; } } diff --git a/gazebo/rendering/RenderingLight_TEST.cc b/gazebo/rendering/RenderingLight_TEST.cc index 7b82fa37a8..d7f2bbaf77 100644 --- a/gazebo/rendering/RenderingLight_TEST.cc +++ b/gazebo/rendering/RenderingLight_TEST.cc @@ -115,8 +115,9 @@ TEST_F(Light_TEST, CastShadows) msg.set_cast_shadows(true); spotLight->LoadFromMsg(msg); EXPECT_EQ(spotLight->LightType(), "spot"); - // issue #2083: spot light does not cast shadows - EXPECT_FALSE(spotLight->CastShadows()); + // issue #2083: spot light generates shadow maps but they are not currently + // being rendered + EXPECT_TRUE(spotLight->CastShadows()); scene->RemoveLight(spotLight); spotLight.reset(); diff --git a/media/materials/programs/CMakeLists.txt b/media/materials/programs/CMakeLists.txt index f63e65c6d8..5374739d76 100644 --- a/media/materials/programs/CMakeLists.txt +++ b/media/materials/programs/CMakeLists.txt @@ -45,6 +45,8 @@ projector.vert shadow_caster_fp.glsl shadow_caster_vp.glsl StdQuad_vp.glsl +spotlight_shadow_demo_fp.glsl +spotlight_shadow_demo_vp.glsl spot_shadow_receiver_fp.glsl spot_shadow_receiver_vp.glsl wide_lens_map_fp.glsl diff --git a/media/materials/programs/spotlight_shadow_demo_fp.glsl b/media/materials/programs/spotlight_shadow_demo_fp.glsl new file mode 100644 index 0000000000..1508127845 --- /dev/null +++ b/media/materials/programs/spotlight_shadow_demo_fp.glsl @@ -0,0 +1,44 @@ +#version 120 + +uniform sampler2D shadowMap0; +varying vec4 lightSpacePos0; + +varying vec4 worldPos; +varying vec4 worldViewPos; + +//------------------------------------------------------------------------------ +float ShadowSimple(in sampler2D shadowMap, in vec4 shadowMapPos) +{ + // perform perspective divide + vec3 shadowMapUV = shadowMapPos.xyz / shadowMapPos.w; + + if (shadowMapUV.z < 0.0 || shadowMapUV.z > 1.0) + return 0.0; + + // get closest depth value from light's perspective + float closestDepth = texture2D(shadowMap, shadowMapUV.xy).r; + + // get depth of current fragment from light's perspective + float currentDepth = shadowMapUV.z; + + // check whether current frag pos is in shadow + float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + + return shadow; +} + +void main() +{ + float f = 0.0f; + + // flat red color - no lighting + vec4 outputCol = vec4(1.0, 0.0, 0.0, 1.0); + + // grey shadows + f += ShadowSimple(shadowMap0, lightSpacePos0); + f = clamp(f, 0.0f, 1.0f); + if (f > 0.0f) + outputCol = vec4(0.2, 0.2, 0.2, 1.0); + + gl_FragColor = outputCol; +} diff --git a/media/materials/programs/spotlight_shadow_demo_vp.glsl b/media/materials/programs/spotlight_shadow_demo_vp.glsl new file mode 100644 index 0000000000..71dc3e6e18 --- /dev/null +++ b/media/materials/programs/spotlight_shadow_demo_vp.glsl @@ -0,0 +1,22 @@ +#version 120 + +uniform mat4 worldMatrix; +uniform mat4 worldViewMatrix; +uniform mat4 viewProjMatrix; + +uniform mat4 texViewProjMatrix0; +varying vec4 lightSpacePos0; + +varying vec4 worldPos; +varying vec4 worldViewPos; + +void main() +{ + worldPos = worldMatrix * gl_Vertex; + gl_Position = viewProjMatrix * worldPos; + + worldViewPos = worldViewMatrix * gl_Vertex; + + lightSpacePos0 = texViewProjMatrix0 * worldPos; +} + diff --git a/media/materials/scripts/CMakeLists.txt b/media/materials/scripts/CMakeLists.txt index 9e4200dec6..f72e9f718e 100644 --- a/media/materials/scripts/CMakeLists.txt +++ b/media/materials/scripts/CMakeLists.txt @@ -21,6 +21,7 @@ picker.material ShadowCaster.material shadow_caster.program ShadowCaster.program +spotlight_shadow_demo.material ssao.compositor ssao.material SSAOPost.compositor diff --git a/media/materials/scripts/spotlight_shadow_demo.material b/media/materials/scripts/spotlight_shadow_demo.material new file mode 100644 index 0000000000..061f1f1c64 --- /dev/null +++ b/media/materials/scripts/spotlight_shadow_demo.material @@ -0,0 +1,48 @@ +vertex_program Gazebo/SpotLightShadowDemoVS glsl +{ + source spotlight_shadow_demo_vp.glsl + + default_params + { + param_named_auto worldViewMatrix worldview_matrix + param_named_auto viewProjMatrix viewproj_matrix + param_named_auto worldMatrix world_matrix + + param_named_auto texViewProjMatrix0 texture_viewproj_matrix 0 + } +} + +fragment_program Gazebo/SpotLightShadowDemoFS glsl +{ + source spotlight_shadow_demo_fp.glsl + + default_params + { + param_named shadowMap0 int 0 + } +} + +material Gazebo/SpotLightShadowDemo +{ + technique + { + pass + { + max_lights 32 + iteration 1 per_n_lights 1 spot + + vertex_program_ref Gazebo/SpotLightShadowDemoVS + { + } + + fragment_program_ref Gazebo/SpotLightShadowDemoFS + { + } + texture_unit shadowMap0 + { + content_type shadow + tex_address_mode clamp + } + } + } +} diff --git a/worlds/spotlight_shadows_demo.world b/worlds/spotlight_shadows_demo.world new file mode 100644 index 0000000000..ba3da631d5 --- /dev/null +++ b/worlds/spotlight_shadows_demo.world @@ -0,0 +1,112 @@ + + + + + + + + + + model://ground_plane + + + + model://sun + + + + + true + -3.0 0.0 3.0 0 0 0 + 0.8 0.8 0.8 1 + 0.2 0.2 0.2 1 + + 10 + 0.05 + 0.01 + 0.001 + + + 0.1 + 0.5 + 1.2 + + 1 0 -1 + + + + 0 0 0.1 0 0 0 + true + + + + + 0 0 1 + 10 10 + + + + + 0xffff + + + + 100 + 50 + + + + + + false + + + 0 0 1 + 10 10 + + + + + + + + + + + + 0 0 0.5 0 0 0 + + + + + 1 1 1 + + + + + + + 1 1 1 + + + + + + + + + + +