Advertisement

Cascade Shadow Maps - Bounding Sphere radius for stabilization

Started by April 24, 2019 11:29 PM
5 comments, last by MJP 5 years, 8 months ago

Hello,

I have been implementing CSM and applying some of the methods found on the Internet for stabilizing the cascades to fix shadow shimmering. While implementing the algorithm, I followed both Microsoft SDK code (link to SDK) and MJP's version (here) (which I found quite clear and great). While the general algorithm works okay I have found out an issue which I'm not exactly sure if it's my fault or if it works as intended.

My camera has a FOV of 60º and is divided in three different frustas, each one of them covering a percentage of the whole camera frustum and giving shadows for a given area. For each one of these frustas I compute a Bounding Sphere that contains the corners of it and is centered in the middle point of the frusta. The code works ok and I get a bounding sphere that covers the frusta completely. The issue is that such bounding spheres seem to be so big that they completely overlap each other most of the time and, while fov or depth changes for each of the frustas make the overlap bigger or smaller, they still overlap by more than a half most of the time. This makes the quality of the shadows for the bigger frustas to not be too great as the amount of unused space is quite big.

While googling for a solution I also found this blog from The Witness creators in which they detailed their approach and had a similar issue. Nonetheless, I would like to see If somebody knows if there may be something wrong with my approach and how to fix it if possible.

Below I attach some captures of the problem and the code for the BS computation.

Captures:

https://imgur.com/a/FhGBwvW

The code for the computation of the BS can be found below (I decided to skip the code that gets the frustums corners in WS but I'm quite sure it's working okay as I can see the points being in their position in my render):


// This code is repeated for each cascade.		
		
		// Calculate the centroid of the view frustum slice.
		// cascade_frustas_corners contains all the corners of all the frustas the camera is divided in.
		// Their positions are already in WS.
		Vector3 frustumCenter;
		for (unsigned int j = 0; j < 8; ++j)
			frustumCenter = frustumCenter + cascade_frustas_corners[cascadeIdx * 8 + j];
		frustumCenter *= 1.0f / 8.0f;

		// Calculate the radius of a bounding sphere surrounding the frustum corners.
		float sphereRadius = 0.0f;
		for (unsigned int j = 0; j < 8; ++j)
		{
			float dist = (cascade_frustas_corners[cascadeIdx * 8 + j] - frustumCenter).Length();
			sphereRadius = std::max(sphereRadius, dist);
		}
		
		// Store the radius for this frusta.
		sphere_radius_frustas[cascadeIdx] = std::ceil(sphereRadius * 16.0f) / 16.0f;

 

Hopefully somebody can shine some light on my issue.

Thank you very much.

One simple thing you can do is to have each pixel always use the tightest cascade that overlaps it. They describe that here on slide 27: 

This won't fix your cascades overlapping with each other, but it does at least make sure that you're utilizing the higher-res cascades as much as possible:

I have this implemented in my shadows demo if you change the "Cascade Selection Mode" setting to "Projection"

Advertisement

Hi MJP,

Thanks for your reply and sorry for asnwering so late, I didn't have too much time these days to work on it. I Implemented your recommendation and I am quite satisfied with the increase in the covering area for each slice. I also tried finding a smaller bounding sphere by using the miniball algorithm but the hit on the performance vs the slight decrease in size of the bounding sphere didn't pay off.

Once again, thanks for the input.

In order to avoid making a new post, I would like to ask another question to see if somebody knows what might be the issue. While testing shadows on my scene I found out what appears to be a bug in my code. Given a light coming from the right side pointing forward and being sligtly tilted, there seems to be a discrepancy between the cascade shadows, as if some of the projections were off (as far as I know the computations are ok), shadows differ in size breaking between cascades. Images are provided at the end.

This issue only seems to happen on certain light angles. If, for example, I rotate the light slightly to the left or side (so it hits the scene diagonally) this issue dissappears. The use of a bounding sphere or a bounding box doesn't fix the bug, but it seems to be a bit less noticiable with bounding spheres. It has been bugging me a bit and I have been trying to fix it for a while but I am a bit lost right now. ¿Does anyone know where the error could be?

I attach some images and a gif in the links below:

https://imgur.com/a/BjWoAyK

https://gyazo.com/2e83c3f658b3beb2858dbaedfc743c04

Thank you very much.

 

 

How are you forming the "view" matrix for you shadow caster? If you're doing it by crossing the camera direction with an "up vector" to form an orthogonal basis (AKA the typical LookAt function for forming a view matrix) then you need to make sure that the forward and up vectors aren't pointing in the same direction, otherwise the result will be degenerate.

Hi MJP,

For most of the maths in my engine, including computing my view and projection matrices,  I'm using the SimpleMath.h wrapper from Microsoft for DirectXMath: https://github.com/Microsoft/DirectXTK/blob/master/Inc/SimpleMath.h.

The wrapper ends up calling the same XMMatrixLookToLH being used in your sample but first calls the XMMatrixLookAtRH from which the NegEyeDirection vector is computed. Maybe I'm missing something but, as far as I know, doesn't this  XMMatrixLookToLH function already consider such a case?

Could it be the fact that my Engine is supposed to be LH yet the functions being called are RH be the issue? I'm refollowing the sample and trying to see if there's something wrong.

Anyways, thanks a lot for the help already given and sorry for any inconvenience.

It doesn't look like that function does any checking to ensure that the result of the cross product isn't degenerate. But looking at your images I don't think that's your issue here, since you would be getting totally garbled results if the view matrix were malformed.

This topic is closed to new replies.

Advertisement