float CloudNoise(vec2 coord, vec2 wind) {
	float noise = texture2D(noisetex, coord * 0.125 + wind * 0.25).x;
	noise += texture2D(noisetex, coord * 0.0625 + wind * 0.15).x * 10.0;
	noise += texture2D(noisetex, coord * 0.03125 + wind * 0.05).x * 10.0;
	noise += texture2D(noisetex, coord * 0.015625 + wind * 0.05).x * 20.0;
	noise += texture2D(noisetex, coord * 0.012500 + wind * 0.05).x * 20.0;
	noise += texture2D(noisetex, coord * 0.025000 + wind * 0.05).x * 20.0;
	return noise * 0.30;
}

float CloudCoverage(float noise, float coverage, float NdotU, float cosS) {
	float noiseCoverageCosS = abs(cosS);
	noiseCoverageCosS *= noiseCoverageCosS;
	noiseCoverageCosS *= noiseCoverageCosS;
	float NdotUmult = 0.365;

	float noiseCoverage = pow2(coverage) + CLOUD_AMOUNT * (1.0 + noiseCoverageCosS * 0.175) * (1.0 + NdotU * NdotUmult * (1.0 - rainStrengthS * CLOUD_RAIN_COVERING)) - 2.5;

	return Max0(noise - noiseCoverage);
}

vec4 DrawCloud(vec3 viewPos, float dither, vec3 lightCol, vec3 ambientCol, int sampleCount) {

	float cosT = dot(normalize(viewPos), upVec);
	float cosS = dot(normalize(viewPos), sunVec);
	float cloud = 0.0;
	float cloudGradient = 0.0;
	float gradientMix = dither * 0.1667;
	float colorMultiplier = CLOUD_BRIGHTNESS * (0.7 - 0.25 * clamp01((1.0 - sunVisibility * DAY_CLOUD_VISIBILITY)) * clamp01((1.0 - moonVisibility * NIGHT_CLOUD_VISIBILITY)) * clamp01((1.0 - rainStrengthS * RAIN_CLOUD_VISIBILITY)));

	float noiseMultiplier = CLOUD_THICKNESS * 0.125;
	float scattering = 0.5 * pow(abs(cosS * 0.50 * (1.5 * sunVisibility - 1.0) + 0.5), 8.0);

	float cloudHeightFactor = Max0(1.07 - 0.001 * eyeAltitude);
	cloudHeightFactor *= cloudHeightFactor;
	float cloudHeight = CLOUD_HEIGHT * cloudHeightFactor * 0.5;

	float cloudSpeedFactor = 0.003;
	vec2 wind = vec2(frameTimeCounter * CLOUD_SPEED * cloudSpeedFactor, 0.0);

	vec3 cloudColor = vec3(0.0);

	float coordFactor = 0.009375;

	if (cosT > 0.025) {
		vec3 wpos = normalize((gbufferModelViewInverse * vec4(viewPos, 1.0)).xyz);
		for (int i = 0; i < sampleCount; i ++) {
			if (cloud > 0.99)
				break;

			vec3 planeCoord = wpos * ((cloudHeight + (i + dither) * 2.5 * DITHER_HEIGHT_QUALITY / sampleCount) / wpos.y) * CLOUD_COVERING;
			vec2 coord = cameraPosition.xz * 0.00025 + planeCoord.xz;

			float ang1 = i * 2.391;
			float ang2 = ang1 + 2.391;
			coord += mix(vec2(cos(ang1), sin(ang1)), vec2(cos(ang2), sin(ang2)), dither * 0.25 + 0.75) * coordFactor;

			float coverage = float(i - 3.0 + dither) * 0.5;

			float noise = CloudNoise(coord, wind);
			noise = CloudCoverage(noise, coverage, cosT, cosS) * noiseMultiplier;
			noise = noise / sqrt(pow2(noise) + 1.0);

			cloudGradient = mix(cloudGradient, mix(gradientMix * gradientMix, 1.0 - noise, 0.25), noise * (1.0 - cloud));

			cloud += Max0(noise - cloud * 0.95);
			cloud = mix(cloud, 1.0, rainStrengthS * pow4(noise));
			gradientMix += 0.2 * (6.0 / sampleCount);
		}

		cloudColor = mix(ambientCol * (0.35 * sunVisibility + 0.5), lightCol * (0.85 + 1.15 * scattering), cloudGradient * cloud);

		cloudColor.r *= (1.0 + sunVisibility * (0.25 - timeBrightness * 0.25) * (1.0 - 0.4 * rainStrengthS));
		cloudColor += pow4(lightNight) * 2.0;

		cloud *= pow2(pow2(1.0 - exp(- (10.0 - 8.2 * rainStrengthS) * cosT)));
	}

	#ifdef UNDERGROUND_SKY
		if (isEyeInWater == 0) {
			float ug = mix(clamp01((cameraPosition.y - 48.0) * 0.0625), 1.0, eBS);
			cloudColor = mix(minLightCol * 0.125, cloudColor, ug);
		}
    #endif

	#if MC_VERSION >=11800
		cloudColor *= clamp01((cameraPosition.y + 70.0) * 0.125);
	#else
		cloudColor *= clamp01((cameraPosition.y + 6.0) * 0.125);
	#endif

	return vec4(cloudColor * colorMultiplier, cloud * CLOUD_OPACITY);
}