Shadow mapping on Monogame

If you want some samples you can download my deferred Engine and check out the DeferredDirectionaLight shader, i have some different techniques in there.

The PCF I gave you is a bit blocky. If you want a better variant (more expensive) use this

A good input for the iSqrtSamples would be between 3 and 7

// Calculates the shadow term using PCF with edge tap smoothing
float CalcShadowTermSoftPCF(float fLightDepth, float ndotl, float2 vTexCoord, int iSqrtSamples)
{
    float fShadowTerm = 0.0f;
    
    float variableBias = clamp(0.0005 * tan(acos(ndotl)), 0.00001, DepthBias);

    //float variableBias = (-cos(ndotl) + 1)*0.02;
    //variableBias = DepthBias;

    float shadowMapSize = ShadowMapSize.x;

    float fRadius = iSqrtSamples - 1; //mad(iSqrtSamples, 0.5, -0.5);//(iSqrtSamples - 1.0f) / 2;

    for (float y = -fRadius; y <= fRadius; y++)
    {
        for (float x = -fRadius; x <= fRadius; x++)
        {
            float2 vOffset = 0;
            vOffset = float2(x, y);
            vOffset /= shadowMapSize;
            //vOffset *= 2;
            //vOffset /= variableBias*200;
            float2 vSamplePoint = vTexCoord + vOffset;
            float fDepth = ShadowMap.Sample(ShadowMapSampler, vSamplePoint).x;
            float fSample = (fLightDepth <= fDepth + variableBias);
            
            // Edge tap smoothing
            float xWeight = 1;
            float yWeight = 1;
            
            if (x == -fRadius)
                xWeight = 1 - frac(vTexCoord.x * shadowMapSize);
            else if (x == fRadius)
                xWeight = frac(vTexCoord.x * shadowMapSize);
                
            if (y == -fRadius)
                yWeight = 1 - frac(vTexCoord.y * shadowMapSize);
            else if (y == fRadius)
                yWeight = frac(vTexCoord.y * shadowMapSize);
                
            fShadowTerm += fSample * xWeight * yWeight;
        }
    }
    
    fShadowTerm /= (fRadius*fRadius*4);
    
    return fShadowTerm;
}

Or you can modify the original PCF with some edge tapping

float CalcShadowTermPCF(float light_space_depth, float ndotl, float2 shadow_coord)
{
    float shadow_term = 0;

    //float2 v_lerps = frac(ShadowMapSize * shadow_coord);

    float variableBias = GetVariableBias(ndotl);

    //safe to assume it's a square
    float size = 1.0f / 2048;
    	
    float samples[5];
    samples[0] = (light_space_depth - variableBias < 1-ShadowMap.SampleLevel(pointSampler, shadow_coord, 0).r);
    samples[1] = (light_space_depth - variableBias < 1 - ShadowMap.SampleLevel(pointSampler, shadow_coord + float2(size, 0), 0).r) * frac(shadow_coord.x * ShadowMapSize);
    samples[2] = (light_space_depth - variableBias < 1 - ShadowMap.SampleLevel(pointSampler, shadow_coord + float2(0, size), 0).r) * frac(shadow_coord.y * ShadowMapSize);
    samples[3] = (light_space_depth - variableBias < 1 - ShadowMap.SampleLevel(pointSampler, shadow_coord - float2(size, 0), 0).r) * (1-frac(shadow_coord.x * ShadowMapSize));
    samples[4] = (light_space_depth - variableBias < 1 - ShadowMap.SampleLevel(pointSampler, shadow_coord - float2(0, size), 0).r) * (1 - frac(shadow_coord.y * ShadowMapSize));


    shadow_term = (samples[0] + samples[1] + samples[2] + samples[3] + samples[4]) / 5.0;
    //shadow_term = lerp(lerp(samples[0],samples[1],v_lerps.x),lerp(samples[2],samples[3],v_lerps.x),v_lerps.y);

    return shadow_term;
}
2 Likes