float cloud_height = 1500.;
float cloud_height_max = 1650;
float cloudDensity = 1;
const int maxIT_clouds = 8;
uniform sampler2D depthtex1;

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

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

    vec2 posScaled = pos * texSize;
    vec2 f = fract(posScaled);
    vec2 smoothWeights = vec2(cubeSmooth(f.x), cubeSmooth(f.y));

    vec2 posRight = pos + vec2(texelSize.x, 0.0);
    vec2 posUp = pos + vec2(0.0, texelSize.y);
    vec2 posDiag = pos + texelSize;

    float baseTexel = texture(tex, pos).a;
    float rightTexel = texture(tex, posRight).a;
    float upTexel = texture(tex, posUp).a;
    float diagonalTexel = texture(tex, posDiag).a;

    float mixXTop = mix(baseTexel, rightTexel, smoothWeights.x);
    float mixXBot = mix(upTexel, diagonalTexel, smoothWeights.x);
    return mix(mixXTop, mixXBot, smoothWeights.y);
}

float getCloudDensity(in vec3 pos, float dummy)
{
    float ftimeTemp = frameTimeCounter * 24.0;
    pos.x += ftimeTemp;

    vec3 samplePos = pos * 0.25;
    float invCloudSize = 1.0 / CloudSize;           // CloudSize assumed to be a uniform or constant.
    vec2 sampleCoord = samplePos.xz * invCloudSize; // = pos.xz * (0.25 / CloudSize)

    const float CloudBaseOffset = 100.0;
    const float CloudTopOffset = 450.0;
    const float CloudBottomOffset = 125.0;
    const float HeightDivisionFactor = 500.0;
    const float BottomHeightDivisionFactor = 100.0;
    const float InverseHeightDivisionFactor = 1.0 / HeightDivisionFactor;
    const float InverseBottomHeightDivisionFactor = 1.0 / BottomHeightDivisionFactor;

    float baseHeight = cloud_height + CloudBaseOffset;
    float topHeight = cloud_height + CloudTopOffset;
    float bottomHeight = cloud_height + CloudBottomOffset;

    float heightAboveCloud = max(pos.y - baseHeight, 0.0);
    float heightBelowCloudTop = max(topHeight - pos.y, 0.0);
    float heightBelowCloudBottom = min(bottomHeight - pos.y, 0.0);

    float mult = heightAboveCloud * InverseHeightDivisionFactor;
    float mult2 = heightBelowCloudTop * InverseHeightDivisionFactor;
    float mult3 = heightBelowCloudBottom * InverseBottomHeightDivisionFactor;

    float textureContribution = cloudTextureCubic(depthtex1, sampleCoord) * 2.0;

    float denom = 0.8 * (1.0 - rainStrength);
    float coverage = (textureContribution + rainStrength) / denom;
    coverage = clamp(coverage, 0.0, 1.0);

    float cloud = max(coverage - mult - mult2 + mult3, 0.0);
    return cloud;
}

vec4 renderClouds(float dither, vec3 view, vec3 fogC)
{
    const float cdensity = CloudDensity * 10.0;

    vec3 worldV = view;
    vec3 dV_view = view;

    float sqrtWorldVy = sqrt(max(worldV.y, 1e-6));
    int itCount = int(float(maxIT_clouds) / sqrtWorldVy);
    itCount = clamp(itCount, 0, maxIT_clouds);
    float maxIT_cloudsF = float(itCount);

    worldV = worldV * 300000.0 + cameraPosition;

    float dVy = dV_view.y;
    dV_view = (dV_view * 150.0) / (dVy * float(maxIT_clouds));

    float cloudThreshold = 300000.0 - (cloud_height * 200.0);
    if (worldV.y < cloudThreshold)
        return vec4(0.0, 0.0, 0.0, 0.0);

    float factor = (cloud_height - cameraPosition.y) / dV_view.y;
    vec3 progress_view = dV_view * factor + cameraPosition;

    float tExt = 1.0;
    float mult = length(dV_view);

    mat3 mGBuffer = mat3(gbufferModelView);
    float SdotV = dot(sunVec, mGBuffer * view);

    float phase1 = phaseg(SdotV, cloudMieG);
    float phase2 = phaseg(SdotV, cloudMieG2) * 0.7;
    float mieDay = max(phase1, phase2);

    vec3 sunContribution = mieDay * sunLight * PI;
    float ambientMult = exp(-(0.8 * rainStrength + 1.24) * cdensity * 5.0);
    vec3 skyCol0 = atmosphereUp * ambientMult;
#ifdef END
    skyCol0 = fogColor.rgb * fogColor.rgb;
    sunContribution = mieDay * skyCol0 * PI;
#endif

    vec3 scatteredLight = sunContribution + skyCol0;
    vec3 color = vec3(0.0);

    float invMaxIT_cloudsF = 1.0 / maxIT_cloudsF;

    for (int i = 0; i < itCount; i++)
    {
        if (tExt < 1e-4)
            break;

        float cloud = getCloudDensity(progress_view, 1.0) * cdensity;

        float powder0 = exp(-cloud * mult);

        float gradFactor = mix(0.4, 1.0, float(i) * invMaxIT_cloudsF);

        float powder = (1.0 - powder0) * (1.0 - powder0) * gradFactor;

        color += scatteredLight * powder * tExt;

        tExt *= powder0;

        progress_view += dV_view * 0.4;
    }

    vec3 dV_norm = normalize(dV_view);
    float norm_dVy = dV_norm.y;
    float dist = (cloud_height - cameraPosition.y) / norm_dVy;
    float fog = clamp(exp(-dist / 3000.0 * (1.0 + rainStrength * 2.0)), 0.0, 1.0);

    return mix(vec4(0.0), vec4(color, clamp(tExt, 0.0, 1.0)), fog);
}
