Shadow precision loss for orthogonal lights

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Post Reply
User avatar
GlowingPotato
Goblin
Posts: 211
Joined: Wed May 08, 2013 2:58 pm
x 10

Shadow precision loss for orthogonal lights

Post by GlowingPotato »

Hi,

There is this card on trello about a fix for shadow precision loss https://trello.com/c/jsPDLF9L/215-fix-s ... nal-lights

What it takes to fix this ? Should be simple or its something actually complex ?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: Shadow precision loss for orthogonal lights

Post by dark_sylinc »

Essentially simple, but I have been delaying it because of the potential complexity of dealing with corner cases (different lighting setups).

Basically we do

Code: Select all

outVs.posL@n.z = outVs.posL@n.z * pass.shadowRcv[@n].shadowDepthRange.y; //Receiver
outVs.depth	= (gl_Position.z + shadowConstantBias * pass.depthRange.y) * pass.depthRange.y; //caster
gl_Position.z = (gl_Position.z + shadowConstantBias * pass.depthRange.y) * pass.depthRange.y * gl_Position.w; //caster
in both caster and receiver passes to get the depth in range [0; 1] in what I call pseudo linear depth to improve accuracy.
The thing is, for directional lights which use orthogonal projection, posL@n.z is already in [0; 1] so multiplying this value by the inverse of the depth range just loses precision as it becomes in range [0; 1/depth_range]. Directional lights should do:

Code: Select all

//Receiver does nothing
outVs.depth	= gl_Position.z + shadowConstantBias * pass.depthRange.y; //caster
gl_Position.z = gl_Position.z + shadowConstantBias * pass.depthRange.y; //caster
The rest of the lights should do it the old way.
User avatar
GlowingPotato
Goblin
Posts: 211
Joined: Wed May 08, 2013 2:58 pm
x 10

Re: Shadow precision loss for orthogonal lights

Post by GlowingPotato »

Thanks for replying!

We did this modification but the shadows broke.

We use only one shadow caster light, its a directional light (our sun). we rotate this light as time pass.

Shadows disappeared, and sometimes only a thin region on screen was rendering shadows.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: Shadow precision loss for orthogonal lights

Post by dark_sylinc »

It's possible "shadowConstantBias * pass.depthRange.y" should be "shadowConstantBias * pass.depthRange.y * pass.depthRange.y". Or maybe I made another mistake. It was tricky to get right (part of why I was delaying it...)

Can you post a picture when something shadowed shows up. Perhaps the artifact reminds me something.
User avatar
GlowingPotato
Goblin
Posts: 211
Joined: Wed May 08, 2013 2:58 pm
x 10

Re: Shadow precision loss for orthogonal lights

Post by GlowingPotato »

Here are the images.

Below, you can only see the shadow of the head of the character, If the character jumps, then you see the torso on the shadows, It clips near and far.
Image

Here, there is the shadow of the tree, there is only a thin region of the bark o the shadows, and the leafs on the top.
Image

same here...
Image
Image

Here, is easy to see the near clip of the shadow, But it clips far away too.
Image

Image


old piece of code:

Code: Select all

@property( !hlms_shadowcaster )
@insertpiece( ShadowReceive )
@foreach( hlms_num_shadow_maps, n )
outVs.posL@n.z = outVs.posL@n.z * pass.shadowRcv[@n].shadowDepthRange.y;
outVs.posL@n.z = (outVs.posL@n.z * 0.5) + 0.5;@end

@property( hlms_pssm_splits )	outVs.depth = gl_Position.z;@end

@end @property( hlms_shadowcaster )
   float shadowConstantBias = uintBitsToFloat( instance.worldMaterialIdx[drawId].y );

@property( !hlms_shadow_uses_depth_texture )
//Linear depth
outVs.depth	= (gl_Position.z + shadowConstantBias * pass.depthRange.y) * pass.depthRange.y;
outVs.depth = (outVs.depth * 0.5) + 0.5;
@end

