Sign in to follow this  
Viik

Cascaded or parallel-split shadow maps Bounding Box slice selection

Recommended Posts

Viik    252
On last GDC, Johan Andersson explained their approach on stabilizing cascaded shadow maps in Frostbite. I saw only slides. Stabilization part is quite clear to me, but I can't understand from slides how Bounding Box slice selection is done. Dose anybody have more information about that or explaine how it works? Btw, what is the principal difference between cascaded and parallel-split shadow maps?

Share this post


Link to post
Share on other sites
wolf    852
Quote:
Btw, what is the principal difference between cascaded and parallel-split shadow maps?
There is none. The word Cascaded Shadow maps describes how the light view frustums slice up the view frustum if you look at them from above in 2D ... it just looks like a cascade. Parallel-Split Shadow maps describe a way to calculate the distance from the camera of each of those slices and how to split them up. It is a way to divide a view frustum in slices. It is an algorithm to calculate splits dynamically.

Share this post


Link to post
Share on other sites
Viik    252
@ Ahh, now I see. Thank you for explanation.
You've mentioned ones on forum, that you did Bounding Sphere slice selection (Killzone2) for shadow map lookup. Looks like it greatly improves utilization of shadow maps. Can you explaine how it's done? If its not a "trade secret".

Share this post


Link to post
Share on other sites
wolf    852
Using bounding spheres greatly simplifies the usage of Cascaded Shadow Maps. It is just easy to think of the light view frustum to rotate around a sphere enclosing a frustum split like a joint.

Share this post


Link to post
Share on other sites
wolf    852
You want to check out the Cascaded Shadow Map article in ShaderX7 ... it should answer most questions regarding CSM.

Share this post


Link to post
Share on other sites
osmanb    2082
Agreed, the sphere techniques have worked very well for us. One thing to be careful about is the FOV of your camera. Everything will continue to work fine, but if your camera has a wide FOV, the bounding spheres will be very large. In some cases, you can use this to your advantage to make the shader logic for picking the correct cascade even faster.

Personally, though, I think the most important feature missing from 90% of all CSM implementations is blending between splits. I pay close attention to the light and shadow implementations in every game I play, and nearly every title (including major AAA stuff) has terrible seams between splits. It's not *that* hard to blend across a short distance, and the visual improvement is drastic.

Share this post


Link to post
Share on other sites
MJP    19754
Quote:
Original post by osmanb
Personally, though, I think the most important feature missing from 90% of all CSM implementations is blending between splits. I pay close attention to the light and shadow implementations in every game I play, and nearly every title (including major AAA stuff) has terrible seams between splits. It's not *that* hard to blend across a short distance, and the visual improvement is drastic.


*shrug*

I wouldn't classify it as important. I really really doubt anybody notices it...heck I don't even notice it unless I'm looking for it.

Share this post


Link to post
Share on other sites
osmanb    2082
Yeah, I guess I'm really focused on that right now, because I've been working on it. And really, the lack of shimmer from camera movement is far more important. These are all solved problems, and we continue to see major titles ship with first-year student project quality shadow mapping. It's actually sort of pathetic how bad most CSM implementations are.

Share this post


Link to post
Share on other sites
B_old    689
Quote:
Original post by osmanb
Agreed, the sphere techniques have worked very well for us. One thing to be careful about is the FOV of your camera. Everything will continue to work fine, but if your camera has a wide FOV, the bounding spheres will be very large. In some cases, you can use this to your advantage to make the shader logic for picking the correct cascade even faster.

Sorry for hijacking the thread, but I stumbled over this sphere technique a couple of times but can't find more detailed info on it. It would be cool if someone could shed some more light on this.

Share this post


Link to post
Share on other sites
osmanb    2082
I implemented it based on information from the ShaderX6 article "Stable Rendering of Cascaded Shadow Maps." I think the ShaderX7 article that Wolf mentioned above supercedes that one, (and includes more details on some of the tricky bits).

Share this post


Link to post
Share on other sites
Viik    252
I read these articles and want to be sure that we was talking about same things. Tryed aproximate solution from ShaderX 7 article for shadows stabilization, works great, I really like it, very simple but so effective.
Than there was a solution proposed for cases when the same point is rendered into several split shadow maps and we choose the best one. But I have impression that Johan Andersson on GDC was expaining a better approach on split's shadow maps utilization, what he called "Bounding Box slice selection" or this was just a fix for cases when point is in several shadows maps at the same time?

Share this post


Link to post
Share on other sites
Viik    252
Stupid me, next slide shows actuall code of this Bounding Box slice selection approach ))) Now I understand it better - we check calculated shadow map UVs for every point: if uv is proper (0..1) than point is in this slice, if point have proper UV's in more than one slice, we choose closest one.

Share this post


Link to post
Share on other sites
Viik    252
@Matt Aufderheide
Aproximate solution described in ShaderX 7 4.1, is improved version of technique described in ShaderX 6 Stable Rendering of CSM, it utilizes shadow map space much better. At the same time shadow will be stable to some degree of camera movement or rotation, so there will be a point where it will change, but this is not happening every frame, in my scene and constants suggested in book I don't see any shadow map movement when camera rotates as well as moves a bit, it changes when you move further but it's hard to notice because of the actuall movement, maybe my camera is running too fast.

I've implemented split selection scheme similar to described by Johan Andersson: first, check if current point can be inside each split (this is done by checking UV's that would be used to sample shadow maps in every split, simple 0..1 check), if you have more than one proper split than sample closest one to camera. This is comparison with regular by depth selection of split:
Parallel-split shadow maps Split selection methods

Share this post


Link to post
Share on other sites
Viik    252
I'm not using texture arrays or atlas, just three separate shadow maps for three splits. This is code for selecting best split:

