vec3 R2(const float n)
{
    return fract(float(n) * vec3(0.245122333753, 0.430159709002, 0.145122333753)) * 2 - 1;
}

void AtmosphereLoop(float samples, float expFactor, float dither, float rayLength, vec3 start, vec3 dV, float dY,
                    float mie_water, float Rayleigh, bool noWater, float estEyeDepth, vec3 refractedSunVec,
                    vec3 ambient, vec3 sunColor, inout float sh0, inout vec3 absorbance_w, inout vec3 vL_w, inout float density, vec3 dVWorld)
{
    vec2 bounds = vec2(1.0, 1.0) - 1.5 / shadowMapResolution;
    vec3 rL_Water = water_coefficientRayleigh;
    vec3 mieScatterFactor_Water = dirtScatterCoef;
    vec3 ScatterCoeff_water = (totEpsilon + mieScatterFactor_Water);

    float expFactorInvSamples = log(expFactor) / samples;
    float expFactorBase = pow(expFactor, dither / samples);
    float denom = expFactor - 1.0;
    float precomp = 0.035 * 2.2 * 20 / shadowMapResolution;
    float sh = 1.0;

    sh0 = 0.0;
#ifdef VL_NOISE_DENSITY
    vec3 temp = gbufferModelViewInverse[3].xyz + cameraPosition;
#endif
    for (int i = 0; i < samples; i++)
    {
        float d = (expFactorBase * pow(expFactor, float(i) / samples) - 1.0 / expFactor) / denom;
        float dd = expFactorBase * pow(expFactor, float(i) / samples) * expFactorInvSamples / denom;
        vec3 progress = start + d * dV;

        float distortFac = calcDistort(progress.xy);
        vec3 pos = progress;
        pos.xy *= distortFac;

        if (all(lessThan(abs(pos.xy), bounds)) && abs(pos.z) < 6)
        {
            pos = pos * vec3(0.5, 0.5, 0.5 / 6.0) + 0.5;

            sh = clamp(shadow2D(shadow, pos).x, rainStrength, 1);
        }
        if (!noWater)
        {
            float attenuationDepth = dd * rayLength;

            vec3 sun = sunColor * sh;
            vec3 ambientMul_water = exp(-max(estEyeDepth - dY * d, 0.0) * rL_Water);
            vec3 sunMul_water = exp(-max((estEyeDepth - dY * d), 0.0) / abs(refractedSunVec.y) * totEpsilon);
            vec3 mie_water_mult = mie_water * sunMul_water;
            vec3 rayleighContribution_Water = Rayleigh * rL_Water;
            vec3 mieScatteringContribution_Water = mieScatterFactor_Water * mie_water_mult;
            vec3 totalScatter_Water = rayleighContribution_Water + mieScatteringContribution_Water;
            vec3 sunContribution_Water = sun * totalScatter_Water;
            vec3 ambientContribution_Water = ambient * ambientMul_water * 0.75 * (rL_Water + mieScatterFactor_Water);
            vec3 light_water = sunContribution_Water + ambientContribution_Water;

            vL_w += (light_water - light_water * exp(-ScatterCoeff_water * attenuationDepth)) /
                    (ScatterCoeff_water + safety) * absorbance_w;
            absorbance_w *= calculateAbsorbance(ScatterCoeff_water, attenuationDepth);
        }
#ifdef VL_NOISE_DENSITY
        vec3 dpos = temp + d * dVWorld;
#ifdef VL_NOISE_DENSITY_HQ
        density += cloud_density(dpos);
#else
        density += cloudVol(dpos);
#endif

#endif
        sh0 += sh;
    }

    sh0 /= samples;
#ifdef VL_NOISE_DENSITY
    density = (density / samples);
#endif
}
