Parallel-Split Shadow Maps (PSSM): Shadows for large scenes


Here it is an implementation of the PSSM algorithm for Panda3D that has been developed for the Estrada Real Digital (ERD) game (a game from UFMG/Brazil university).

Using pssm, it’s possible to render real-time shadow maps on large terrains and with great quality. The algorithm basically splits the view frustum into different parts, so each part has a different shadow map. That way, it’s possible to maintain a good shadow map resolution, even on large scenes. For more details, you can find the paper here:

PSSM is basically the same as Cascaded Shadow maps (from NVIDIA).

In order to use pssm, all you have to do is:

self.pssm = ParallelSplitShadowMap.ParallelSplitShadowMap(Vec3(0, -1, -1), lightsQuality = [2048, 2048, 1024], pssmBias = 0.8, pushBias = 0.03, lightColor = VBase3(0.125, 0.149, 0.160), lightIntensity = 0.8)

The lightsQuality parameter tells the algorithm how many times you want to split your view frustum, and the quality of each shadow map. Another important parameter is pssmBias, wich is responsable for changing the clip position. You can also change the light color, intensity, etc…

At each frame, you have to call:


The python code handles the lights frustums positions, directions, and the calculations that are needed in order to change the width/height of the Orthographic lens.

The vertex shader is basically the same as the one found in the shadow map sample. The fragment shader, on the other hand, is a bit different, because it’s responsable for choosing what shadow map is going to be used at a particular fragment.

Here is a list of a few commands:
insert/delete: change the ‘sun’ position.
home/end: change the pssmBias.
page up/page down: change the pushBias.
f5: turn off the shader.
f6: view the buffers.
f7: view the different frustum parts (each one has a different color).

I have uploaded two videos to youtube:

You can see how good pssm really is. At a certain point, I zoom out to see the whole scene, and the shadow resolution is quite good.
You can also see the impact of the pssmBias change. As I increase its value, the depth of each part also increases, so a shadow map has to cover a larger area (decreasing the shadow quality).

Below, a few pictures:

Feel free to criticize. =)
There are a few minor bugs left, but I’m working on them. Oh, and I’m using an ATI video card, but it will probably work on a NVIDIA one.


Roaming Ralpha sample + PSSM:
(if you want to use the PSSM code, all you need is the PSSM folder)

Edit1: fixed link…
Edit2: Roaming Ralph sample + PSSM
Edit3: Roaming Ralph sample + PSSM, with a better visual quality. There is also a new .bat

Wow, that’s pretty cool. Thanks for sharing!

Thank you, fabiom! Great work!

really nice. since you seems to be intrested in directional lights you might want to look at this one,too->



Yeah, the author of the PSSM paper mentions the LISPSM algorithm on
his website. It has been used, together with PSSM, on the Lost Planet

Very cool !! Thank you for sharing !

Very cool. One question tho, is the code released under any license?

I would say that it is under public domain, but I used the vertex shader code from the Panda3d shadow map sample.

So, it’s safer to say that it’s under BSD license (which is very permissive).

Edit: grammar

Very cool, thanks for posting this code! I was about to ask if anyone has a sample scene to demonstrate this code with, but actually creating something of my own to test this with would make a good project for me to practice building games in Panda.

Here it is the the Roaming Ralpha sample + PSSM: … h-PSSM.rar

I did it in 5 minutos, so it’s pretty rought. :stuck_out_tongue:
But if you want, you can tweak the parameters to increase the visual quality.

It is amazing what shadows can do for realism.

While trying to run the Roaming Ralph Demo I get the following warnings/errors in the shadow shader:
:gobj(error): PSSM/shadow.sha: (44) : warning C7011: implicit cast from “float4” to “float3”
:gobj(error): PSSM/shadow.sha: (99) : warning C7011: implicit cast from “float4” to “float”
:gobj(error): PSSM/shadow.sha: (101) : error C1008: undefined variable “k_lightColor”
:gobj(error): PSSM/shadow.sha: (108) : warning C7011: implicit cast from “float4” to “float”
:gobj(error): PSSM/shadow.sha: (110) : error C1008: undefined variable “k_lightColor”
:gobj(error): PSSM/shadow.sha: (115) : warning C7011: implicit cast from “float4” to “float”
:gobj(error): PSSM/shadow.sha: (117) : error C1008: undefined variable “k_lightColor”
:gobj(error): PSSM/shadow.sha: (122) : warning C7011: implicit cast from “float4” to “float”
:gobj(error): PSSM/shadow.sha: (124) : error C1008: undefined variable “k_lightColor”