// "focused" is a {light view croped projection matrix}
float4 toSunPos1 = mul(float4(pixelWorldPos, 1), focused1);
float4 toSunPos2 = mul(float4(pixelWorldPos, 1), focused2);
float4 toSunPos3 = mul(float4(pixelWorldPos, 1), focused3);

// pixel's shadow map sampling coordinates for each split
float3 shadowCoord1 = getShadowCoord(toSunPos1);
float3 shadowCoord2 = getShadowCoord(toSunPos2);
float3 shadowCoord3 = getShadowCoord(toSunPos3);

//check that UV's are in 0..1 range, don't forget to check XYZ and not only XY (UV)
bool s1 = all(abs(shadowCoord1 - 0.5f) < 0.5f);
bool s2 = all(abs(shadowCoord2 - 0.5f) < 0.5f);
bool s3 = all(abs(shadowCoord3 - 0.5f) < 0.5f);

if(s1)
{
... sample shadow map of first split ...
}
else if(s2)
{
... sample shadow map of second split ...
}
else if(s3)
{
... sample shadow map of third split ...
}




This is not optimal way for sure and it uses branching, so only SM3.0.

[Edited by - Viik on May 8, 2009 3:33:28 PM]

Share this post


Link to post
Share on other sites
B_old    689
Hi Viik, thanks for the code you posted. I tried to apply it to my shadow maps and it almost works. :) That is, I have situations were it fails.

Should this work regardless of the way I generate my shadow map? I am under the impression, that I sometimes choose the closer split although it doesn't contain the shadow data I am looking for.

Here is what I do:
//This is just to decide between the first or second split!
float4 pos0 = mul(float4(viewPos.xyz, 1.0f), g_shadowTransforms[0]);
float4 pos1 = mul(float4(viewPos.xyz, 1.0f), g_shadowTransforms[1]);

float3 shadowProj0 = pos0.xyz / pos0.w;
float3 shadowProj1 = pos1.xyz / pos1.w;

//I use an atlas with 4 shadowmaps in one texture, so the first shadowmap should be between 0 and 0.5 for uv.
if (shadowProj0.x > 0.f && shadowProj0.x < 0.5f
&& shadowProj0.y > 0.f && shadowProj0.y < 0.5f
&& shadowProj0.z >= 0.f && shadowProj0.z < 1.0f)
{
//sample the first split
}
else
{
//sample the second split
}



Can you so a mistake in the code? Maybe I have to generate the map in a special way though...

EDIT:
I believe I have to slightly adjust the way I generate shadowmaps. When I render my objects into the shadowmap I cull those objects that don't intersect the volume, which the split-section of the camerafrustum extended towards the light makes up (I believe you know what I mean). This works perfectly well with paralles splits. If I use the bounding box shader code though, this can be a problem.
Now I need to figure out another way to cull objects when generating the shadow maps. Not really sure, what to do there though.

[Edited by - B_old on May 12, 2009 9:25:58 AM]

Share this post


Link to post
Share on other sites
Viik    252
From vector name I would say that you transform pixel view position into shadow map space instead of pixel world position:
float4 pos0 = mul(float4(viewPos.xyz, 1.0f), g_shadowTransforms[0]);

This approach is used when your split shadow maps overlap, so objects that belong to overlap section would not be culled and will be rendered into both shadow maps. Just check what you see in each split.

Another thing, are you sure that:
float3 shadowProj0 = pos0.xyz / pos0.w;
float3 shadowProj1 = pos1.xyz / pos1.w;
is enought for you to get proper shadow map space coordinates? As in my case
getShadowCoord() is more complex, as I need to move coordinates to 0..1 range and invert Y direction:
float3	getShadowCoord(float4 postAtLight)
{
float3 shadowCoord = (0.5 * postAtLight.xyz / postAtLight.w) + float3( 0.5, 0.5, 0.5 );
shadowCoord.y = 1.0f - shadowCoord.y;
return shadowCoord;
}

Share this post


Link to post
Share on other sites
B_old    689
Quote:
Original post by Viik
From vector name I would say that you transform pixel view position into shadow map space instead of pixel world position:
float4 pos0 = mul(float4(viewPos.xyz, 1.0f), g_shadowTransforms[0]);

Indeed, but we both end up with the pixel position in lightspace. I hope.
Quote:
Original post by Viik
This approach is used when your split shadow maps overlap, so objects that belong to overlap section would not be culled and will be rendered into both shadow maps. Just check what you see in each split.

I don't understand. What approach?

Quote:
Original post by Viik
Another thing, are you sure that:
float3 shadowProj0 = pos0.xyz / pos0.w;
float3 shadowProj1 = pos1.xyz / pos1.w;
is enought for you to get proper shadow map space coordinates? As in my case
getShadowCoord() is more complex, as I need to move coordinates to 0..1 range and invert Y direction:
*** Source Snippet Removed ***

Yes I am sure, my coordinates already are in the correct range. The matrix g_shadowTransform does all this in fact.

Actually I can get it to work. The biggest problem I have now is to determine which objects exactly I have to draw.
Thanks for the quick reply.

Share this post


Link to post
Share on other sites
Viik    252
Quote:
I don't understand. What approach?

Approach of choosing split not by view space depth, but by spheres or split bounding box.

Btw, I remember that I had a bug like you described when in first implementation I didn't checked Z value of calculated texture coordinates.

Share this post


Link to post
Share on other sites
B_old    689
OK, I fiddled around a little bit more and I like it. The quality is really cool. Although I do have to render more in the shadow map generation pass and that is noticeable performance wise. I will have to see what a difference it makes in a more complex scene, but I think the improved quality is worth it. Thanks for clarifying this a bit!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this