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

float shadowFunc(
    float diffuseSun,
    float sssAmount,
    vec3 p3,
    float noise,
    vec2 lightmap,
    vec3 fragpos,
    inout vec3 sAlbedo,
    vec3 albedo,
    vec3 normals,
    inout vec3 colored,
    float entity)
{
    // === Shadow Bias ===
    // === Basic slope bias
    float ndotl = dot(normals, lightPos);
    float slope = clamp(1.0 - ndotl, 0.0, 1.0);

    // === Sample shadow distortion strength
    float distort = calcDistortMainDH(abs(p3.xy));

    // === Compensate for nonlinear warping
    float distortComp = 1.0 + distort * 1.5; // distortion-aware scaling

    // === Final bias (tunable)
    float bias = max(0.003, 0.05 * slope * distortComp);

    // === Offset along normal (NOT lightDir, avoids acne on vertical blocks)
    p3 += normals * bias;

    // === Shadow Map Projection ===
    vec3 ProjShadowPos = mat3(shadowModelView) * p3 + shadowModelView[3].xyz;
    ProjShadowPos = diagonal3(shadowProjection) * ProjShadowPos + shadowProjection[3].xyz;

    // === Shadow Distortion ===
    float distortFac = calcDistort(ProjShadowPos.xy);
    ProjShadowPos.xy *= distortFac;
    ProjShadowPos.z -= bias * 0.005; // fine-tune depending on PCF strength

    // === Subsurface Light Enhancement ===
    if (sssAmount > 0.0)
    {
        // How much light comes from the "behind" side of the block
        float backLit = max(dot(-lightPos, normals), 0.0);

        // How much light comes from the side (not top-down)
        float sideLit = 1.0 - abs(dot(normals, lightPos)); // max at 90°

        // Mix both with adjustable weights
        float sssBoost = mix(backLit, sideLit, 0.5) * sssAmount;

        // Blend it in if it helps
        diffuseSun = max(diffuseSun, sssBoost);
    }

    // === Shadow Bounds Check ===
    vec2 bounds = vec2(1.0) - (1.5 / shadowMapResolution);
    bool inBounds = all(lessThan(abs(ProjShadowPos.xy), bounds)) &&
                    (abs(ProjShadowPos.z) < 6.0) &&
                    (diffuseSun > 0.001);

    float shading = 1.0;

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

#if SHADOW_FILTERING == 1
        float angle = noise * 6.2831853; // 2 * PI
        float ca = cos(angle), sa = sin(angle);
        mat2 rot = mat2(ca, -sa, sa, ca) * 0.001;

        // Unrolled 6-tap filter with rotation
        for (int i = 0; i < 6; ++i)
        {
            vec3 offset = vec3(rot * shadowOffsets[i], 0.0);
            shading += texture(shadow, ProjShadowPos + offset);
        }

        shading *= 1.0 / 6.0;
#else
        shading = texture(shadow, ProjShadowPos);
#endif

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

    // === Final Light Application ===
    float shadowed = min(diffuseSun, shading);
    shadowed *= lightmap.y;
    shadowed = mix(0.0, shadowed, shadowblend);
    shadowed *= lightmap.y * (1.0 - rainStrength * 0.9);

    return shadowed;
}
