Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate spot light shadow maps #2914

Merged
merged 12 commits into from
Jan 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 28 additions & 21 deletions gazebo/rendering/Heightmap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3314,27 +3314,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 @@ -29,6 +29,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 @@ -584,12 +585,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
169 changes: 117 additions & 52 deletions gazebo/rendering/RTShaderSystem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "gazebo/common/Profiler.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 @@ -241,6 +242,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 @@ -512,52 +514,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");
sceneMgr->setShadowTextureCasterMaterial("Gazebo/shadow_caster");
this->UpdateShadows(_scene);

// Disable fog on the caster pass.
// Ogre::MaterialPtr passCaterMaterial =
Expand Down Expand Up @@ -636,20 +593,39 @@ Ogre::PSSMShadowCameraSetup *RTShaderSystem::GetPSSMShadowCameraSetup() const
void RTShaderSystem::Update()
{
GZ_PROFILE("rendering::RTShaderSystem::Update");
if (!this->dataPtr->initialized || !this->dataPtr->updateShaders)
if (!this->dataPtr->initialized)
{
return;
}

for (const auto &scene : this->dataPtr->scenes)
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;
}

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 @@ -717,3 +693,92 @@ double RTShaderSystem::ShadowSplitPadding() const
{
return this->dataPtr->shadowSplitPadding;
}

/////////////////////////////////////////////////
void RTShaderSystem::UpdateShadows()
{
std::lock_guard<std::mutex> lock(this->dataPtr->updateMutex);
this->dataPtr->updateShadows = true;
scpeters marked this conversation as resolved.
Show resolved Hide resolved
}

/////////////////////////////////////////////////
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");
sceneMgr->setShadowTextureCasterMaterial("Gazebo/shadow_caster");
}
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
2 changes: 2 additions & 0 deletions media/materials/programs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading