OGRECave/ogre-next

Graphical errors in the Exponential Shadow Maps

Th3V1kt0r opened this issue · 4 comments

Ogre 2.3 (master)

There are some graphical errors in the Exponential Shadow Maps.

You can see it clearly in Sample_ShadowMapDebugging if you enable ESM. Once on the cubes (green). Or on the edges when you make the plate smaller (red).
It's even worse if you have multiple plates next to each other that have a gap.

shadow

Shadow2

In the shadow texture you can see holes where the plates touch each other.

shadow3

Hi!

I can't reproduce your bug. Can you provide a diff to apply the same changes you're doing?

I'm doing:

diff --git a/Samples/2.0/ApiUsage/ShadowMapDebugging/ShadowMapDebuggingGameState.cpp b/Samples/2.0/ApiUsage/ShadowMapDebugging/ShadowMapDebuggingGameState.cpp
index 52db386537..044f17f20c 100644
--- a/Samples/2.0/ApiUsage/ShadowMapDebugging/ShadowMapDebuggingGameState.cpp
+++ b/Samples/2.0/ApiUsage/ShadowMapDebugging/ShadowMapDebuggingGameState.cpp
@@ -74,6 +74,7 @@ namespace Demo
             Ogre::SceneNode *sceneNode = sceneManager->getRootSceneNode( Ogre::SCENE_DYNAMIC )
                                              ->createChildSceneNode( Ogre::SCENE_DYNAMIC );
             sceneNode->setPosition( 0, -1, 0 );
+            sceneNode->setScale(Ogre::Vector3(0.1f));
             sceneNode->attachObject( item );
         }
 
@@ -169,6 +170,18 @@ namespace Demo
         MiscUtils::setGaussianLogFilterParams( "ESM/GaussianLogFilterV", kernelRadius,
                                                gaussianDeviationFactor, K );
 #endif
+        {
+            Ogre::Hlms *hlms = mGraphicsSystem->getRoot()->getHlmsManager()->getHlms( Ogre::HLMS_PBS );
+
+            assert( dynamic_cast<Ogre::HlmsPbs *>( hlms ) );
+            Ogre::HlmsPbs *pbs = static_cast<Ogre::HlmsPbs *>( hlms );
+
+            Ogre::HlmsPbs::ShadowFilter nextFilter = Ogre::HlmsPbs::ExponentialShadowMaps;
+            pbs->setShadowSettings( nextFilter );
+
+            if( nextFilter == Ogre::HlmsPbs::ExponentialShadowMaps )
+                setupShadowNode( true );
+        }
 
         TutorialGameState::createScene01();
     }

Also please provide the following info:

  • Operating System / Platform: ❔
  • RenderSystem: ❔
  • GPU: ❔

Cheers

Hi!

I created small plates and did not scale them.
I used F5 to switch to ESM mode.

Windows 7/10
OpenGL/DirectX
GeForce GTX 1060

diff --git a/ShadowMapDebuggingGameState - Kopie.cpp b/ShadowMapDebuggingGameState.cpp
index 52db386537..c3adb779fd 100644
--- a/ShadowMapDebuggingGameState - Kopie.cpp	
+++ b/ShadowMapDebuggingGameState.cpp
@@ -61,7 +61,13 @@ namespace Demo
 
         Ogre::v1::MeshPtr planeMeshV1 = Ogre::v1::MeshManager::getSingleton().createPlane(
             "Plane v1", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
-            Ogre::Plane( Ogre::Vector3::UNIT_Y, 1.0f ), 50.0f, 50.0f, 1, 1, true, 1, 4.0f, 4.0f,
+            Ogre::Plane( Ogre::Vector3::UNIT_Y, 1.0f ), 5.0f, 5.0f, 1, 1, true, 1, 4.0f, 4.0f,
+            Ogre::Vector3::UNIT_Z, Ogre::v1::HardwareBuffer::HBU_STATIC,
+            Ogre::v1::HardwareBuffer::HBU_STATIC );
+
+        Ogre::v1::MeshPtr planeMeshV2 = Ogre::v1::MeshManager::getSingleton().createPlane(
+            "Plane v2", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
+            Ogre::Plane( Ogre::Vector3::UNIT_Y, 1.0f ), 5.0f, 5.0f, 1, 1, true, 1, 4.0f, 4.0f,
             Ogre::Vector3::UNIT_Z, Ogre::v1::HardwareBuffer::HBU_STATIC,
             Ogre::v1::HardwareBuffer::HBU_STATIC );
 
