
float remap2(const float originalValue, const float originalMin, const float originalMax, const float newMin, const float newMax)
{
    return newMin + (((originalValue - originalMin) / (originalMax - originalMin)) * (newMax - newMin));
}
#if VL_QUALITY == 0
float densityfunction(vec3 pip, float fogAmount)
{

    if (fogAmount <= 0.35)
        return 0.0;

    #ifdef NETHER
    const float cloud_height = -1;
    const float cloud_depth = 200.0;

    #endif
    #ifdef END
    const float cloud_height = 0;
    const float cloud_depth = 256.0;

    #endif
    #ifdef OVERWORLD
    const float cloud_height = FOG_HEIGHT;
    float cloud_depth = mix(64.0, 32.0, rainStrength);
    #endif

    // Early exit 2: If the point is outside the cloud volume, return 0.
    if (pip.y < cloud_height || pip.y > (cloud_height + cloud_depth))
        return 0.0;

    vec3 p = pip;
    p.xz += 1 * 12.0 * vec2(0.707107, 0.707107);

    float height_fraction = (pip.y - cloud_height) / cloud_depth;
    height_fraction = clamp(height_fraction, 0.0, 1.0);

    vec4 fogGradient = vec4(-0.9, 0.2, 0.3, 1.0);

    float g = smoothstep(fogGradient.x, fogGradient.y, height_fraction) - smoothstep(fogGradient.z, fogGradient.w, height_fraction);

    float base_cloud = remap2(0.55 * g, 1.0 - fogAmount, 1.0, 0.0, 1.0);
    base_cloud *= fogAmount;

    // Early exit 3:
    if (base_cloud <= 0.20)
        return 0.0;

    base_cloud = remap2(base_cloud, 0.3 * clamp(height_fraction * 2.0, 0.0, 1.0), 1.0, 0.0, 1.0);

    return pow(clamp(base_cloud, 0.0, 1.0), (1.0 - height_fraction) * 0.8 + 0.5);
}
#elif VL_QUALITY == 1

float densityfunction(vec3 pip, float fogAmount)
{

    // fogAmount = 1;
    // Early exit 1: No fog coverage means no density.
    #ifdef HQ_VL_MIST
    if (fogAmount <= 0.0)
        return 0.0;
    #else
    if (fogAmount <= 0.35)
        return 0.0;
    #endif

    #ifdef NETHER
    const float cloud_height = -1;
    const float cloud_depth = 200.0;

    #endif
    #ifdef END
    const float cloud_height = 0;
    const float cloud_depth = 256.0;

    #endif
    #ifdef OVERWORLD
    const float cloud_height = FOG_HEIGHT;
    float cloud_depth = mix(64.0, 32.0, rainStrength);

    #endif

    // Early exit 2: If the point is outside the cloud volume, return 0.
    if (pip.y < cloud_height || pip.y > (cloud_height + cloud_depth))
        return 0.0;

    float time = frameCounter * 0.005;
    vec3 p = pip;
    const vec2 wind_norm = vec2(0.707107, 0.707107);
    p.xz += time * 12.0 * wind_norm;
    vec2 a = texture(colortex15, p * 0.002).xy;

    float base_cloud = 1.0 - a.y * 1.5;
    float height_fraction = (pip.y - cloud_height) / cloud_depth;
    height_fraction = clamp(height_fraction, 0.0, 1.0);

    vec4 fogGradient = vec4(-0.9, 0.2, 0.3, 1.0);

    float g = smoothstep(fogGradient.x, fogGradient.y, height_fraction) - smoothstep(fogGradient.z, fogGradient.w, height_fraction);

    base_cloud = remap2(base_cloud * g, 1.0 - fogAmount, 1.0, 0.0, 1.0);
    base_cloud *= fogAmount;

    // Early exit 3:
    if (base_cloud <= 0.20)
        return 0.0;

    float hfbm = a.x;

    base_cloud = remap2(base_cloud, hfbm * clamp(height_fraction * 2.0, 0.0, 1.0), 1.0, 0.0, 1.0);

    return pow(clamp(base_cloud, 0.0, 1.0), (1.0 - height_fraction) * 0.8 + 0.5);
}
#elif VL_QUALITY >= 2

