#ifdef END
float cloud_height = 200; // Start of the cloud layer
#else
float cloud_height = cloudHeight; // Start of the cloud layer
#endif
// float cloud_height = 350;
float cloudDensity = 0.75;
float cloud_depth = 5;
float cloud_height_max = cloud_height + cloud_depth;
float cloud_height_mid = float(cloud_height) + cloud_depth * 0.5;
const int maxIT_clouds = 8;

float cubeSmooth(float x)
{
    return x * x * (3.0 - 2.0 * x);
}

float cloudTextureCubic(sampler2D tex, vec2 pos)
{
    vec2 texSize = textureSize(tex, 0) * 2.0;
    vec2 texelSize = 1.0 / texSize;

    vec2 fractPos = fract(pos * texSize);
    vec2 smoothPos = vec2(cubeSmooth(fractPos.x), cubeSmooth(fractPos.y));

    float baseTexel = texture(tex, pos).a;
    float rightTexel = texture(tex, pos + vec2(texelSize.x, 0.0)).a;
    float upTexel = texture(tex, pos + vec2(0.0, texelSize.y)).a;
    float diagonalTexel = texture(tex, pos + texelSize).a;

    float mixX1 = mix(baseTexel, rightTexel, smoothPos.x);
    float mixX2 = mix(upTexel, diagonalTexel, smoothPos.x);

    return mix(mixX1, mixX2, smoothPos.y);
}
vec2 GetCloudOffset()
{
    float timeMult = 1;
    float time = cloudTime * timeMult;
    vec2 cloudOffset = vec2(-time / 12.0, 0.33);
    cloudOffset = mod(cloudOffset, vec2(256.0));

    return cloudOffset;
}
float GetHeightFractionForPoint(float inPosition)
{ // get global fractional position in cloud zone
    float height_fraction = (inPosition - cloud_height) / cloud_depth;
    return clamp(height_fraction, 0.0, 1.0);
}
//  / CloudSize
float getCloudDensity(in vec3 pos, float dummy, vec2 wind)
{
    float fadeLow = smoothstep(float(cloud_height), float(cloud_height) + float(cloud_depth) * 0.02, pos.y);
    float fadeHigh = 1.0 - smoothstep(cloud_height_max - float(cloud_depth) * 0.02, cloud_height_max, pos.y);

    float linStepLow = clamp((pos.y - float(cloud_height)) / (float(cloud_height) + float(cloud_depth) * 5 - float(cloud_height)), 0.0, 1.0);
    float linStepHigh = clamp((pos.y - (float(cloud_height) + float(cloud_depth) * 5)) / (cloud_height_max - (float(cloud_height) + float(cloud_depth) * 5)), 0.0, 1.0);

    float cubeLow = linStepLow * linStepLow * linStepLow;
    float cubeHigh = linStepHigh * linStepHigh * linStepHigh;

    float erodeLow = (1.0 - cubeLow) * (1.0 - cubeLow) * (1.0 - cubeLow);
    float erodeHigh = cubeHigh * cubeHigh * cubeHigh;
    erodeHigh = erodeHigh * 0.2;
    erodeLow = erodeLow * 0.2;
    pos.x = mod(pos.x, 3072.0);
    pos.z = mod(pos.z, 3072.0);

    vec2 cloudOffset = GetCloudOffset();

    vec2 cloudTexcoord = ((pos.xz + vec2(0.0, 4.0)) / 12.0 - cloudOffset.xy) / 256.0;

    float textureContribution = cloudTextureCubic(depthtex1, cloudTexcoord - vec2(0.00096, 0.00090));
#ifdef DH
    textureContribution = cloudTextureCubic(depthtex0, cloudTexcoord - vec2(0.00096, 0.00090));
#endif

    float coverage = (textureContribution + rainStrength) / (0.8 - rainStrength * 0.8);

    coverage -= erodeLow + erodeHigh;
    coverage *= fadeLow * fadeHigh;
    textureContribution *= fadeLow * fadeHigh;
    return clamp(pow(coverage + textureContribution, 0.5), 0, 1);
}

float calculateSunShadow(float sunContributionG, vec2 stepPosXZ, vec2 cameraPositionXZ, vec3 stepPos, vec3 dV_Sun, float lss)
{

    float shadow = 0.0;

    if (sunContributionG > 1e-5)
    {
        for (int j = 1; j < 6; j++)
        {
            vec3 shadowSamplePos = stepPos + dV_Sun * float(j);
            if (shadowSamplePos.y < cloud_height_max)
            {
                float cloudS = getCloudDensity(shadowSamplePos, 1, vec2(0.0));
                shadow += cloudS * cloudDensity * lss;
            }
        }
    }

    return shadow;
}
float calculateSunShadow2(float sunContributionG, vec2 stepPosXZ, vec2 cameraPositionXZ, vec3 stepPos, vec3 dV_Sun, float lss)
{

    float shadow = 0.0;

    if (sunContributionG > 1e-5)
    {
        for (int j = 1; j < 6; j++)
        {
            vec3 shadowSamplePos = stepPos + dV_Sun * float(j);
            if (shadowSamplePos.y < cloud_height_max)
            {
                float cloudS = getCloudDensity(shadowSamplePos, 1, vec2(0.0));
                shadow += cloudS * cloudDensity * lss;
            }
        }
    }

    return shadow;
}

