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 9 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
19 changes: 13 additions & 6 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,13 +585,19 @@ void Light::SetRange(const double _range)
//////////////////////////////////////////////////
void Light::SetCastShadows(const bool _cast)
{
if (this->dataPtr->light->getType() == Ogre::Light::LT_DIRECTIONAL)
{
this->dataPtr->light->setCastShadows(_cast);
}
else
this->dataPtr->light->setCastShadows(_cast);
scpeters marked this conversation as resolved.
Show resolved Hide resolved

if (_cast && this->dataPtr->light->getType() !=
Ogre::Light::LT_DIRECTIONAL)
{
this->dataPtr->light->setCastShadows(false);
if (this->dataPtr->shadowCameraSetup.isNull())
{
auto *setup = new Ogre::FocusedShadowCameraSetup();
this->dataPtr->shadowCameraSetup = Ogre::ShadowCameraSetupPtr(setup);
scpeters marked this conversation as resolved.
Show resolved Hide resolved
this->dataPtr->light->setCustomShadowCameraSetup(
this->dataPtr->shadowCameraSetup);
}
RTShaderSystem::Instance()->UpdateShadows();
}
}

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
160 changes: 108 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 @@ -512,52 +513,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 +592,32 @@ 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)
if (this->dataPtr->updateShadows)
{
VisualPtr vis = scene->WorldVisual();
if (vis)
for (const auto &scene : this->dataPtr->scenes)
{
this->UpdateShaders(vis);
this->UpdateShadows(scene);
}
this->dataPtr->updateShadows = false;
}

if (this->dataPtr->updateShaders)
{
for (const auto &scene : this->dataPtr->scenes)
{
VisualPtr vis = scene->WorldVisual();
if (vis)
{
this->UpdateShaders(vis);
}
}
this->dataPtr->updateShaders = false;
}
this->dataPtr->updateShaders = false;
}

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

/////////////////////////////////////////////////
void RTShaderSystem::UpdateShadows()
{
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, 1);
sceneMgr->setShadowTextureConfig(1, texSize, texSize, Ogre::PF_FLOAT32_R, 2);
sceneMgr->setShadowTextureConfig(2, texSize, texSize, Ogre::PF_FLOAT32_R, 3);
scpeters marked this conversation as resolved.
Show resolved Hide resolved

#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
3 changes: 3 additions & 0 deletions gazebo/rendering/RTShaderSystemPrivate.hh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ namespace gazebo
/// \brief Flag to indicate that shaders need to be updated.
public: bool updateShaders;

/// \brief Flag to indicate that shaders need to be updated.
scpeters marked this conversation as resolved.
Show resolved Hide resolved
public: bool updateShadows = false;

/// \brief Size of the Parallel Split Shadow Map (PSSM) shadow texture
/// at closest layer.
public: unsigned int shadowTextureSize = 2048u;
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
44 changes: 44 additions & 0 deletions media/materials/programs/spotlight_shadow_demo_fp.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#version 130

uniform sampler2D shadowMap0;
in vec4 lightSpacePos0;

in vec4 worldPos;
in vec4 worldViewPos;

out vec4 outputCol;

//------------------------------------------------------------------------------
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
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);
}
Loading