float densityfunction(vec3 pip, float fogAmount)
{
    // fogAmount = 1;
    // Early exit 1: No fog coverage means no density.
    #ifdef HQ_VL_MIST
    if (fogAmount <= 0.0)
        return 0.0;
    #else
    if (fogAmount <= 0.1)
        return 0.0;
    #endif

    #ifdef NETHER
    const float cloud_height = -32;
    const float cloud_depth = 200.0;

    #endif
    #ifdef END
    const float cloud_height = 0;
    const float cloud_depth = 256.0;

    #endif
    #ifdef OVERWORLD
    const float cloud_height = FOG_HEIGHT;
    float cloud_depth = mix(64.0, 32.0, rainStrength);

    #endif

    // Early exit 2: If the point is outside the cloud volume, return 0.
    if (pip.y < cloud_height || pip.y > (cloud_height + cloud_depth))
        return 0.0;

    float time = frameCounter * 0.001;
    vec3 p = pip;
    const vec2 wind_norm = vec2(0.707107, 0.707107);
    p.xz += time * 12.0 * wind_norm;
    #ifndef NETHER
    vec2 n = texture(colortex15, p * 0.0004).xy;
    float base_cloud = 1.0 - n.x * pow(n.y * 3.0, 2.0);

    #else
    float n = 0.5;
    float base_cloud = 0.625;

    #endif
    float height_fraction = (pip.y - cloud_height) / cloud_depth;
    height_fraction = clamp(height_fraction, 0.0, 1.0);

    vec4 fogGradient = vec4(-0.9, 0.2, 0.3, 1.0);

    float g = smoothstep(fogGradient.x, fogGradient.y, height_fraction) - smoothstep(fogGradient.z, fogGradient.w, height_fraction);

    base_cloud = remap2(base_cloud * g, 1.0 - fogAmount, 1.0, 0.0, 1.0);
    base_cloud *= fogAmount ;

    // Early exit 3:
    #ifdef HQ_VL_MIST
    if (base_cloud <= 0.10)
        return 0.0;
    #else
    if (base_cloud <= 0.15)
        return 0.0;

    #endif
    p.xz -= time * wind_norm * 40.0;
    p.y -= time * 20.0;
    #ifdef HQ_VL_MIST
    float hfbm = texture(colortex15, p * 0.001).x * 0.6;
    hfbm += texture(colortex15, p * 0.001 * 4.0).x * 0.3;
    hfbm += texture(colortex15, p * 0.001 * 16.0).x * 0.15;
    #else
        // For samples = 2

        #ifdef END
    float hfbm = texture(colortex15, p * 0.0005).x * 0.7;

        #else
    float hfbm = texture(colortex15, p * 0.001).x * 0.7;
    hfbm += texture(colortex15, p * 0.006).x * 0.4;
        #endif

    #endif

    base_cloud = remap2(base_cloud, hfbm * clamp(height_fraction * 2.0, 0.0, 1.0), 1.0, 0.0, 1.0);

    return pow(clamp(base_cloud, 0.0, 1.0), (1.0 - height_fraction) * 0.8 + 0.5);
}

#endif
#ifdef CAUSTICS

float CalculateWaterCaustics(vec3 worldPos)
{

    float scale = worldPos.y / lightPos.y;
    // float scale = worldPos.y;
    vec2 pos = worldPos.xz - lightPos.xz * scale;

    vec2 coord0 = fract(pos / 64.0);

    float caustics = textureLod(colortex7, coord0, 0).g;
    return 1.0 * caustics;
}
#endif
#if VL_QUALITY >= 2 || defined(NETHER) || defined(END)