vec3 calculateLighting(float stepPy, float muE, float muEshD, float mult, vec3 sunContribution, vec3 sunContributionMulti, vec3 skyCol0, float transmittance)
{

    float h = 0.5 - 0.5 * GetHeightFractionForPoint(stepPy);
    float powder = 1.0 - exp(-muE * mult);
    float sunShadowMulti = exp(-log(muEshD * 0.15 + 0.5)) * powder;
    float sunShadow = exp(-muEshD);

    float ambientPowder = mix(1.0, powder, h) * (1.0 - h);
    vec3 S = vec3(sunContribution * sunShadow + sunShadowMulti * sunContributionMulti + skyCol0 * ambientPowder);

    vec3 Sint = (S - S * exp(-mult * muE)) / muE;
    vec3 stepScatter = muE * Sint * transmittance;

    return stepScatter;
}
vec4 renderClouds(float dither, vec3 worldPos, vec3 fogC, bool terrain, float usecamera, bool usefade)
{

    float eyeAlt;
    vec3 cameraPos;
    if (usecamera == 0)
    {
        cameraPos = vec3(cameraPosition.x, 70, cameraPosition.z);
        eyeAlt = 70;
    }
    else
    {
        cameraPos = cameraPosition;
        eyeAlt = eyeAltitude;
    }

    vec3 worldDir = normalize(worldPos);
    vec3 totalScattering = vec3(0.0);
    float totalTransmittance = 1.0;
    float b = 6;
    float within = smoothstep(float(cloud_height) - b, float(cloud_height), eyeAlt) * (1.0 - smoothstep(cloud_height_max, cloud_height_max + b, eyeAlt));

    bool isBelowVol = eyeAlt < cloud_height_mid;
    bool visibleVol = worldDir.y > 0.0 && isBelowVol || worldDir.y < 0.0 && !isBelowVol;
    const float sigmaT = 1.5;
    float alpha = 0;
    float atmosFade;
    if (visibleVol || within > 0.0)
    {
        float vDotL = dot(normalize(worldPos), normalize(lightPos));

        vec3 bottom = worldDir * ((cloud_height - eyeAlt) * (1 / worldDir.y));
        vec3 top = worldDir * ((cloud_height_max - eyeAlt) * (1 / worldDir.y));

        if (worldDir.y < 0.0 && isBelowVol || worldDir.y > 0.0 && !isBelowVol)
        {
            bottom = vec3(0.0);
            top = vec3(0.0);
        }

        vec3 start = isBelowVol ? bottom : top;
        vec3 end = isBelowVol ? top : bottom;

        vec3 temp = gbufferModelViewInverse[3].xyz + cameraPos;
        vec3 dpos = temp + worldPos;

        float test = exp2(-max(dpos.y - cloud_height, 0.0));

        if (terrain)
        {
            if (test < 1 || isBelowVol)
                end = worldPos;
        }

        float noise = mix(0.0, 1.0, dither);
        vec3 rStep = (end - start) * (1 / float(maxIT_clouds));
        rStep = mix(rStep, worldDir * clamp(32 * dither, 1.0, 128), within);
        vec3 rPos = rStep * noise + start + cameraPos;
        rPos = mix(rPos, cameraPos, within);

        vec3 scattering = vec3(0.0);
        float transmittance = 1.0;
        float ambientMult = exp(-(-0.3 + 0.24 + 0.8 * rainStrength) * cloudDensity * 16.0);

        float mieDay = max(max(phaseg(vDotL, 0.2), phaseg(vDotL, 0.7)), phaseg(vDotL, -0.2));

        vec3 skyCol0 = atmosphereUp * ambientMult;
        vec3 sunContribution = mieDay * sunLight * 1.5;

        // Precompute constants used in loops
        float exp_factor = 0.005;
        float dist = distance(rPos, cameraPos);
        atmosFade = exp(-dist * exp_factor);
#ifdef END
        skyCol0 = fogColor.rgb * fogColor.rgb * PI;
        sunContribution = mieDay * skyCol0 * PI;

#endif

        float lss = (cloud_depth / 8);

        float mieDayMulti = phaseg(vDotL, 0.2);
        vec3 sunContributionMulti = mieDayMulti * sunLight;

        // vec3 dV_Sun = lightPos * lss;
        vec3 dV_Sun = vec3(0, 1, 0) * lss;

        float mult_a = length(worldPos) / PI;
        float mult_b = length(worldPos * cloud_depth / worldPos.y / maxIT_clouds);
        float mult = mix(mult_b, mult_a, within);

        for (int i = 0; i < maxIT_clouds; ++i, rPos += rStep)
        {

            float dist = distance(rPos.xz, cameraPos.xz);
            float atmosFade2 = exp(-dist * 0.002);

            if (atmosFade2 <= 0.005)
                continue;

            if ((rPos.y < cloud_height || rPos.y > cloud_height_max))
                continue;

            float cloud = getCloudDensity(rPos, 1, vec2(0.0));
            if (cloud <= 0.001)
                continue;
            //    float shadow = calculateSunShadow(sunContribution.g, rPos.xz, cameraPos.xz, rPos, dV_Sun, lss);
            float shadow = calculateSunShadow2(sunContribution.g, rPos.xz, cameraPos.xz, rPos, dV_Sun, lss);

            float muE = cloud * cloudDensity;

            scattering += calculateLighting(rPos.y, muE, shadow, mult, sunContribution, sunContributionMulti, skyCol0, transmittance);

            transmittance *= max(exp(-mult * muE), 0.0);
            alpha += atmosFade2;

            if (transmittance < 1e-5)
                break;
        }

        totalScattering = scattering;
        totalTransmittance = transmittance;
    }

    vec4 result = vec4(totalScattering, totalTransmittance);
    // result = mix(result, vec4(0, 0, 0, 1), exp(-max(worldDir.y * PI * 4, 0)) * (1.0 - within) * float(eyeAlt < cloud_height));
    result = mix(vec4(0, 0, 0, 1), result, clamp(atmosFade * 2, 0, 1));

    return vec4(result);
}
//////////////////