//We can't make the depth buffer linear without Z out in the fragment shader;
//however we can use a cheap approximation ("pseudo linear depth")
//see http://www.yosoygames.com.ar/wp/2014/01/linear-depth-buffer-my-ass/
gl_Position.z = (gl_Position.z + shadowConstantBias * pass.depthRange.y) * pass.depthRange.y * gl_Position.w;
@end 
new code

Code: Select all

@property( !hlms_shadowcaster )
@insertpiece( ShadowReceive )

@property( hlms_pssm_splits )	outVs.depth = gl_Position.z;@end

@end @property( hlms_shadowcaster )
   float shadowConstantBias = uintBitsToFloat( instance.worldMaterialIdx[drawId].y );

@property( !hlms_shadow_uses_depth_texture )
//Linear depth
ooutVs.depth	= (gl_Position.z + shadowConstantBias * pass.depthRange.y);
outVs.depth = (outVs.depth * 0.5) + 0.5;
@end

//We can't make the depth buffer linear without Z out in the fragment shader;
//however we can use a cheap approximation ("pseudo linear depth")
//see http://www.yosoygames.com.ar/wp/2014/01/linear-depth-buffer-my-ass/
gl_Position.z = (gl_Position.z + shadowConstantBias * pass.depthRange.y);
@end

Thanks for having a look at this. :D
zxz
Gremlin
Posts: 184
Joined: Sat Apr 16, 2016 9:25 pm
x 19

Re: Shadow precision loss for orthogonal lights

Post by zxz »

Hi everybody,

GlowingPotato, did you get any further towards a solution to your shadow precision issues?

We are having some large issues with PSSM shadow quality, which I guess might be related to this.

Here's a shot of a single terrain tile, with materials removed for clarity.

Image

The areas with striped artifacts should be shadowed.

Random info that might be relevant:
Terrain tile size: ~100x130 meters, nothing else is visible or casting/receiving shadows. This doesn't seem excessive to me.
Shadow setup: 4 PSSM splits, 4096x4096 resolution for all
PSSM Lambda: 0.95
Filter: PCF 3x3
Shadow far distance: 200 meters
Light angle: 55 degrees from zenith.
Shadow texture format: 32 bits floating point
Depth range parameters to vertex shader in shadow passes: { 1, 0.0299 }, { 1, 0.02246 }, {1, 0.01241}, {1, 0.1249 }


Below is some debug output from ShadowCameraSetupFocused

Code: Select all

texCam: ShadowNode Camera ID 64 Map 0
world space camera final: Vector3(47.2884, -51.2397, 10.5207)
orthowindow dimensions: 14.9542, 9.93415
mMinDistance: 1.0
mMaxDistance: 34.5801

texCam: ShadowNode Camera ID 64 Map 1
world space camera final: Vector3(48.2127, -39.4848, 14.017)
orthowindow dimensions: 29.6532, 16.7299
mMinDistance: 1.0
mMaxDistance: 45.8776

texCam: ShadowNode Camera ID 64 Map 2
world space camera final: Vector3(50.9142, 0.597526, 21.8115)
orthowindow dimensions: 93.7737, 47.0959
mMinDistance: 1.0
mMaxDistance: 82.1352

texCam: ShadowNode Camera ID 64 Map 3
world space camera final: Vector3(65.9933, 8.63716, 14.627)
orthowindow dimensions: 152.548, 52.7239
mMinDistance: 1.0
mMaxDistance: 81.3816
I don't see any obviously incorrect numbers, over the top shadow ranges or anything of that kind. For example the near/far spans don't seem excessive to cause such poor precision.
I have tried changing all the parameters regaring PSSM, shadow distances, etc, but the issue does not want to go away or get significantly improved either way. I can play around with the shadow constant bias to get rid of the moving artifacts, but it must be so large that I might just not use shadows at all instead.

I am hoping that again there is something simple I have missed in the setup to cause these huge artifacts. Any ideas what I might be missing?

I appreciate any help

Thanks in advance!
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: Shadow precision loss for orthogonal lights

Post by dark_sylinc »

