implementing bilinear pcf for shadows by hand

My particular setup ( an old macbook pro with an AT x1600 and OSX 10.6.2 ) doesn’t do filtered pcf for free, like an NVIDIA supposedly would.

My understanding is that you get it “for free” on nVIDIA if you enable GL_LINEAR filtering for magnification.

So, I thought it might be fun to implement it “by hand” as it were for my GPU. I’ve done bi & trilinear filtering in C++ before, and the idea is simple enough. Take four neighboring samples and use the fractional part of the texture coordinate to blend between them.

Here’s my stab:


// ShadowMap is the shadow map
// ShadowSize is the size of the shadow map, which is square
// InvShadowSize is 1/ShadowSize


float shadow_pcf( in vec4 tc )
{
    //
    //  simple bilinear filtering
    //  first bring up to whole coords, get fractionals, and then drop back to normalized coords
    //

    tc *= ShadowSize;

    vec4 sc = floor( tc ),
         fractional = tc - sc;
    
    sc *= InvShadowSize;
    fractional *= InvShadowSize;
    
    float x1 = shadow2DProj( ShadowMap, sc ).r,
          x2 = shadow2DProj( ShadowMap, sc + vec4( InvShadowSize,0,0,0 )).r,
          x3 = shadow2DProj( ShadowMap, sc + vec4( 0,InvShadowSize,0,0 )).r,
          x4 = shadow2DProj( ShadowMap, sc + vec4( InvShadowSize,InvShadowSize,0,0 )).r,
          a  = mix( x2, x1, fractional.x ),
          b  = mix( x4, x3, fractional.x );

    return mix( b, a, fractional.y );
}

What I get is basically identical to what I got without any filtering at all. Which is to say: blocky shadows.

Any ideas?

remove :
fractional *= InvShadowSize;

That was it – however, the quality of the filtering is terrible :stuck_out_tongue:

I assume it has to do with precision loss scaling up ( shadow map size is 1024 ) and then back down. Unless there’s a smarter way to filter I’m just going to have to live with without filtered PCF.

It’s a shame, my shadows look nice and smooth on nVIDIA cards.