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=coverage * coverage + CLOUD_AMOUNT
							* (1.0 + noiseCoverageCosS * 0.175)
							* (1.0 + NdotU * NdotUmult * (1.0 - rainStrengthS * CLOUD_RAIN_COVERING))
							- 2.5;

	return max(noise - noiseCoverage, 0.0);
}

vec4 DrawCloud(vec3 viewPos, float dither, vec3 lightCol, vec3 ambientCol, int sampleCount) {
	float cosT=dot(normalize(viewPos), upVec);
	float cosS=dot(normalize(viewPos), sunVec);

	dither=fract(16.0 * frameTimeCounter + dither);

	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 =max(1.07 - 0.001 * eyeAltitude, 0.0);
	      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(noise * noise + 1.0);

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

			cloud      +=max(noise - cloud * 0.95, 0.0);
			cloud       =mix(cloud, 1.0, rainStrengthS * pow2(noise * 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  +=lightNight * lightNight * lightNight * lightNight * 2.0;

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

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

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

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