float cloud_height = 1500.;
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);
}
// This function computes the coverage of clouds in a scene at a given 3D position, based on the cloud texture and rain
// strength.

float cloudCov(in vec3 pos, vec3 samplePos)
{

    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;

    // Height differences simplified with constants
    float heightAboveCloud = max(pos.y - (cloud_height + CloudBaseOffset), 0.0);
    float heightBelowCloudTop = max(-pos.y + (cloud_height + CloudTopOffset), 0.0);
    float heightBelowCloudBottom = min(-pos.y + (cloud_height + CloudBottomOffset), 0.0);

    // Multipliers based on height using multiplication for optimization
    float mult = heightAboveCloud * InverseHeightDivisionFactor;
    float mult2 = heightBelowCloudTop * InverseHeightDivisionFactor;
    float mult3 = heightBelowCloudBottom * InverseBottomHeightDivisionFactor;

    // Cloud coverage calculation with optimizations
    float textureContribution = cloudTextureCubic(depthtex1, samplePos.xz / CloudSize) * 2.0;
    // float textureContribution = texture(depthtex1, samplePos.xz / CloudSize).x * 2.0;

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

    // Final cloud coverage with adjustments for readability and optimization
    float cloud = max(coverage - mult - mult2 + mult3, 0.0);

    return cloud;
}

vec4 renderClouds(float dither, vec3 view, vec3 fogC)
{

    // calculate cloud density
    const float cdenisty = CloudDensity * 10;

    // calculate the direction of the view
    vec3 worldV = (view);

    // store a copy of the view direction
    vec3 dV_view = worldV;

    // calculate the maximum number of iterations of the cloud rendering loop
    float maxIT_cloudsF = float(clamp(maxIT_clouds / sqrt(worldV.y), 0, maxIT_clouds));

    // calculate the world space position of the view direction
    worldV = worldV * 300000 + cameraPosition;

    // scale the view direction by a factor dependent on the view direction and the maximum number of iterations
    dV_view = dV_view * 150 / dV_view.y / maxIT_clouds;

    // return a transparent color if the view direction is below the cloud layer
    if (worldV.y < 300000 - (cloud_height * 200))
        return vec4(fogC, 1);

    // calculate the initial progress along the view direction
     vec3 progress_view = dV_view * ((cloud_height - cameraPosition.y) / dV_view.y) + cameraPosition;
    // vec3 progress_view = dV_view * (dither + (cloud_height - cameraPosition.y) / dV_view.y) + cameraPosition;

    // calculate a time-dependent offset
    float ftimeTemp = frameTimeCounter * 35;
    progress_view.x += ftimeTemp;

    // calculate the amount of light that can be scattered in the first iteration
    float tExt = 1.0;

    // calculate the length of the view direction
    float mult = length(dV_view);

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

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

    vec3 sunContribution = mieDay * sunLight * PI;
    float ambientMult = exp(-(0.8 * rainStrength + 1.24) * cdenisty * 5.0);
    vec3 skyCol0 = atmosphereUp * ambientMult;

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

    float invMaxIT_cloudsF = 1.0 / float(maxIT_cloudsF);
    for (int i = 0; i < maxIT_cloudsF; i++)
    {

        if (tExt < 1e-4)
            break;
        float cloud = cloudCov(progress_view, progress_view * 0.25) * cdenisty;

        float powder0 = exp(-cloud * mult);
        float gradFactor = mix(0.5, 1.0, float(i) * invMaxIT_cloudsF);
        float powder = (powder0 - 1) * (powder0 - 1) * gradFactor;

        color += scatteredLight * powder * clamp(tExt, 0.01, 1);
        tExt *= powder0;
        progress_view += dV_view * 0.75;
    }

    // Assume fog color = sky gradient at long distance
    float dist = (cloud_height - cameraPosition.y) / normalize(dV_view).y;
    float fog = clamp(exp(-dist / 5000.0 * (1.0 + rainStrength * 2.0)), 0, 1);

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