Needless to say, the application runs normally but I get no shadows.
I have an NVidia GTX280. I have tried with Panda 1.5.4 and 1.6.0. Am I doing something wrong?

Thanx guys

Maybe you forgot to setShaderInput(“lightColor”, ?

I don’t think so. This is the line you are referring to::


I just downloaded the .rar archive, unzipped it and ran it.
I don’t know anything about shaders, however the fragment shader in the shadow.sha that I downloaded does not seem to have any parameter named k_lightColor:

void fshader(in float2 l_texcoord0 : TEXCOORD0,
             in float4 l_shadowcoord0 : TEXCOORD1,
             in float4 l_shadowcoord1 : TEXCOORD2,
             in float4 l_shadowcoord2 : TEXCOORD3,
             in float4 l_shadowcoord3 : TEXCOORD4,
	     	 in float l_smooth : TEXCOORD5,
             in float4 l_fragCoord : WPOS, 
             uniform sampler2D tex_0 : TEXUNIT0,
             uniform sampler2D k_depthmap0 : TEXUNIT1,
             uniform sampler2D k_depthmap1 : TEXUNIT2,
             uniform sampler2D k_depthmap2 : TEXUNIT3,
             uniform sampler2D k_depthmap3 : TEXUNIT4,
             uniform float4 k_depth,
             uniform float4 k_ambient,
	     	 uniform float4 k_texDisable,
	     	 uniform float4 k_debugColors,
             out float4 o_color:COLOR)

The same goes for the vertex shader:

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

             uniform float4x4 trans_model_to_clip_of_light0,
             uniform float4x4 trans_model_to_clip_of_light1,
             uniform float4x4 trans_model_to_clip_of_light2,
             uniform float4x4 trans_model_to_clip_of_light3,
             uniform float4x4 mat_modelproj,
             uniform float4 mspos_light0,
             uniform float4 mspos_light1,
             uniform float4 mspos_light2,
             uniform float4 mspos_light3,
             uniform float4 k_ambient,
             uniform float4 k_scale,
             uniform float4 k_push,
             uniform float4 k_lightDirection,

             out float4 l_position : POSITION,
             out float2 l_texcoord0 : TEXCOORD0,
             out float4 l_shadowcoord0 : TEXCOORD1,
             out float4 l_shadowcoord1 : TEXCOORD2,
             out float4 l_shadowcoord2 : TEXCOORD3,
             out float4 l_shadowcoord3 : TEXCOORD4,
             out float l_smooth : TEXCOORD5

Maybe I downloaded corrupted files? Could anyone post a version of their shaders here?


Sounds like a problem with the shader - you should add this line to both the vertex and fragment shader, just to make sure:

             uniform float4 k_lightColor, 

You’re right. I tried it but now it seems that there is a shader input missing from the code because I get this error:
AssertionError: Shader input ambient is not present.
So if I comment out k_ambient in the shader(which btw does not seem to be used anywhere in the shader) then the demo runs, I get the shadows but I also get some weird effect on ralph.
Is k_ambient necessary, and what should be it’s input?


Are you sure you’re not just using the wrong shader?

Yes, I’m sure I’m using the shader I downloaded in the .rar archive. That’s why I asked if anyone could post their shadow.sha file just to make sure.

Oh, my bad.
Grab the updated code from the Roaming-ralph sample here: … h-PSSM.rar
(there is also a new .bat in case anyone wants to run the sample)

I have an ATI video card, so I was always using the shadow-nossuport.sha. But the shadow.sha should be working now.

If you find another problem, just post a new message. =)