#if !defined SOFT_SHADOW_LIB
#define SOFT_SHADOW_LIB
    vec3 calculateSoftShadows(in vec3 position, in bool sss) {
        cInt blurQuality = SHADOW_BLUR_QUALITY;
        cFloat sampleRadius = 0.0002;
        position.z /= SHADOW_DEPTH_SCALE;

        float shadowBias = clamp(0.0001 / shadowDistortionFactor(position.xy), 0.0001, 10.0);

        vec3 shadows = vec3(0.0);

        cFloat ditherSize = 8.0;
        float dither = fract(frameCounter * (1.0 / (ditherSize-1.0)) + bayer8(gl_FragCoord.st)) * ditherSize;
        int weight;
        for(int i = 0; i < blurQuality; ++i) {
            vec2 shadowCoordinate = position.xy + circleMap(i * ditherSize + dither, blurQuality * ditherSize) * sampleRadius;
                 shadowCoordinate = shadowDistortion(shadowCoordinate);

            float id = texture(shadowcolor1, shadowCoordinate * 0.5 + 0.5).b * 255.0;
            bool foliage = id == 18.0;

            float shadowDepth = texture(shadowtex0, shadowCoordinate * 0.5 + 0.5).r * 2.0 - 1.0;
            float shadowTranslucent = texture(shadowtex1, shadowCoordinate * 0.5 + 0.5).r * 2.0 - 1.0;

            float foliageFraction = 0.0;
            foliageFraction = float(texture(shadowcolor1, shadowCoordinate * 0.5 + 0.5).b * 255.0 == 18.0);
            foliageFraction *= shadowTranslucent.r - shadowDepth.r * shadowTranslucent.r;

            if(!foliage && !sss) {
                shadowDepth = step(position.z - shadowBias, shadowDepth);
                shadowTranslucent = step(position.z - shadowBias, shadowTranslucent);
                vec4 shadowColor = texture(shadowcolor0, shadowCoordinate * 0.5 + 0.5);
                shadowColor.rgb = srgbToLinear(shadowColor.rgb);
                shadowColor.rgb = mix(vec3(1.0) * mix(vec3(1.0), shadowColor.rgb, shadowColor.a), shadowColor.rgb, shadowColor.a);

                shadows += mix(vec3(shadowDepth), shadowColor.rgb, float(shadowDepth < shadowTranslucent));
            } else if(sss && foliage) {
                vec4 shadowColor = texture(shadowcolor0, shadowCoordinate * 0.5 + 0.5);
                shadowColor.rgb = srgbToLinear(shadowColor.rgb);

                float surfaceDepth = shadowDepth.r - position.z;
                      surfaceDepth = clamp(-2.0 * SHADOW_DEPTH_RADIUS * surfaceDepth, 0.0, 1e35);

                shadows += exp(-vec3(1.0, 0.8, 0.9) * surfaceDepth);
            }
            ++weight;
        }

        return shadows / weight;
    }
#endif