From 505297a753b6492ed3c5cd38c244b84b24b8eaff Mon Sep 17 00:00:00 2001 From: William Lew Date: Thu, 9 Dec 2021 01:11:40 -0800 Subject: [PATCH] Point light shadows (#3051) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added point light textures Signed-off-by: William Lew * Added point light shadow camera setup Signed-off-by: William Lew * Fixed frustum for point light shadow cube map Signed-off-by: William Lew * Update gazebo/rendering/PointLightShadowCameraSetup.cc Co-authored-by: Alejandro Hernández Cordero * Added code suggestions Signed-off-by: William Lew * Added newline to end of file * Added code suggestion Signed-off-by: William Lew * Uncommented parameter * Point lights now cast shadows, expect true for tests * Fixed OGRE errors Signed-off-by: William Lew * Commented out unused parameter warning Signed-off-by: William Lew * Added code suggestions Signed-off-by: William Lew * remove unused variable Co-authored-by: Alejandro Hernández Cordero Co-authored-by: Steve Peters --- gazebo/rendering/CMakeLists.txt | 1 + gazebo/rendering/Light.cc | 15 +- .../rendering/PointLightShadowCameraSetup.cc | 109 +++++++++ .../rendering/PointLightShadowCameraSetup.hh | 48 ++++ gazebo/rendering/RTShaderSystem.cc | 13 +- gazebo/rendering/RenderingLight_TEST.cc | 3 +- media/materials/programs/CMakeLists.txt | 2 + .../programs/point_light_shadow_demo_fp.glsl | 62 +++++ .../programs/point_light_shadow_demo_vp.glsl | 34 +++ media/materials/scripts/CMakeLists.txt | 1 + .../scripts/point_light_shadow_demo.material | 74 ++++++ .../shadow_caster_ignore_heightmap.program | 13 +- worlds/point_light_shadows_demo.world | 217 ++++++++++++++++++ 13 files changed, 585 insertions(+), 7 deletions(-) create mode 100644 gazebo/rendering/PointLightShadowCameraSetup.cc create mode 100644 gazebo/rendering/PointLightShadowCameraSetup.hh create mode 100644 media/materials/programs/point_light_shadow_demo_fp.glsl create mode 100644 media/materials/programs/point_light_shadow_demo_vp.glsl create mode 100644 media/materials/scripts/point_light_shadow_demo.material create mode 100644 worlds/point_light_shadows_demo.world diff --git a/gazebo/rendering/CMakeLists.txt b/gazebo/rendering/CMakeLists.txt index 6ca1b860f9..55e52e3634 100644 --- a/gazebo/rendering/CMakeLists.txt +++ b/gazebo/rendering/CMakeLists.txt @@ -47,6 +47,7 @@ set (sources OrbitViewController.cc OriginVisual.cc OrthoViewController.cc + PointLightShadowCameraSetup.cc Projector.cc RayQuery.cc RenderEngine.cc diff --git a/gazebo/rendering/Light.cc b/gazebo/rendering/Light.cc index fd32c9f8ee..6bd5be14e0 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/PointLightShadowCameraSetup.hh" #include "gazebo/rendering/RTShaderSystem.hh" using namespace gazebo; @@ -594,9 +595,21 @@ void Light::SetCastShadows(const bool _cast) RTShaderSystem::Instance()->UpdateShadows(); } } + else if (this->dataPtr->light->getType() == Ogre::Light::LT_POINT) + { + // use different shadow camera for point light + this->dataPtr->light->setCastShadows(_cast); + if (_cast && this->dataPtr->shadowCameraSetup.isNull()) + { + this->dataPtr->shadowCameraSetup = + Ogre::ShadowCameraSetupPtr(new PointLightShadowCameraSetup()); + 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/PointLightShadowCameraSetup.cc b/gazebo/rendering/PointLightShadowCameraSetup.cc new file mode 100644 index 0000000000..09627031e3 --- /dev/null +++ b/gazebo/rendering/PointLightShadowCameraSetup.cc @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +// Code in this file has been adapted from Ogre's OgreShadowCameraSetup. +// The original Ogre's licence and copyright headers are copied below: + +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2014 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#include "gazebo/rendering/PointLightShadowCameraSetup.hh" +#include "gazebo/rendering/ogre_gazebo.h" + +using namespace gazebo; +using namespace rendering; + +////////////////////////////////////////////////// +PointLightShadowCameraSetup::PointLightShadowCameraSetup() +{ +} + +////////////////////////////////////////////////// +PointLightShadowCameraSetup::~PointLightShadowCameraSetup() +{ +} + +////////////////////////////////////////////////// +void PointLightShadowCameraSetup::getShadowCamera( + const Ogre::SceneManager */*_sm*/, const Ogre::Camera *_cam, + const Ogre::Viewport */*_vp*/, const Ogre::Light *_light, + Ogre::Camera *_texCam, size_t _iteration) const +{ + Ogre::Vector3 pos, dir; + + // reset custom view / projection matrix in case already set + _texCam->setCustomViewMatrix(false); + _texCam->setCustomProjectionMatrix(false); + _texCam->setNearClipDistance(_light->_deriveShadowNearClipDistance(_cam)); + _texCam->setFarClipDistance(_light->_deriveShadowFarClipDistance(_cam)); + + _texCam->setProjectionType(Ogre::PT_PERSPECTIVE); + + // theoretically set x to +-0.25 for accuracy + // decrease x for quality of shadows + float x = 0.18f; + _texCam->setFrustumExtents(-x, x, x, -x); + + // set shadow cube map depending on the iteration + switch (_iteration) { + case 0: + _texCam->lookAt(_texCam->getPosition() + Ogre::Vector3(1, 0, 0)); + break; + case 1: + _texCam->lookAt(_texCam->getPosition() + Ogre::Vector3(-1, 0, 0)); + break; + case 2: + _texCam->lookAt(_texCam->getPosition() + Ogre::Vector3(0, 1, 0)); + break; + case 3: + _texCam->lookAt(_texCam->getPosition() + Ogre::Vector3(0, -1, 0)); + break; + case 4: + _texCam->lookAt(_texCam->getPosition() + Ogre::Vector3(0, 0, 1)); + break; + case 5: + _texCam->lookAt(_texCam->getPosition() + Ogre::Vector3(0, 0, -1)); + break; + default: + break; + } +} diff --git a/gazebo/rendering/PointLightShadowCameraSetup.hh b/gazebo/rendering/PointLightShadowCameraSetup.hh new file mode 100644 index 0000000000..fccbeddbbd --- /dev/null +++ b/gazebo/rendering/PointLightShadowCameraSetup.hh @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef GAZEBO_RENDERING_POINTLIGHTSHADOWCAMERASETUP_HH_ +#define GAZEBO_RENDERING_POINTLIGHTSHADOWCAMERASETUP_HH_ + +#include "gazebo/rendering/ogre_gazebo.h" + +#include "gazebo/util/system.hh" + +namespace gazebo +{ + namespace rendering + { + class GAZEBO_VISIBLE PointLightShadowCameraSetup + : public Ogre::DefaultShadowCameraSetup + { + /// \brief Constructor + public: PointLightShadowCameraSetup(); + + /// \brief Destructor + public: ~PointLightShadowCameraSetup(); + + /// \brief Returns a shadow camera for a point light + /// \sa FocusedShadowCameraSetup::getShadowCamera() + public: virtual void getShadowCamera(const Ogre::SceneManager *_sm, + const Ogre::Camera *_cam, const Ogre::Viewport *_vp, + const Ogre::Light *_light, Ogre::Camera *_texCam, size_t _iteration) + const override; + }; + } +} + +#endif diff --git a/gazebo/rendering/RTShaderSystem.cc b/gazebo/rendering/RTShaderSystem.cc index 0d44bacbc3..fe7197ab5f 100644 --- a/gazebo/rendering/RTShaderSystem.cc +++ b/gazebo/rendering/RTShaderSystem.cc @@ -728,6 +728,7 @@ void RTShaderSystem::UpdateShadows(ScenePtr _scene) // point: not working yet unsigned int dirLightCount = 1u; unsigned int spotLightCount = 0u; + unsigned int pointLightCount = 0u; for (unsigned int i = 0; i < _scene->LightCount(); ++i) { LightPtr light = _scene->LightByIndex(i); @@ -737,6 +738,9 @@ void RTShaderSystem::UpdateShadows(ScenePtr _scene) if (light->Type() == "spot") spotLightCount++; + + if (light->Type() == "point") + pointLightCount++; } // 3 textures per directional light @@ -746,12 +750,14 @@ void RTShaderSystem::UpdateShadows(ScenePtr _scene) sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_SPOTLIGHT, 1); // \todo(anyone) make point light shadows work - sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_POINT, 0); + sceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_POINT, 6); // \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 pointShadowCount = 6 * pointLightCount; + sceneMgr->setShadowTextureCount(dirShadowCount + spotShadowCount + + pointShadowCount); unsigned int texSize = this->dataPtr->shadowTextureSize; #if defined(__APPLE__) @@ -771,7 +777,8 @@ void RTShaderSystem::UpdateShadows(ScenePtr _scene) // 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 + spotShadowCount; ++i) + for (size_t i = 0u; i < dirShadowCount + spotShadowCount + pointShadowCount; + ++i) { const Ogre::TexturePtr tex = sceneMgr->getShadowTexture(i); // This will fail if not using OpenGL as the rendering backend. diff --git a/gazebo/rendering/RenderingLight_TEST.cc b/gazebo/rendering/RenderingLight_TEST.cc index d7f2bbaf77..e1ac13793d 100644 --- a/gazebo/rendering/RenderingLight_TEST.cc +++ b/gazebo/rendering/RenderingLight_TEST.cc @@ -127,8 +127,7 @@ TEST_F(Light_TEST, CastShadows) msg.set_cast_shadows(true); pointLight->LoadFromMsg(msg); EXPECT_EQ(pointLight->LightType(), "point"); - // issue #2083: point light does not cast shadows - EXPECT_FALSE(pointLight->CastShadows()); + EXPECT_TRUE(pointLight->CastShadows()); scene->RemoveLight(pointLight); pointLight.reset(); } diff --git a/media/materials/programs/CMakeLists.txt b/media/materials/programs/CMakeLists.txt index c98231da88..93d4ef88ac 100644 --- a/media/materials/programs/CMakeLists.txt +++ b/media/materials/programs/CMakeLists.txt @@ -38,6 +38,8 @@ perpixel_fp.glsl perpixel_vp.glsl plain_color_fs.glsl plain_color_vs.glsl +point_light_shadow_demo_fp.glsl +point_light_shadow_demo_vp.glsl point_receiver_fp.glsl point_receiver_vp.glsl projector.frag diff --git a/media/materials/programs/point_light_shadow_demo_fp.glsl b/media/materials/programs/point_light_shadow_demo_fp.glsl new file mode 100644 index 0000000000..ac4e1afc60 --- /dev/null +++ b/media/materials/programs/point_light_shadow_demo_fp.glsl @@ -0,0 +1,62 @@ +#version 130 + +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; +uniform sampler2DShadow shadowMap4; +uniform sampler2DShadow shadowMap5; + +varying vec4 lightSpacePos0; +varying vec4 lightSpacePos1; +varying vec4 lightSpacePos2; +varying vec4 lightSpacePos3; +varying vec4 lightSpacePos4; +varying vec4 lightSpacePos5; + +varying vec4 worldPos; + +//------------------------------------------------------------------------------ +float ShadowSimple(in sampler2DShadow 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; + if (shadowMapUV.x < 0.0 || shadowMapUV.x > 1.0) + return 0.0; + if (shadowMapUV.y < 0.0 || shadowMapUV.y > 1.0) + return 0.0; + + // get closest depth value from light's perspective + float closestDepth = texture(shadowMap, shadowMapUV); + + // 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; + + vec4 outColor = vec4(1.0, 0.0, 0.0, 1.0); + + // grey shadows + f += ShadowSimple(shadowMap0, lightSpacePos0); + f += ShadowSimple(shadowMap1, lightSpacePos1); + f += ShadowSimple(shadowMap2, lightSpacePos2); + f += ShadowSimple(shadowMap3, lightSpacePos3); + f += ShadowSimple(shadowMap4, lightSpacePos4); + f += ShadowSimple(shadowMap5, lightSpacePos5); + f = clamp(f, 0.0f, 1.0f); + if (f > 0.0f) + outColor = vec4(0.0, 0.0, 0.0, 1.0); + + gl_FragColor = outColor; +} diff --git a/media/materials/programs/point_light_shadow_demo_vp.glsl b/media/materials/programs/point_light_shadow_demo_vp.glsl new file mode 100644 index 0000000000..77bf973735 --- /dev/null +++ b/media/materials/programs/point_light_shadow_demo_vp.glsl @@ -0,0 +1,34 @@ +#version 120 + +uniform mat4 worldMatrix; +uniform mat4 worldViewMatrix; +uniform mat4 viewProjMatrix; + +uniform mat4 texViewProjMatrix0; +uniform mat4 texViewProjMatrix1; +uniform mat4 texViewProjMatrix2; +uniform mat4 texViewProjMatrix3; +uniform mat4 texViewProjMatrix4; +uniform mat4 texViewProjMatrix5; +varying vec4 lightSpacePos0; +varying vec4 lightSpacePos1; +varying vec4 lightSpacePos2; +varying vec4 lightSpacePos3; +varying vec4 lightSpacePos4; +varying vec4 lightSpacePos5; + +varying vec4 worldPos; + +void main() +{ + worldPos = worldMatrix * gl_Vertex; + gl_Position = viewProjMatrix * worldPos; + + lightSpacePos0 = texViewProjMatrix0 * worldPos; + lightSpacePos1 = texViewProjMatrix1 * worldPos; + lightSpacePos2 = texViewProjMatrix2 * worldPos; + lightSpacePos3 = texViewProjMatrix3 * worldPos; + lightSpacePos4 = texViewProjMatrix4 * worldPos; + lightSpacePos5 = texViewProjMatrix5 * worldPos; +} + diff --git a/media/materials/scripts/CMakeLists.txt b/media/materials/scripts/CMakeLists.txt index 51b157cc58..d61e768cf5 100644 --- a/media/materials/scripts/CMakeLists.txt +++ b/media/materials/scripts/CMakeLists.txt @@ -18,6 +18,7 @@ noise.compositor oculus.material perpixel.program picker.material +point_light_shadow_demo.material ShadowCaster.material shadow_caster.program shadow_caster_ignore_heightmap.program diff --git a/media/materials/scripts/point_light_shadow_demo.material b/media/materials/scripts/point_light_shadow_demo.material new file mode 100644 index 0000000000..d341520a55 --- /dev/null +++ b/media/materials/scripts/point_light_shadow_demo.material @@ -0,0 +1,74 @@ +vertex_program Gazebo/PointLightShadowDemoVS glsl +{ + source point_light_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 + param_named_auto texViewProjMatrix1 texture_viewproj_matrix 1 + param_named_auto texViewProjMatrix2 texture_viewproj_matrix 2 + param_named_auto texViewProjMatrix3 texture_viewproj_matrix 3 + param_named_auto texViewProjMatrix4 texture_viewproj_matrix 4 + param_named_auto texViewProjMatrix5 texture_viewproj_matrix 5 + } +} + +fragment_program Gazebo/PointLightShadowDemoFS glsl +{ + source point_light_shadow_demo_fp.glsl + + default_params + { + param_named shadowMap0 int 0 + param_named shadowMap1 int 1 + param_named shadowMap2 int 2 + param_named shadowMap3 int 3 + param_named shadowMap4 int 4 + param_named shadowMap5 int 5 + } +} + +material Gazebo/PointLightShadowDemo +{ + technique + { + pass + { + vertex_program_ref Gazebo/PointLightShadowDemoVS + { + } + + fragment_program_ref Gazebo/PointLightShadowDemoFS + { + } + texture_unit shadowMap0 { + content_type shadow + tex_address_mode clamp + } + texture_unit shadowMap1 { + content_type shadow + tex_address_mode clamp + } + texture_unit shadowMap2 { + content_type shadow + tex_address_mode clamp + } + texture_unit shadowMap3 { + content_type shadow + tex_address_mode clamp + } + texture_unit shadowMap4 { + content_type shadow + tex_address_mode clamp + } + texture_unit shadowMap5 { + content_type shadow + tex_address_mode clamp + } + } + } +} diff --git a/media/materials/scripts/shadow_caster_ignore_heightmap.program b/media/materials/scripts/shadow_caster_ignore_heightmap.program index 6fe04097c8..bf4c3c8ac2 100644 --- a/media/materials/scripts/shadow_caster_ignore_heightmap.program +++ b/media/materials/scripts/shadow_caster_ignore_heightmap.program @@ -1,3 +1,15 @@ +vertex_program shadow_caster_vp_glsl glsl +{ + source shadow_caster_vp.glsl + + default_params + { + param_named_auto world_view_proj_mat worldviewproj_matrix + param_named_auto texel_offsets texel_offsets + } +} + + fragment_program shadow_caster_ignore_heightmap_fp_glsl glsl { source shadow_caster_ignore_heightmap_fp.glsl @@ -10,7 +22,6 @@ fragment_program shadow_caster_ignore_heightmap_fp_glsl glsl } } - material Gazebo/shadow_caster_ignore_heightmap { technique diff --git a/worlds/point_light_shadows_demo.world b/worlds/point_light_shadows_demo.world new file mode 100644 index 0000000000..24a4b421f3 --- /dev/null +++ b/worlds/point_light_shadows_demo.world @@ -0,0 +1,217 @@ + + + + + + + + + model://ground_plane + + + + true + 0.0 0.0 3.0 0 0 0 + 0.8 0.8 0.8 1 + 0.2 0.2 0.2 1 + + 25 + 0.05 + 0.01 + 0.001 + + true + + + + 0 0 0.1 0 0 0 + true + + + + + 0 0 1 + 25 25 + + + + + 0xffff + + + + 100 + 50 + + + + + + false + + + 0 0 1 + 25 25 + + + + + + + + + + + 0 0 0.5 0 0 0 + + + + + 1 1 1 + + + + + + + 1 1 1 + + + + + + + + + + + 2 0 0.5 0 0 0 + + + + + 1 1 1 + + + + + + + 1 1 1 + + + + + + + + + + + 1 4 0.5 0 0 0 + + + + + 1 1 1 + + + + + + + 1 1 1 + + + + + + + + + + + -4 -4 0.5 0 0 0 + + + + + 1 + + + + + + + 1 + + + + + + + + + + + -4 5 0.5 0 0 0 + + + + + 0.25 + + + + + + + 0.25 + + + + + + + + + + + 7 -6 0.5 0 0 0 + + + + + 1 + 1 + + + + + + + 1 + 1 + + + + + + + + + + +