Skip to content

Commit

Permalink
Generate spot light shadow maps (#2914)
Browse files Browse the repository at this point in the history
Updated gazebo to generate shadow maps for spot lights.
This does not mean the spot lights will now start casting
shadows in gazebo - we still need to update the
RTShaderSystem code to actually render them.
This does however mean that the shadow maps are now
available and we can access them through custom
material shaders.

The spotlight_shadow_demo.world contains a shadow
receiver ground plane with custom material shaders to show
how to access the shadow texture generated by OGRE.

Signed-off-by: Ian Chen <ichen@osrfoundation.org>
  • Loading branch information
iche033 authored and scpeters committed Jan 28, 2021
1 parent ea72e4d commit fc2f8e2
Show file tree
Hide file tree
Showing 13 changed files with 420 additions and 81 deletions.
49 changes: 28 additions & 21 deletions gazebo/rendering/Heightmap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned int>(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<int>(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<unsigned int>(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<int>(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++;
Expand Down
18 changes: 18 additions & 0 deletions gazebo/rendering/Light.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
Expand Down
5 changes: 4 additions & 1 deletion gazebo/rendering/LightPrivate.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
};
}
}
Expand Down
181 changes: 124 additions & 57 deletions gazebo/rendering/RTShaderSystem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<std::mutex> lock(this->dataPtr->updateMutex);
this->dataPtr->updateShaders = true;
}

Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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<std::mutex> 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;
}

/////////////////////////////////////////////////
Expand Down Expand Up @@ -728,3 +701,97 @@ double RTShaderSystem::ShadowSplitPadding() const
{
return this->dataPtr->shadowSplitPadding;
}

/////////////////////////////////////////////////
void RTShaderSystem::UpdateShadows()
{
std::lock_guard<std::mutex> 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
}
7 changes: 7 additions & 0 deletions gazebo/rendering/RTShaderSystem.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();

Expand Down
7 changes: 7 additions & 0 deletions gazebo/rendering/RTShaderSystemPrivate.hh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#ifndef _GAZEBO_RTSHADERSYSTEM_PRIVATE_HH_
#define _GAZEBO_RTSHADERSYSTEM_PRIVATE_HH_

#include <mutex>
#include <string>
#include <vector>

Expand Down Expand Up @@ -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;
Expand All @@ -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;
};
}
}
Expand Down
5 changes: 3 additions & 2 deletions gazebo/rendering/RenderingLight_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Loading

0 comments on commit fc2f8e2

Please sign in to comment.