float SF_fast(in sampler2D shadowtex, in vec3 wPos, float cdepth, float diff) {
    const float step = 1.0/shadowMapResolution;
    float noise     = ditherGradNoise()*pi;
    vec2 offset     = vec2(cos(noise), sin(noise))*step;
    float shade     = clamp(cdepth-textureLod(shadowtex1, wPos.xy+offset, 0).x*256.0, 0.0, diff)/diff;
        shade      += clamp(cdepth-textureLod(shadowtex1, wPos.xy-offset, 0).x*256.0, 0.0, diff)/diff;
        shade      += (clamp(cdepth-textureLod(shadowtex1, wPos.xy, 0).x*256.0, 0.0, diff)/diff)*0.5;
    return shade*0.4;
}
vec4 SFcol_fast(in sampler2D shadowtex, in vec3 wPos) {
    const float step = 1.0/shadowMapResolution;
    float noise     = ditherGradNoise()*pi;
    vec2 offset     = vec2(cos(noise), sin(noise))*step;
    vec4 shade     = texture2D(shadowtex, wPos.xy+offset);
        shade      += texture2D(shadowtex, wPos.xy-offset);
        shade      += texture2D(shadowtex, wPos.xy)*0.5;
    return shade*0.4;
}

float SF_low(in vec3 pos, in float sigma, inout vec4 col, float cdepth, float diff) { //using 15 tap poisson
    float shadow    = 0.0;
    col             = vec4(1.0, 1.0, 1.0, 0.0);
    vec2 dither     = taaShadowDither();

    for (int i = 0; i<15; i++) {
        vec2 coord      = pos.xy + poisson60o[i*4]*dither*sigma;
        float depth1    = clamp(cdepth-textureLod(shadowtex1, coord, 0).x*256.0, 0.0, diff)/diff;
        shadow         += depth1;

        float depth0 = clamp(cdepth-textureLod(shadowtex0, coord, 0).x*256.0-0.05, 0.0, diff)/diff;
        bool translucency = depth1<depth0;
        if (translucency) {
                vec4 scol   = textureLod(shadowcolor0, coord, 0);
            col.rgb     = min(col.rgb, scol.rgb*0.95);
            col.a       = max(col.a, scol.a);
        }
    }
    shadow /= 15.0;
    return shadow;
}
float SF_med(in vec3 pos, in float sigma, inout vec4 col, float cdepth, float diff) { //using 30 tap poisson
    float shadow    = 0.0;
    col             = vec4(1.0, 1.0, 1.0, 0.0);
    vec2 dither     = taaShadowDither();

    for (int i = 0; i<30; i++) {
        vec2 coord  = pos.xy + poisson60o[i*2]*dither*sigma;
        float depth1    = clamp(cdepth-textureLod(shadowtex1, coord, 0).x*256.0, 0.0, diff)/diff;
        shadow         += depth1;

        if (i%2 == 0) {
            float depth0 = clamp(cdepth-textureLod(shadowtex0, coord, 0).x*256.0-0.05, 0.0, diff)/diff;
            bool translucency = depth1<depth0;
            if (translucency) {
                vec4 scol   = textureLod(shadowcolor0, coord, 0);
                col.rgb     = min(col.rgb, scol.rgb*0.95);
                col.a       = max(col.a, scol.a);
            }
        }
    }
    shadow /= 30.0;
    return shadow;
}

void getShadowSoft(in vec3 pos, in float distortion, inout float shadow, inout vec4 shadowcol, float cdepth, float diff) {
    #if shadowFilterQuality==0
        shadow  = 1.0-SF_fast(shadowtex0, pos, cdepth, diff);
        shadowcol = SFcol_fast(shadowcolor0, pos);
    #elif shadowFilterQuality==1
        shadow  = 1.0-SF_low(pos, 0.0002*distortion, shadowcol, cdepth, diff);
    #elif shadowFilterQuality==2
        shadow  = 1.0-SF_med(pos, 0.0002*distortion, shadowcol, cdepth, diff);
    #endif
}