shadow mapping ...'again' ( 1.6 problem)

Hi.
Sorry to bother you once again for a topix that I already ask a lot.

But I still can’t have my shadow to work well, and i’m out of idea.
I’ve try with the parallel split algorithm. ( idea from fabiom ( discourse.panda3d.org/viewtopic.php?t=5943))

It doesn’t seems to have improved.

I use only one directional light, on a pretty large world ( 20002000). Every mesh on this world is a cube ( 12 triangles) and can be small ( 222) to big(404040)
I’ve try to use very big shadow_map ( 4000
4000), change bias, use a lot of small cube instead of big one,…
I haven’t got any more idea on what can cause this, or what I’m doing wrong, so any idea is welcome.

since depth texture still crahs on some of the box here, I emulate this with shader and Standard texture.

The thing is…
have you got any idea of what I do wrong or what can cause this ?
( I know is more a general programming problem but,…)

shadow_caster:

light_depthmap = Texture()
light_depthmap.setFormat(Texture.FLuminance)  # to store depth value instead of color
light_depthmap.setMinfilter(Texture.FTShadow)      # to use OpenGL SGIX_shadow extension
light_depthmap.setMagfilter(Texture.FTShadow)
light_buffer   = base.win.makeTextureBuffer('depthmap%i'%index, BUFFER_SIZE, BUFFER_SIZE, light_depthmap)
light_buffer.setClearColor(Point4(1,1,1,1))
light_cam = base.makeCamera(light_buffer)
light_cam.reparentTo(self.spotholder)
lens = OrthographicLens()
lens.setNearFar(100.0,max(self.world_size))
light_cam.node().setLens(lens)
light_cam.setPos(self.spotholder, 0,0,0)
light_cam.setHpr(self.spotholder, 0,0,0)
myShaderAttrib = ShaderAttrib.make()
shadowshader = loader.loadShader(pssm_caster%i.sha'%index))
standard_attrib = myShaderAttrib.setShader(shadowshader,1000)
standard_state = render.getState().addAttrib(standard_attrib)
light_cam.node().setInitialState(standard_state)
render.setShaderInput('shadow%i'%index, light_cam)
return light_depthmap, light_buffer, light_cam

shadow_receiver:

 myShaderAttrib  = ShaderAttrib.make()
        shadowshader    = loader.loadShader(ResourcePath('shader/pssm_receiver.sha').for_panda())
        standard_attrib = myShaderAttrib.setShader(shadowshader,1000)
        standard_state  = render.getState().addAttrib(standard_attrib)
        standard_state  = standard_state.addAttrib(standard_attrib)
        self.shadow_cam.node().setInitialState(standard_state)
        #==
        card_maker = CardMaker('')
        card_maker.setFrameFullscreenQuad()
        self.filter = NodePath(card_maker.generate())
        self.filter.setTexture(self.shadow_map)
        mycolorAttrib = ColorBlendAttrib.make(ColorBlendAttrib.MInvSubtract)
        standard_state = self.filter.getState().addAttrib(mycolorAttrib)
        self.filter.reparentTo(render2d)
        self.filter.setBin('unsorted',50)
        self.filter.setState(standard_state)

shaders:

//Cg
//Cg profile arbvp1 arbfp1

void vshader(   float4 vtx_position         : POSITION,
                uniform float4x4 mat_modelproj,
                in uniform float4x4 trans_model_to_clip_of_shadow0,

                out float4 l_position       : POSITION,
                out float4 l_pos            : TEXCOORD0
)
{

    l_position = mul(mat_modelproj, vtx_position);

    // scale matrix
    float4x4 scaleBiasMatrix = {
    0.5f,0.0f,0.0f, .5,//+0.0001,
    0.0f,0.5f,0.0f, .5,//+0.0001,
    0.0f,0.0f,0.5f, .5,
    0.0f,0.0f,0.0f, 1.0f
    };

    float4x4 textureMat = mul(scaleBiasMatrix, trans_model_to_clip_of_shadow0);
    l_pos = mul(textureMat, vtx_position);
}

void fshader(   in float4 l_pos :TEXCOORD0,// POSITION,
                out float4 o_color:COLOR)
{
    o_color = float4(l_pos.z, l_pos.z, l_pos.z, 1);// color.w);
}

shader_receiver( 4 shadow_map)

void fshader( 
              in float4 l_position  : TEXCOORD0,
              in float4 l_shade0    : TEXCOORD1,
              in float4 l_shade1    : TEXCOORD2,
              in float4 l_shade2    : TEXCOORD3,
              in float4 l_shade3    : TEXCOORD4,
             // in float4 l_distance  : TEXCOORD4,

              in uniform float4 k_parallelesplit,
              in uniform float4 k_bias,

              in uniform sampler2D k_Ldepthmap0 : TEXUNIT0,
              in uniform sampler2D k_Ldepthmap1 : TEXUNIT1,
              in uniform sampler2D k_Ldepthmap2 : TEXUNIT2,
              in uniform sampler2D k_Ldepthmap3 : TEXUNIT3,

              out float4 o_color : COLOR )
{
    float3 shadowUV0   = l_shade0.xyz / l_shade0.w ;
    float3 shadowUV1   = l_shade1.xyz / l_shade1.w ;
    float3 shadowUV2   = l_shade2.xyz / l_shade2.w ;
    float3 shadowUV3   = l_shade3.xyz / l_shade3.w ;
    float shadowTerm0  = 1 ;
    float shadowTerm1  = 1 ;
    float shadowTerm2  = 1 ;
    float shadowTerm3  = 1 ;

    shadowTerm0 = tex2D(k_Ldepthmap0, shadowUV0.xy).x ;
    if (shadowTerm0 < l_shade0.z -k_bias[0]  )
        shadowTerm0 = 0.8 ;
    else
        shadowTerm0 = 1.0 ;
    shadowTerm1 = tex2D(k_Ldepthmap1, shadowUV1.xy).x ;
    if (shadowTerm1 < l_shade1.z - k_bias[1]  )
        shadowTerm1 = 0.8 ;
    else
        shadowTerm1 = 1.0 ;
    shadowTerm2 = tex2D(k_Ldepthmap2, shadowUV2.xy).x ;
    if (shadowTerm2 < l_shade2.z - k_bias[2]  )
        shadowTerm2 = 0.8 ;
    else
        shadowTerm2 = 1.0 ;
    shadowTerm3 = tex2D(k_Ldepthmap3, shadowUV3.xy).x ;
    if (shadowTerm3 < l_shade3.z - k_bias[3]  )
        shadowTerm3 = 0.8 ;
    else
        shadowTerm3 = 1.0 ;

    float shadowTerm = shadowTerm3 ;
    if (l_position.z < k_parallelesplit[2])
    {
        shadowTerm = shadowTerm2 ;
    }
    if (l_position.z < k_parallelesplit[1])
    {
        shadowTerm = shadowTerm1 ;
    }
    if (l_position.z < k_parallelesplit[0] )
    {
        shadowTerm = shadowTerm0 ;
    }
    o_color = float4(1-shadowTerm,1-shadowTerm,1-shadowTerm,1);
}

First of all, you’re not using any shadow filtering. You’ll get less artifacts if you enable shadow filtering and use shadow samplers instead of texture samplers.

Next, 4000*4000 is a weird size. Try changing that to 4096x4096 or so.

Also, modify the shader to project the depth map onto the scene - that way it will be easier to see what’s wrong.

EDIT: Wait a minute. You are using ARB_shadow. But then why is the shader code still comparing the depth value against proj.z?

4000*4000 was just an example, actually I use 1024x1024

like I said, declaring a texture.depthcomponant create a seg fault with ubuntu and some of the nvidia drivers here ( I won’t say more, I already had 2 threads on the subject)
I have kept minfilter.FTShadow but It doesn’t change anything.

or else I don’t understand what you mean ?

hee, what_do_you_mean exactly ?

But a comparison like this:

shadowTerm0 < l_shade0.z -k_bias[0]

Doesn’t really make sense with FTShadow enabled, does it?

what do you mean ?, what should I do then ?

First of all, use sampler2DShadow and shadow2D. Then, the value returned by shadow2D should already be 0 or 1 so you do not need to compare against proj.z anymore.

But I’m not sure if that’s the problem at all - but it sure looks weird.

I can’t find find any reference to sampler2DShadow or Shadow2D in the cg reference manual, so I’m not sure exactly want do you mean,…

Have you read about how ARB_shadow works, or googled about shadow mapping?

The shader would look something like this: (Untested. I do shadows using shadow2DProj myself)

void fshader(
              in float4 l_position  : TEXCOORD0,
              in float4 l_shade0    : TEXCOORD1,
              in float4 l_shade1    : TEXCOORD2,
              in float4 l_shade2    : TEXCOORD3,
              in float4 l_shade3    : TEXCOORD4,
             // in float4 l_distance  : TEXCOORD4,

              in uniform float4 k_parallelesplit,
              in uniform float4 k_bias,

              in uniform sampler2DShadow k_Ldepthmap0 : TEXUNIT0,
              in uniform sampler2DShadow k_Ldepthmap1 : TEXUNIT1,
              in uniform sampler2DShadow k_Ldepthmap2 : TEXUNIT2,
              in uniform sampler2DShadow k_Ldepthmap3 : TEXUNIT3,

              out float4 o_color : COLOR )
{
    float3 shadowUV0   = l_shade0.xyz / l_shade0.w ;
    float3 shadowUV1   = l_shade1.xyz / l_shade1.w ;
    float3 shadowUV2   = l_shade2.xyz / l_shade2.w ;
    float3 shadowUV3   = l_shade3.xyz / l_shade3.w ;
    float shadowTerm0  = 1 ;
    float shadowTerm1  = 1 ;
    float shadowTerm2  = 1 ;
    float shadowTerm3  = 1 ;

    shadowTerm0 = 0.8+0.2*shadow2D(k_Ldepthmap0, shadowUV0.xy) ;
    shadowTerm1 = 0.8+0.2*shadow2D(k_Ldepthmap1, shadowUV1.xy) ;
    shadowTerm2 = 0.8+0.2*shadow2D(k_Ldepthmap2, shadowUV2.xy) ;
    shadowTerm3 = 0.8+0.2*shadow2D(k_Ldepthmap3, shadowUV3.xy) ;

    float shadowTerm = shadowTerm3 ;
    if (l_position.z < k_parallelesplit[2])
    {
        shadowTerm = shadowTerm2 ;
    }
    if (l_position.z < k_parallelesplit[1])
    {
        shadowTerm = shadowTerm1 ;
    }
    if (l_position.z < k_parallelesplit[0] )
    {
        shadowTerm = shadowTerm0 ;
    }
    o_color = float4(1-shadowTerm,1-shadowTerm,1-shadowTerm,1);
}

However, the bias would have to be moved to the coordinate calculation. Maybe it’s better to make the texture projected and use projection coordinates.

PS. Note that this shader could easily be made at least 4x faster by limiting the number of texture reads and compares.

Hi SylHar,

I think that you should check the pssmBias and see if it improves things.
Another thing, there is a function called adjustCameraFar on the PSSM code. You should set your camera’s far so that it is as tight as possible You can’t leave a lot of ‘empty’ space on the view frustum. That way, you will be making sure that a shadow map is always beeing used to render shadows.

You should also check the buffers viewers (base.bufferViewer.toggleEnable).

thank all of you, my result is now nearly correct.

what made a difference: the pssmbias was really too low ( 0.5) and the use of tex2Dproj greatly improve quality as well.

But the exact problem I was having was this Scale.

All the cube I used were unit cubes scaled with a setScale.
Which mean that the bias of the cube where using it. so instead of having 0.5 bias, I was having a 17. bias which through strange result :stuck_out_tongue:

So it’s not perfect but it’s now usable.

I’m trying to blur it know, see what good that can do

the other problem I still have, but I can’t think that can really be resolved is that the values for bias and pssmbias should really different for each of my scene, so I can’t really write them hard so now. ( since my scene are procedurally created )

I don’t think that you need to set the pssmbias very often. At least you shouldn’t need.

The code should have to set the camera’s far and near automatically each frame, so you never have a lot of “empty” space.

This wasn’t described on the original paper, but only on the Directx9 implementation of the PSSM algorithm (http://hax.fi/asko/PSSM.html). The author says:

Adjusting the near plane doesn’t make a lot of difference for a third person game, for example.
But if you’re making an strategy game, where you can ‘fly’ with the camera, than adjusting the near plane will greatly improve the quality.

I will probably modify the pssm code to support that later on.

hi again.

my shadow looked good and I was changing subject when I changed to panda3D1.6.0

under linux, no problem. ( well I still have a problem but in 1.5.4 too, more on that later)
under windows, nothing work anymore:

no change in bias / pssm bias change anything.

changing

light_depthmap.setMinfilter(Texture.FTShadow)    
light_depthmap.setMagfilter(Texture.FTShadow)

to

light_depthmap.setMinfilter(Texture.FTLinear)
light_depthmap.setMagfilter(Texture.FTLinear)

reduce the effect but not suppress it

so, no idea/ solution for this 1.6 problem (with window only ) ?

the creation of the buffer/camera:

 light_depthmap = Texture()
        light_depthmap.setFormat(Texture.FDepthComponent)
        light_depthmap.setComponentType(Texture.TFloat)
        light_depthmap.setMinfilter(Texture.FTShadow)      
        light_depthmap.setMagfilter(Texture.FTShadow)

        buff_size = BUFFER_SIZE
        light_buffer   = base.win.makeTextureBuffer('depthmap%i'%index, buff_size, buff_size, light_depthmap)
        light_buffer.setClearColor(Point4(1,1,1,1))
        light_cam = base.makeCamera(light_buffer)
        light_cam.reparentTo(self.spotholder)
        lens = OrthographicLens()
        lens.setNearFar(10.0,max(self.world_size))
        light_cam.node().setLens(lens)
        light_cam.setPos(self.spotholder, 0,0,0)
        light_cam.setHpr(self.spotholder, 0,0,0)
        render_attrib    = CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise)
        standard_state  = render.getState().addAttrib(render_attrib)
        light_cam.node().setInitialState(standard_state)
        render.setShaderInput('shadow%i'%index, light_cam)

and the creation of the card shadow rendered:

        self.shadow_map = Texture()
        self.shadow_map.setFormat(Texture.FRgb)
        self.shadow_buffer = createOffscreenBuffer(-1)
        self.shadow_buffer.addRenderTexture(self.shadow_map, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
        self.shadow_buffer.setClearColor(Point4(0,0,0,0))
        self.shadow_cam = base.makeCamera(self.shadow_buffer)
        camera_mask = BitMask32()
        camera_mask.setBit(0)
        self.shadow_cam.node().setCameraMask(camera_mask)
        self.shadow_cam.reparentTo(base.cam)
        lens = PerspectiveLens()
        lens.setNearFar(10.0,2000)
        lens.setFov(base.cam.node().getLens().getFov())
        self.shadow_cam.node().setLens(lens)
        self.shadow_cam.setPos(base.cam, 0,0,0)
        self.shadow_cam.setHpr(base.cam, 0,0,0)
        myShaderAttrib  = ShaderAttrib.make()
        shadowshader    = loader.loadShader(ResourcePath('shader/pssm_receiver2.sha').for_panda())
        standard_attrib = myShaderAttrib.setShader(shadowshader,1000)
        standard_state  = render.getState().addAttrib(standard_attrib)
        self.shadow_cam.node().setInitialState(standard_state)
        self.blurXBuffer = makeFilterBuffer(self.shadow_buffer, "Blur X", -1, loader.loadShader(ResourcePath('shader/blurx.sha').for_panda()))
        self.blurYBuffer = makeFilterBuffer(self.blurXBuffer,   "Blur Y", -1, loader.loadShader(ResourcePath('shader/blury.sha').for_panda()))
        card_maker = CardMaker('')
        card_maker.setFrameFullscreenQuad()
        self.shadow_filter = NodePath(card_maker.generate())
        self.shadow_filter.setTexture(self.blurYBuffer.getTexture())
        mycolorAttrib = ColorBlendAttrib.make(ColorBlendAttrib.MInvSubtract)
        standard_state = self.shadow_filter.getState().addAttrib(mycolorAttrib)
        self.shadow_filter.reparentTo(render2d)
        self.shadow_filter.setBin('unsorted',50)
        self.shadow_filter.setState(standard_state)

Sorry to bump a little this topic again.
But this is the last thing that block us from going to 1.6.

After some other test, I still have no idea why this code doesn’t work on windows/panda1.6.X
(still work in linux/panda1.5.4 linux/panda1.6.x and windows/panda1.5.4

Any idea what can cause this ? the slightest idea will be helpful,…

Well, what does your shader look like?

I don’t see what might have changed in 1.6 - well, one thing maybe. Try replacing FDepthComponent with FDepthStencil - it’s faster anyways and will fallback to FDepthComponent if not supported.

The screenshot and creation of the camera are just ahead.

The shader:
caster:

//Cg
//Cg profile arbvp1 arbfp1

void vshader(   float4 vtx_position         : POSITION,
                uniform float4x4 mat_modelproj,

                out float4 l_position       : POSITION,
                out float4 l_pos            : TEXCOORD0
)
{
    float4 position = vtx_position; 
    l_position = mul(mat_modelproj, vtx_position);
    l_pos = mul(mat_modelproj, vtx_position);

    
    // scale matrix
    float4x4 scaleBiasMatrix = {
    0.5f,0.0f,0.0f, .5,//+0.0001,
    0.0f,0.5f,0.0f, .5,//+0.0001,
    0.0f,0.0f,0.5f, .5,
    0.0f,0.0f,0.0f, 1.0f
    };
}

void fshader(   in float4 l_pos :TEXCOORD0,// POSITION,
              //  in float2 l_texcoord : TEXCOORD1,
                
               // uniform sampler2D tex_0    : TEXUNIT0, //texture 

                out float4 o_color:COLOR)
{
    o_color = float4(l_pos.z, l_pos.z, l_pos.z, 1);
}

and receiver:

/Cg
//Cg profile vp_3_0 fp_3_0

void vshader( in float4 vtx_position  : POSITION,
              in float3 vtx_normal    : NORMAL,
               

              in uniform float4x4 mat_modelproj,
              in uniform float4x4 trans_model_to_clip_of_shadow0,
              in uniform float4x4 trans_model_to_clip_of_shadow1,
              in uniform float4x4 trans_model_to_clip_of_shadow2,
              in uniform float4x4 trans_model_to_clip_of_shadow3,

              out float4 l_position  : TEXCOORD0,
              out float4 l_shade0    : TEXCOORD1,
              out float4 l_shade1    : TEXCOORD2,
              out float4 l_shade2    : TEXCOORD3,
              out float4 l_shade3    : TEXCOORD4,
              out float4 l_pos       : POSITION)
{
    l_position  = mul( mat_modelproj, vtx_position );
    l_pos       = l_position;

     // scale matrix
    float4x4 scaleBiasMatrix = {
    0.5f,0.0f,0.0f, .5+0.0001,
    0.0f,0.5f,0.0f, .5+0.0001,
    0.0f,0.0f,0.5f, .5,
    0.0f,0.0f,0.0f, 1.0f
    };

    float4x4 textureMat0 = mul(scaleBiasMatrix, trans_model_to_clip_of_shadow0);
    float4x4 textureMat1 = mul(scaleBiasMatrix, trans_model_to_clip_of_shadow1);
    float4x4 textureMat2 = mul(scaleBiasMatrix, trans_model_to_clip_of_shadow2);
    float4x4 textureMat3 = mul(scaleBiasMatrix, trans_model_to_clip_of_shadow3);

    float4 pushed = vtx_position + float4(0.5 * normalize(vtx_normal),0); 
    l_shade0 = mul(textureMat0, pushed);
    l_shade1 = mul(textureMat1, pushed);
    l_shade2 = mul(textureMat2, pushed);
    l_shade3 = mul(textureMat3, pushed);
}

void fshader( 
              in float4 l_position  : TEXCOORD0,
              in float4 l_shade0    : TEXCOORD1,
              in float4 l_shade1    : TEXCOORD2,
              in float4 l_shade2    : TEXCOORD3,
              in float4 l_shade3    : TEXCOORD4,

              in uniform float4 k_parallelesplit,
              in uniform float4 k_bias,

              in uniform sampler2D k_Ldepthmap0 : TEXUNIT0,
              in uniform sampler2D k_Ldepthmap1 : TEXUNIT1,
              in uniform sampler2D k_Ldepthmap2 : TEXUNIT2,
              in uniform sampler2D k_Ldepthmap3 : TEXUNIT3,

              out float4 o_color : COLOR )
{
    float3 shadowUV0   = l_shade0.xyz / l_shade0.w ;
    //float2 delta0      = saturate(shadowUV0.xy) - shadowUV0.xy ;
    float3 shadowUV1   = l_shade1.xyz / l_shade1.w ;
    //float2 delta1      = saturate(shadowUV1.xy) - shadowUV1.xy ;
    float3 shadowUV2   = l_shade2.xyz / l_shade2.w ;
    //float2 delta2      = saturate(shadowUV2.xy) - shadowUV2.xy ;
    float3 shadowUV3   = l_shade3.xyz / l_shade3.w ;
    //float2 delta3      = saturate(shadowUV3.xy) - shadowUV3.xy ;
    float shadowTerm0  = 1 ;
    float shadowTerm1  = 1 ;
    float shadowTerm2  = 1 ;
    float shadowTerm3  = 1 ;
    shadowTerm0 = tex2D(k_Ldepthmap0, shadowUV0.xy).x ;
    if (shadowTerm0 < l_shade0.z -k_bias[0]  )
         shadowTerm0 = 0.8 ;
    else
         shadowTerm0 = 1.0 ;
    shadowTerm1 = tex2D(k_Ldepthmap1, shadowUV1.xy).x ;
    if (shadowTerm1 < l_shade1.z - k_bias[1]  )
         shadowTerm1 = 0.8 ;
    else
         shadowTerm1 = 1.0 ;
    shadowTerm2 = tex2D(k_Ldepthmap2, shadowUV2.xy).x ;
    if (shadowTerm2 < l_shade2.z - k_bias[2]  )
         shadowTerm2 = 0.8 ;
    else
         shadowTerm2 = 1.0 ;
    shadowTerm3 = tex2D(k_Ldepthmap3, shadowUV3.xy).x ;
    if (shadowTerm3 < l_shade3.z - k_bias[3]  )
         shadowTerm3 = 0.8 ;
    else
         shadowTerm3 = 1.0 ;

    float shadowTerm = shadowTerm3 ;
    if (l_position.z < k_parallelesplit[2])
    {
        shadowTerm = shadowTerm2 ;
    }
    if (l_position.z < k_parallelesplit[1])
    {
        shadowTerm = shadowTerm1 ;
    }
    if (l_position.z < k_parallelesplit[0] )
    {
        shadowTerm = shadowTerm0 ;
    }
    o_color = float4(1-shadowTerm,1-shadowTerm,1-shadowTerm,1);
}

Wow, that must be the most inefficient shader I’ve ever seen.

Can you pack everything and let me see if I can reproduce the bug myself?

Hi
Once again, thank you prosoft, the FDepthStencil worked !

Do you still want a test case to see why the FDepthComponent doesn’t work ?

Yes, please do. I’d also be happy to make your shader at least 4x faster.

I don’t know what changed again. but in 1.7.0, Texture.FDepthStencil no longer work while Texture.FDepthComponent works again.

I must switch type for the same code to work between 1.7.0 and 1.6.2.

Is this a bug ? from which version ?