@@ -77,6 +83,18 @@ namespace Demo
             sceneNode->attachObject( item );
         }
 
+        Ogre::MeshPtr planeMesh2 = Ogre::MeshManager::getSingleton().createByImportingV1(
+            "Plane2", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, planeMeshV1.get(), true,
+            true, true );
+
+        {
+            Ogre::Item *item = sceneManager->createItem( planeMesh, Ogre::SCENE_DYNAMIC );
+            Ogre::SceneNode *sceneNode = sceneManager->getRootSceneNode( Ogre::SCENE_DYNAMIC )
+                                             ->createChildSceneNode( Ogre::SCENE_DYNAMIC );
+            sceneNode->setPosition( 5.001, -1, 0 );
+            sceneNode->attachObject( item );
+        }
+
         float armsLength = 2.5f;
 
         for( int i = 0; i < 4; ++i )

Oh NOW I understand what's going on.

You've created two floor panels and there is a small gap between them:

Screenshot_2023-05-21_14-23-19

This gap is triggering a limitation of ESM shadow maps: When there is a discontinuity.

From the shadow camera's POV we have:

  • Pixel to the left: floor tile A
  • Pixel in the center: an abyss far away you can't see the bottom of. It's almost an infinite depth.
  • Pixel to the right: floor tile B

Because ESM works by blurring depth, the "abyss" starts leaking into the nearby pixels. The K setting (setEsmK) also plays an important role.

If you fix the gap you can fix the problem.
If you cannot fix the gap, you can workaround the problem by creating another floor at the same (or very similar) Y position as the floor panels, and set visibility flags so that the object cast shadows but isn't drawn in the main scene.

Like this:

02

{
    Ogre::Item *item = sceneManager->createItem( planeMesh, Ogre::SCENE_DYNAMIC );
    Ogre::SceneNode *sceneNode = sceneManager->getRootSceneNode( Ogre::SCENE_DYNAMIC )
                                     ->createChildSceneNode( Ogre::SCENE_DYNAMIC );
    sceneNode->setPosition( 0, -2, 0 );
    sceneNode->setScale( Ogre::Vector3( 10.0f, 1, 10.0f ) );
    sceneNode->attachObject( item );
}

And if we add to that new fake floor:

item->setVisibilityFlags( 1u );

And change compositor script:

compositor_node ShadowMapDebuggingRenderingNode
{
	in 0 rt_renderwindow

	target rt_renderwindow
	{
		pass render_scene
		{
			load
			{
				all				clear
				clear_colour	0.2 0.4 0.6 1
			}
			store
			{
				colour	store_or_resolve
				depth	dont_care
				stencil	dont_care
			}
			overlays	on
			shadows		ShadowMapDebuggingShadowNode

			visibility_mask 0xFFFFFFFE

			profiling_id "Main Render"
		}
	}
}

Notice the visibility_mask 0xFFFFFFFE is a new line. Then we get the desired output:

FakeFloor

Since the fake floor is casting a shadow, but isn't shown on screen.

Again, this is a limitation of the ESM technique and there is not much that can be done other than hiding the artifact via workarounds or switching to a different technique.

MSM (Moment Shadow Mapping) fixes this problem with brute force by using all techniques (VSM, ESM, PCF) and then select per pixel which one to use (based on a heuristic of "which one has less artifacts"), but we don't implement it in OgreNext yet.

I'm closing the ticket because there is not much that can be done; this is why ESM is best fit for interiors rather than exteriors or open world maps.

Note that the workaround doesn't have to be a plane. You could encase your scene inside a sphere to achieve a similar result.