

const vec2 shadowOffsets[6] = vec2[6](vec2(0.5303, 0.5303), vec2(-0.6250, -0.0000), vec2(0.3536, -0.3536),
                                      vec2(-0.0000, 0.3750), vec2(-0.1768, -0.1768), vec2(0.1250, 0.0000));

float shadowFunc(float diffuseSun, float sssAmount, vec3 p3, float noise, vec2 lightmap, vec3 normals)
{
    // Initialize shading
    float shading = 1.0;

    // Offset to reduce shadow acne
    p3 += normalize(shadowPosWorld) * 0.05;

    // Transform p3 to shadow map space
    vec3 ProjShadowPos = mat3(shadowModelView) * p3 + shadowModelView[3].xyz;
    ProjShadowPos = diagonal3(shadowProjection) * ProjShadowPos + shadowProjection[3].xyz;

    // Calculate distortion factor and apply it
    float distortFac = calcDistort(ProjShadowPos.xy);
    ProjShadowPos.xy *= vec2(distortFac);

// Modify diffuseSun based on shadows and subsurface scattering (sssAmount)
#ifdef SHADOWS_ENABLED
    float maxVal = max(diffuseSun, 0.5 * sssAmount);
#else
    float maxVal = max(diffuseSun, pow(lightmap.y, 16.0) * 0.5);
#endif
    diffuseSun = mix(diffuseSun, maxVal, step(0.0, sssAmount));

    // Define shadow bounds
    vec2 bounds = vec2(1.0) - 1.5 / shadowMapResolution;

    // Determine if the fragment is within shadow bounds and conditions
    bool inBounds = all(lessThan(abs(ProjShadowPos.xy), bounds)) &&
                    (abs(ProjShadowPos.z) < 6.0) &&
                    (diffuseSun > 0.001);

    if (inBounds)
    {
        // Reset shading and adjust ProjShadowPos for shadow sampling
        shading = 0.0;
        ProjShadowPos = ProjShadowPos * vec3(0.5, 0.5, 1.0 / 12.0) + vec3(0.5);

        // Calculate scaling factor based on sssAmount
        float scaleFac = mix(distortFac, (noise * 0.9 + 0.1) * PI, step(0.0, sssAmount));

        // Compute threshold multiplier
        const float threshMul = max(16.0 * shaddist / shadowMapResolution, 0.95);

        // Calculate distortion threshold
        float temp = 1.0 - diffuseSun * diffuseSun;
        float distortThresh = clamp(sqrt(temp) / diffuseSun, 0.0, 1.0) / scaleFac;

        // Compute differential threshold
        float diffthresh = (1.0 - sssAmount) * distortThresh * (threshMul / 6000.0);

#if SHADOW_FILTERING == 1
        // Precompute rotation matrix with noise
        float prec = noise * 6.28318530718; // 2 * PI
        mat2 noiseM = mat2(cos(prec), -sin(prec),
                           sin(prec), cos(prec)) *
                      0.001;

        // Calculate weight factors
        float weight = 1.0 + noise * shadowMapResolution * 0.00016;
        float dweight = -diffthresh * weight;

        // Manually unroll the loop for better performance
        shading += texture(shadow, ProjShadowPos + vec3(noiseM * shadowOffsets[0], dweight));
        shading += texture(shadow, ProjShadowPos + vec3(noiseM * shadowOffsets[1], dweight));
        shading += texture(shadow, ProjShadowPos + vec3(noiseM * shadowOffsets[2], dweight));
        shading += texture(shadow, ProjShadowPos + vec3(noiseM * shadowOffsets[3], dweight));
        shading += texture(shadow, ProjShadowPos + vec3(noiseM * shadowOffsets[4], dweight));
        shading += texture(shadow, ProjShadowPos + vec3(noiseM * shadowOffsets[5], dweight));

        // Average the shading from multiple samples
        shading *= 0.16666666667; // Equivalent to dividing by 6
#else
        // Adjust depth for shadow sampling without filtering
        ProjShadowPos.z -= diffthresh;
        shading = texture(shadow, ProjShadowPos);
#endif

#ifndef SHADOWS_ENABLED
        // Override shading if shadows are disabled
        shading = pow(lightmap.y, 16.0);
#endif

        // Blend shading with night shadows
        shading = mix(1.0, shading, nightblendShadows);
    }

    // Combine shading with diffuseSun and apply lightmap
    diffuseSun = min(diffuseSun, shading) * lightmap.y;

    // Blend shadows based on shadowblend factor
    diffuseSun = mix(0.0, diffuseSun, shadowblend);

    // Apply rain strength effect
    diffuseSun *= lightmap.y * (1.0 - rainStrength * 0.9);

    return diffuseSun;
}