There is a sample called "ShadowMapDebugging" showing how to debug shadow mapping. Please start using it!!! (you're not the first one). Post some screenshots with that on.
There's also a hlmsPbs->setDebugPssmSplits function to help debugging how the PSSM splits are being assigned. Post some screenshots with that on.

Post several angles and distances to understand what's going on and how the shadow map behaves. A frontal as if your camera where facing like the sun may help a lot.

It also sounds like HlmsManager::setShadowMappingUseBackFaces is set to false, or the terrain has no backface culling.
zxz
Gremlin
Posts: 184
Joined: Sat Apr 16, 2016 9:25 pm
x 19

Re: Shadow precision loss for orthogonal lights

Post by zxz »

setShadowMappingUseBackFaces is set to true. The mesh is drawn into the shadow texture with backfaces (verified in renderdoc). If I set it to false, the lit areas get the self-shadowing artifacts, while the shadowed once don't. I think this is expected.

I will look into the other things you mentioned ASAP.

Thanks!
Last edited by zxz on Thu Oct 19, 2017 9:21 pm, edited 1 time in total.
zxz
Gremlin
Posts: 184
Joined: Sat Apr 16, 2016 9:25 pm
x 19

Re: Shadow precision loss for orthogonal lights

Post by zxz »

Here are a few screenshots, showing that the artifacts are very clear in any of the slices. The images are taken in succession, further and further away, but at mostly the same angle.

Image
Image
Image
Image
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: Shadow precision loss for orthogonal lights

Post by dark_sylinc »

So slices are OK. You cant turn it off now.
So these are backfaces (bummer, was hoping that was it)

I'll wait for screenshots with the debugging stuff and the different camera angles. (Also a RenderDoc capture?). The shots you published were all from the same angle.

Edit: If you have a binary-only demo I can fly over that would be fantastic, Linux or Windows.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: Shadow precision loss for orthogonal lights

Post by dark_sylinc »

I just pushed an important fix to shadow mapping precision I've been wanting to correct for quite some time.

Could you please check if this enhancement positively affects your issue in any way?
Thanks
zxz
Gremlin
Posts: 184
Joined: Sat Apr 16, 2016 9:25 pm
x 19

Re: Shadow precision loss for orthogonal lights

Post by zxz »

Hello!

Thanks for having a look at this!

Unfortunately the change doesn't appear to affect our shadow issues at all. :( There are still huge self-shadowing artifacts on our terrain, at all distance ranges.

I also haven't had time to provide more debugging information since last time. I can't distribute our application in order to show the problems, but I will hopefully get some time to create a small Ogre sample demonstrating the issue.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: Shadow precision loss for orthogonal lights

Post by dark_sylinc »

Agh! Too bad. I was hoping that would be it.

Here's the thing:
  • There could be a bug in Ogre, I'm totally open to that possibility.
  • Shadow mapping, as an algorithm, has a limit. Much lower than what we want to admit. When you create a giant terrain that can cast shadows (and therefore, with a huge AABB), you force Ogre into a kind of worst case scenario, positioning the cameras very far and wide to try to capture everything. That's the reason Terra (see Terrain sample) does not cast shadows, and uses a different algorithm for terrain self shadow (it runs a compute shader that raytraces in real time; it could be ported to CPU for older GPUs, dunno about performance).
If it's a bug; then the bug would be in FocusedShadowCameraSetup::getShadowCamera (which gets called by PSSMShadowCameraSetup::getShadowCamera for each split region).

What FocusedShadowCameraSetup does is to take the area that is casting shadows; and clip it against the area that is supposed to receive those shadows in the XY planes (from the light's perspective).
A picture is worth a thousand words:




Now when taken to 3D; this is how it looks from the sun's position:




Now after we cull XY planes, we get a tight fit for the light camera:




In code, this is taken care by the following:

Code: Select all

//Take the 8 camera frustum's corners, transform to
//light space, and compute its AABB in light space
ArrayVector3 corners[NUM_ARRAY_VECTORS];
cam->getCustomWorldSpaceCorners( corners, farDistance );

for( size_t i=0; i<NUM_ARRAY_VECTORS; ++i )
{
    ArrayVector3 lightSpacePoint = worldToLightSpace * corners[i];
    vMinBounds.makeFloor( lightSpacePoint );
    vMaxBounds.makeCeil( lightSpacePoint );
}

Vector3 vMinCamFrustumLS = vMinBounds.collapseMin();
Vector3 vMaxCamFrustumLS = vMaxBounds.collapseMax();
vMinCamFrustumLS.xy should contain the top left border, and vMaxCamFrustumLS.xy should contain the bottom right borders of the light camera.

The problem is now Z. We need to consider the caster's AABB for that; otherwise we'll be placing the sun in front of a mountain instead of placing it behind the mountain; meaning that mountain won't cast shadows. And the Z doesn't need to be further than the maximum's receiver's depth. In pictures, from another angle (this is not from the light's camera perspective):




That scene shows a terrain with its huge caster AABB, and the Focused camera trying to satisfy the constraints.
Now from different angles to make it clear:





There's one more case to consider: The case where the AABB caster is smaller than the frustum. Ideally then the light can be shrunk:




In this case, the caster is smaller than the frustum, thus we can fit the light tighter:




This is taken care by the following part of the code:

Code: Select all

Vector3 casterAabbCornersLS[8];
for( size_t i=0; i<8; ++i )
{
    casterAabbCornersLS[i] = scalarWorldToLightSpace *
                            casterBox.getCorner( static_cast<AxisAlignedBox::CornerEnum>( i ) );
}

ConvexBody convexBody;
convexBody.define( casterAabbCornersLS );

Plane p;
p.redefine( Vector3::NEGATIVE_UNIT_X, vMinCamFrustumLS );
convexBody.clip( p );
p.redefine( Vector3::UNIT_X, vMaxCamFrustumLS );
convexBody.clip( p );
p.redefine( Vector3::NEGATIVE_UNIT_Y, vMinCamFrustumLS );
convexBody.clip( p );
p.redefine( Vector3::UNIT_Y, vMaxCamFrustumLS );
convexBody.clip( p );
p.redefine( Vector3::NEGATIVE_UNIT_Z, vMinCamFrustumLS );
convexBody.clip( p );
Which basically took our XY regions from vMin/vMaxCamFrustumLS and clamped against the caster's AABB to see what's smaller.
Later on we retrieve the Z needed for the near and far planes.

So, basically, this is what FocusedShadowCameraSetup should be doing. AKA finding the best possible fit considering:
  • The sun's direction
  • The caster AABB's min Z and max Z
  • The caster's AABB (in case it's smaller than the frustum)
  • The frustum split
If FocusedShadowCameraSetup is not doing that, then it's an Ogre bug. It could be that the code I wrote is incorrect, or that ConvexBody::clip doesn't work as intended (it was written a long time ago by someone else; I was never 100% convinced it was bug free).
One possible way to verify this would be to draw debug shapes (that don't cast shadows) and see from another camera the "cube" that represents what the light's camera sees; and check it against the camera's frustum shape to see if they fit tightly as in the pictures above.

If FocusedShadowCameraSetup is working as intended, then you're out of luck, your scene is just pushing the limits of what shadow mapping can achieve. You'll have to use more PSSM splits or increase the resolution. Alternatively, you could modify FocusedShadowCameraSetup to artificially clamp the AABB caster size (which could result in missing shadows that suddenly pop in as you move) to improve the quality.
As a final alternative, you could try LiPSM which is a world of pain and has a lot of problems of its own (and Ogre's 1.x implementation was too buggy), but it does improve shadow mapping quality by doing tricks with the projection camera and "warping" the shadow map. Good luck on that though.

By using a compute shader like SDSM shadow mapping does to analyze the depth buffer we could have a lot more information about the scene and make the shadow maps much tighter (because we have minimum and maximum receiver size instead of assuming it's a huge frustum), not to mention we can dynamically adjust the pssm limits.
But this is beyond what Ogre is currently providing.

My hopes is that you find an obvious error in FocusedShadowCameraSetup, or you find adding a few more manual restrictions (to make the shadow map tighter at the expense of some artifacts) to be acceptable.
Post Reply