const float SIGMA = 0.3;
const float DENSITY = 0.03;

void intersect_with_cloud_plane_light(vec3 WorldPos, inout vec3 EndPos, vec3 RayDir) {
    if (RayDir.y > 0) {
        EndPos = intersectRayWithPlane(WorldPos, RayDir, CLOUD_UPPER_PLANE);
    }
    else {
        EndPos = intersectRayWithPlane(WorldPos, RayDir, CLOUD_LOWER_PLANE);
    }
}

float march_to_light(vec3 LightPosN, vec3 WorldPosC, float Dither) {
    float TransmittanceLight = 1;
    vec3 EndPosL;
    intersect_with_cloud_plane_light(WorldPosC, EndPosL, view_player(LightPosN));
    float StepSizeL = min(16, length(EndPosL) / 4);
    vec3 StepL = view_player(LightPosN) * StepSizeL;
    vec3 WorldPosCL = WorldPosC + StepL * Dither;
    for (int i = 1; i <= 4; i++) {
        float DensityL = noise_clouds_base(WorldPosCL);
        DensityL *= StepSizeL;
        float TransmittanceL = exp(-DensityL * SIGMA);
        TransmittanceLight *= TransmittanceL;
        WorldPosCL += StepL;
    }
    return TransmittanceLight;
}

vec3 get_clouds(vec3 PlayerPosN, vec3 Color, const int STEP_COUNT, vec3 CameraPos, const bool AnimateNoise, vec2 FragCoord) {
    vec3 StartPos, EndPos;
    bool Hit = intersect_with_cloud_plane(CameraPos, StartPos, EndPos, PlayerPosN, CLOUD_LOWER_PLANE, CLOUD_UPPER_PLANE);
    if (!Hit) return Color;

    vec3 Step = (EndPos - StartPos) / STEP_COUNT;
    float StepSize = length(Step);
    float TotalTransmittance = 1;
    vec3 FinalColor = vec3(0);
    vec2 Dither = blue_noise(FragCoord, AnimateNoise).rg;
    vec3 PlayerPosC = StartPos + Dither.x * Step;

    vec3 AmbientColor = dataBuf.AmbientColor;

    float LHeight = sin(sunAngle * TAU);
    bool MarchToSun = LHeight > -0.1;
    bool MarchToMoon = LHeight < 0.1;

    float MiePhaseSun, MiePhaseMoon;
    if (MarchToSun) {
        float VdotL = dot(view_player(sunPosN), PlayerPosN);
        MiePhaseSun = max(cs_phase(VdotL, 0.8), 1 / PI);
    }
    if (MarchToMoon) {
        float VdotL = dot(view_player(-sunPosN), PlayerPosN);
        MiePhaseMoon = max(cs_phase(VdotL, 0.8), 1 / PI);
    }

    for (int i = 1; i <= STEP_COUNT; i++) {
        vec3 WorldPosC = PlayerPosC + CameraPos;

        float Density = noise_clouds(WorldPosC);
        if (Density <= 0.001) {
            PlayerPosC += Step;
            continue;
        }
        Density *= StepSize;
        Density *= DENSITY;

        float Transmittance = exp(-Density * SIGMA);
        TotalTransmittance *= Transmittance;
        FinalColor += AmbientColor * Density * Transmittance * ISOTROPIC_PHASE;

        if (MarchToSun) {
            FinalColor += dataBuf.SunColor * Density * TotalTransmittance * march_to_light(sunPosN, WorldPosC, Dither.y) * MiePhaseSun;
        }
        if (MarchToMoon) {
            FinalColor += dataBuf.MoonColor * Density * TotalTransmittance * march_to_light(-sunPosN, WorldPosC, Dither.y) * MiePhaseMoon;
        }

        PlayerPosC += Step;
    }

    vec3 CloudColor = Color * TotalTransmittance + FinalColor;

    float FogFactor = pow2(PlayerPosN.y);
    FogFactor = exp(-30 * FogFactor);
    CloudColor = mix(CloudColor, Color, FogFactor);

    return CloudColor;
}