void shadowing(
    out float depthshadow,
    out float sh0,
    out float density,
    out float sunCaustics,
    bool isWater,
    vec3 start,
    vec3 dV,
    vec3 worldPos,
    float fogAmount)
{
    //----------------------------------------------------------------------------
    // Initialization and coefficient setup
    //----------------------------------------------------------------------------

    // Water-specific scattering parameters (assumed defined elsewhere)

    // Shadow-related variables:
    float currentShadowSample = 1.0; // Current shadow sample (per loop iteration)
    float accumulatedShadow = 1.0;   // Accumulates shadow contribution over samples

    //----------------------------------------------------------------------------
    // Integration loop for volumetric lighting
    //----------------------------------------------------------------------------
    float dither = noise_standard(gl_FragCoord.xy);
    float samples = VL_SAMPLES;
    float compensation = samples / 2048.0;
    float maxDistance = min(
        length(worldPos),
    #ifdef DISTANT_HORIZONS
        dhRenderDistance
    #elif defined(VOXY)
        (0.8 * vxRenderDistance * 16.0)
    #else
        far
    #endif
    );

    vec3 worldDir = normalize(worldPos);
    vec3 lightDir = normalize(lightPos);

    #if VL_QUALITY >= 3 || defined(NETHER) || defined(END)
        #ifdef VL_SELF_SHADOWING
    const float shadowSamples = 2.0;
    const float shadowWeight = 1.0 / shadowSamples;
    vec3 precomputedShadowStep = lightDir * 32.0 * dither;
        #endif
    #endif

    float weight = (maxDistance / samples) * compensation;
    accumulatedShadow = 0.0;
    float invSamples = 1.0 / samples;
    float sunCausticsAccum = 0.0;

    // Loop over samples for integration
    for (int i = 0; i < int(samples); i++)
    {
        // Nonlinear step progression within the volume
        float fraction = (float(i) + dither) * invSamples;
        float stepDistance = fraction * maxDistance;
        vec3 stepPos = cameraPosition + stepDistance * worldDir;

    #ifdef OVERWORLD
        vec3 progress = start + dV * fraction;
        vec3 distortedPos = vec3(progress.xy * calcDistort(progress.xy), progress.z);
        vec3 texCoords = distortedPos * vec3(0.5, 0.5, 0.5 / 6.0) + 0.5;
        // Sample the shadow texture with clamping based on rain strength.
        currentShadowSample = clamp(shadow2D(shadow, texCoords).x, 0.0, 1.0);

        #ifdef CAUSTICS
        if (isWater)
        {
            sunCausticsAccum += CalculateWaterCaustics(stepPos) * 0.5 + 0.5;
        }
        #endif
    #endif

    #if VL_QUALITY >= 3 || defined(NETHER) || defined(END)
        float densitySample = densityfunction(stepPos, fogAmount);
        #ifdef VL_SELF_SHADOWING
        float shadowAccum = 0.0;
        vec3 shadowPos = stepPos;
        for (int j = 1; j < int(shadowSamples); j++)
        {
            shadowPos += precomputedShadowStep;
            float localShadow = densityfunction(shadowPos, fogAmount);
            shadowAccum += localShadow * shadowWeight;
        }
        depthshadow += shadowAccum * 4.0;
        #endif
        density += densitySample * weight;
    #endif
        // Accumulate this sample’s shadow value
        accumulatedShadow += currentShadowSample;
    }

    // Average the shadow samples and apply rain-strength compensation
    accumulatedShadow = mix(accumulatedShadow / samples, 1.0, rainStrength * 0.5);

    #ifdef CAUSTICS
    sunCaustics = mix((sunCausticsAccum / samples), 1.0, rainStrength);
    sunCaustics = pow(sunCaustics, 16.0) * 10.0;
    #else
    sunCaustics = 0.005;

    #endif
    sh0 = pow(accumulatedShadow, 1);

    #if VL_QUALITY >= 3 || defined(NETHER) || defined(END)
    density /= samples;
    depthshadow = mix(depthshadow / samples, 0.0, rainStrength * 0.5);
        #ifdef VL_SELF_SHADOWING
    // Use the lesser of the self-shadow factor and the integrated shadow value.
    sh0 = min(1.0 - depthshadow, sh0);

        #endif
    #else
    density = 0;
    #endif

    density = clamp(density, 0.0, 1.0);
}

#endif
