This topic is 3139 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

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 on other sites
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 on other sites
@ 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 on other sites
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 on other sites
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 on other sites
Quote:
 Original post by osmanbPersonally, 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 on other sites
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 on other sites
Quote:
 Original post by osmanbAgreed, 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 on other sites
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 on other sites
Fair enough answer I guess. Lets see what amazon has to offer...

##### Share on other sites
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 on other sites
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 on other sites
Just a question; how is the lack of shimmer with movement solved?

##### Share on other sites
@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:

##### Share on other sites
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 splitfloat3	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 on other sites
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 on other sites
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 on other sites
Quote:
 Original post by ViikFrom 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 ViikThis 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 ViikAnother 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 casegetShadowCoord() 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.

##### Share on other sites
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 on other sites
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 on other sites

This topic is 3139 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628710
• Total Posts
2984335

• 23
• 11
• 10
• 13
• 14