Hires shadowmaps for large terrains

I like to make games with large outdoor areas to explore. I also want the sun (at least) to cast shadows.

But if you make a shadowmap for the entire terrain it will be very low resolution and look really ugly.

So I decided that a good comptomise would be to display hires shadows for objects close to the camera, and have these shadows fade-out as you move away (and possibly replaced with simple ‘drop’ shadows).

The great thing about this is that it’s really easy to implement. I use an orthographic light camera for the sun, and attach it to the main camera so that it moves with the camera and points directly at it (or a little in front) in the right direction for the sun. At the moment I’m doing this manually, but it would probably be better to use a compassEffect.

The only other requirement is a small change to the shadow shader as shown below:

//Cg

void vshader(float4 vtx_position : POSITION,
             float2 vtx_texcoord0: TEXCOORD0,
             float3 vtx_normal: NORMAL,

             uniform float4x4 trans_model_to_clip_of_light,
             uniform float4x4 mat_modelproj,
             uniform float4 mspos_light,
             uniform float4 k_ambient,
             uniform float4 k_scale,
             uniform float4 k_push,

             out float4 l_position : POSITION,
             out float2 l_texcoord0 : TEXCOORD0,
             out float4 l_shadowcoord : TEXCOORD1,
	     out float  l_smooth : TEXCOORD2,
	     out float4 l_lightclip : TEXCOORD3
             )

{
float4 position = vtx_position * k_scale;

// vertex position
l_position = mul(mat_modelproj, position);

// Pass through texture coordinate for main texture.
l_texcoord0 = vtx_texcoord0;

// Calculate the surface lighting factor.
l_smooth = saturate(dot(vtx_normal, normalize(mspos_light - position)));

// Calculate light-space clip position.
float4 pushed = position + float4(vtx_normal * k_push, 0);
l_lightclip = mul(trans_model_to_clip_of_light, pushed);

// Calculate shadow-map texture coordinates.
l_shadowcoord = l_lightclip * float4(0.5,0.5,0.5,1.0) + l_lightclip.w * float4(0.5,0.5,0.5,0.0);
}


void fshader(in float2 l_texcoord0 : TEXCOORD0,
             in float4 l_shadowcoord : TEXCOORD1,
             in float  l_smooth : TEXCOORD2,
             in float4 l_lightclip : TEXCOORD3,
             uniform sampler2D tex_0 : TEXUNIT0,
             uniform sampler2D k_Ldepthmap : TEXUNIT1,
             uniform float4 k_ambient,
	     uniform float4 k_texDisable,
             out float4 o_color:COLOR)
{
  float3 circleoffs = float3(l_lightclip.xy / l_lightclip.w, 0);
  float falloff = saturate(1.0 - dot(circleoffs, circleoffs));
  float4 baseColor = saturate(tex2D(tex_0, l_texcoord0) + k_texDisable);
  float shade = tex2Dproj(k_Ldepthmap,l_shadowcoord);
  
  /***** this is the only line that has changed *****/
  o_color = baseColor * ( falloff * shade * l_smooth + (1.0-falloff)*l_smooth + k_ambient.x );
}

Obviously you can replace l_smooth with whatever other lighting calculations you want to make. I’d also like to smooth the shadows to make them look even better.

So what do you think? Can you think of any further improvements? Is anybody else using a similar method?

That is a great and easy solution for large outdoor areas.

I have tried your code, but I see one problem:
Lets say that the player is in a city, and he is standing next to a house. If the light source is behind the wall (so inside the house), that wall won’t cast shadows (because of the face normals).
The only solution would be to get the light source outside of the house. That way, it would cast the rooftop’s shadow. But then we would have the same problem: if we get too far from the player, we would have a low resolution shadow map.

I have been looking for some shadow maps solutions for large areas, and the best one I could find was the Trapezoidal Shadow map:
comp.nus.edu.sg/~tants/tsm.html
(take a look at the videos, and the comparassion between standard shadow map, perspective shadow map, and the trapezoidal shadow map. It looks really great, and they’re using only one 1024x1024 shadow map for all the area).

Another good one is the Cascaded shadow map:
developer.download.nvidia.com/SD … w_maps.pdf

Unfortunately, I think that it would be quite hard to implement it on Panda3D. Has anyone ever tried?

There’s no problem putting the shadow camera a long way off, just reduce the FOV of the camera. Well you could get a problem with depth resolution, but it would have to be a pretty long way off for that.

The other techniques look cool - I’ll have a closer look at them when I get a chance, thanks.

I uploaded a video showing the Parallel-Split Shadow Maps (http://appsrv.cse.cuhk.edu.hk/~fzhang/pssm_project/) that we’ve implemented for our game:

http://www.youtube.com/watch?v=pPty2_g35t8 (the normal quality is really terrible. Watch in HD)

There are three shadow maps (one 1024x1024 and two 512x512) that cover all the view frustum. The colors (red, green or blue) show what shadow map is being used at a particular fragment.

I will try to post the python and the shader code later this week, in case someone wants to use it. I have to change a few things before…

whow. looks really nice :slight_smile: would be great if you can post it.

Awesome! It would be great if you share the code :slight_smile:
Is it compatible with the Panda’s shader generator, by the way?

No, it’s not compatible :frowning:
Maybe someday someone will integrate the parallel-split shadow map technique with panda’s c++ code (and the shader generator). Then Panda3D will have a shadow system on par with the one found on Ogre3D.

We have been using Panda3D for almost two years now and we hope to finish our game (Estrada Real Digital) by the end of the month. Maybe then I will have some free time to implement a shadow system using c++. But I have no idea how hard that would be :stuck_out_tongue:

The shader is pretty much the same as the one found in the shadow map sample.
The only difference is the way the light direction is handled, and how the distance between the fragment/pixel and the camera determines what shadow map is going to be used.
The python code handles the lights frustums positions, directions, and the operations needed to get the camera’s far/height/width on each frame.

I will post the python and shader code by the end of the week.

hey, panda3D engine is missing in the pssm_project demo list!
fabiom you should make justice of this ASA you finish your work! :wink:
besides, I just can’t wait to see it finished and I hope you’ll post here a brief of your experience with P3D, how did you planned and developed your project - I guess many ppl here will be amazed to see it.