float CloudNoise(vec2 coord,vec2 wind){
	float noise=texture2D(noisetex,coord*1+wind*.55).x;
	noise+=texture2D(noisetex,coord*.5+wind*.45).x*-2.;
	noise+=texture2D(noisetex,coord*.25+wind*.35).x*2.;
	noise+=texture2D(noisetex,coord*.125+wind*.25).x*5.;
	noise+=texture2D(noisetex,coord*.0625+wind*.15).x*9.;
	noise+=texture2D(noisetex,coord*.03125+wind*.05).x*10.;
	noise+=texture2D(noisetex,coord*.015625+wind*.05).x*-12.;
	return noise;
}

float CloudCoverage(float noise,float cosT,float coverage){
	float noiseMix=mix(noise,21.,.33*rainStrength);
	float noiseFade=clamp(sqrt(cosT*10.),0.,1.);
	float noiseCoverage=((coverage*coverage)+CLOUD_AMOUNT);
	float multiplier=1.-.5*rainStrength;
	
	return max(noiseMix*noiseFade-noiseCoverage,0.)*multiplier;
}

float GetNoise(vec2 pos){
	return fract(sin(dot(pos,vec2(12.9898,4.1414)))*43758.5453);
}

vec4 DrawCloud(vec3 viewPos,float dither,vec3 lightCol,vec3 ambientCol){
	float cosT=dot(normalize(viewPos),upVec);
	float cosS=dot(normalize(viewPos),sunVec);
	
	#if AA==2
	dither=fract(16.*frameTimeCounter+dither);
	#endif
	
	float cloud=0.;
	float cloudGradient=0.;
	float gradientMix=dither*.1667;
	float colorMultiplier=CLOUD_BRIGHTNESS*(.5-.25*(1.-sunVisibility)*(1.-rainStrength));
	float noiseMultiplier=CLOUD_THICKNESS*.25;
	float scattering=pow(cosS*.5*(2.*sunVisibility-1.)+.5,6.);
	
	vec2 wind=vec2(frametime*CLOUD_SPEED*.001,
		sin(frametime*CLOUD_SPEED*.05)*.002)*CLOUD_HEIGHT/15.;
		
		vec3 cloudcolor=vec3(0.);
		
		if(cosT>.1){
			vec3 wpos=normalize((gbufferModelViewInverse*vec4(viewPos,1.)).xyz);
			for(int i=0;i<CLOUD_QUALITY;i++){
				if(cloud>.99)break;
				vec3 planeCoord=wpos*((CLOUD_HEIGHT+(i+dither))/wpos.y)*.004;
				vec2 coord=cameraPosition.xz*.00025+planeCoord.xz;
				float coverage=float(i-3.+dither)*.667;
				
				float noise=CloudNoise(coord,wind);
				noise=CloudCoverage(noise,cosT,coverage)*noiseMultiplier;
				noise=noise/pow(pow(noise,2.5)+1.,.4);
				
				cloudGradient=mix(cloudGradient,
					mix(gradientMix*gradientMix,1.-noise,.25),
					noise*(1.-cloud*cloud));
					cloud=mix(cloud,1.,noise);
					gradientMix+=.2;
				}
				cloudcolor=mix(ambientCol*(.5*sunVisibility+.5),
				lightCol*(1.+scattering),
			cloudGradient*cloud);
			cloudcolor*=1.-.4*rainStrength;
			cloudcolor.r*=(1.+sunVisibility*.2);
			cloudcolor+=lightNight*lightNight*lightNight*lightNight*2;
			cloud*=sqrt(sqrt(clamp(cosT*10.-1.,0.,1.)))*(1.-.6*rainStrength);
			
		}
		
		return vec4(cloudcolor*colorMultiplier,pow(cloud,2)*CLOUD_OPACITY);
	}
	vec4 DrawEndCloud(vec3 viewPos,float dither,vec3 lightCol){
		float cosT=dot(normalize(viewPos),upVec);
		float cosS=dot(normalize(viewPos),sunVec);
		
		#if AA>1
		dither=fract(16.*frameTimeCounter+dither);
		#endif
		
		float cloud=0.;
		float cloudGradient=0.;
		float gradientMix=dither*.5;
		float colorMultiplier=CLOUD_BRIGHTNESS*(.5-.25*(1.-sunVisibility)*(1.-rainStrength));
		float noiseMultiplier=CLOUD_THICKNESS*.25;
		float scattering=pow(cosS*.5+.5,6.);
		
		vec2 wind=vec2(frametime*CLOUD_SPEED*.005,
			sin(frametime*CLOUD_SPEED*.05)*.002)*CLOUD_HEIGHT/15.;
			
			vec3 cloudcolor=vec3(0.);
			
			if(cosT>.1){
				vec3 wpos=normalize((gbufferModelViewInverse*vec4(viewPos,1.)).xyz);
				for(int i=0;i<6;i++){
					if(cloud>.99)break;
					vec3 planeCoord=wpos*((CLOUD_HEIGHT+(i+dither))/wpos.y)*.004*pow(cosT,-.5);
					vec2 coord=cameraPosition.xz*.00025+planeCoord.xz;
					float coverage=float(i-3.+dither)*.667;
					
					float noise=CloudNoise(coord,wind);
					noise=CloudCoverage(noise,cosT,coverage*1.5)*noiseMultiplier;
					noise=noise/pow(pow(noise,2.5)+1.,.4);
					
					cloudGradient=mix(cloudGradient,
						mix(gradientMix*gradientMix,1.-noise,.25),
						noise*(1.-cloud*cloud));
						cloud=mix(cloud,1.,noise);
						gradientMix+=.6;
					}
					cloudcolor=lightCol*vec3(6.4,6.8,5.)*(1.+scattering)*pow(cloudGradient,.75);
					cloudcolor=pow(cloudcolor,vec3(2))*2*vec3(1.4,1.8,1.);
					cloud*=min(pow(cosT*2,2.),.42);
				}
				
				return vec4(cloudcolor*colorMultiplier*(.6+(pow(sunVisibility,.1))*.4),pow(cloud,2)*.1*CLOUD_OPACITY*(2-(pow(sunVisibility,.1))));
			}
			
			void DrawStars(inout vec3 color,vec3 viewPos){
				vec3 wpos=vec3(gbufferModelViewInverse*vec4(viewPos,1.));
				vec3 planeCoord=wpos/(wpos.y+length(wpos.xz));
				vec2 wind=vec2(frametime,0.);
				vec2 coord=planeCoord.xz*.4+cameraPosition.xz*.0001+wind*.00125;
				coord=floor(coord*1024.)/1024.;
				
				float NdotU=max(dot(normalize(viewPos),normalize(upVec)),0.);
				float multiplier=sqrt(sqrt(NdotU))*170.*(1.-rainStrength)*moonVisibility;
				
				float star=1.;
				if(NdotU>0.){
					star*=GetNoise(coord.xy);
					star*=GetNoise(coord.xy+.01);
					star*=GetNoise(coord.xy+.12);
				}
				star=clamp(star-.825,0.,0)*multiplier;
				
				color+=star*pow(lightNight,vec3(1.));
			}
			
			float AuroraNoise(vec2 coord,vec2 wind){
				float noise=texture2D(noisetex,coord*.175+wind*.25).x;
				noise+=texture2D(noisetex,coord*.04375+wind*.15).x*5.;
				
				return noise;
			}
			
			vec3 DrawAurora(vec3 viewPos,float dither,int sampleCount){
				float cosT=dot(normalize(viewPos),upVec);
				
				#if AA==2
				dither=fract(16.*frameTimeCounter+dither);
				#endif
				
				float gradientMix=dither/sampleCount;
				float visibility=moonVisibility*(1.-rainStrength)*(1.-rainStrength);
				
				#ifdef WEATHER_PERBIOME
				visibility*=isCold*isCold;
				#endif
				
				vec2 wind=vec2(
					frametime*CLOUD_SPEED*.000125,
					sin(frametime*CLOUD_SPEED*.05)*.00025
				);
				
				vec3 aurora=vec3(0.);
				
				if(cosT>0.&&visibility>0.){
					vec3 wpos=normalize((gbufferModelViewInverse*vec4(viewPos,1.)).xyz);
					for(int i=0;i<sampleCount;i++){
						vec3 planeCoord=wpos*((8.+(i+dither)*7./sampleCount)/wpos.y)*.004;
						vec2 coord=cameraPosition.xz*.00005+planeCoord.xz;
						
						float noise=AuroraNoise(coord,wind);
						noise=max(1.-1.5/(1.-cosT*.8)*abs(noise-3.),0.);
						
						if(noise>0.){
							noise*=texture2D(noisetex,coord*.25+wind*.25).x;
							noise*=.5*texture2D(noisetex,coord+wind*16.).x+.75;
							noise=noise*noise*3./sampleCount;
							noise*=max(sqrt(1.-length(planeCoord.xz)*3.75),0.);
							
							vec3 cloudcolor=mix(vec3(.1,1.,.5),vec3(.1,.1,1.),pow(gradientMix,.4));
							aurora+=noise*cloudcolor*exp2(-6.*i/sampleCount);
						}
						gradientMix+=1./sampleCount;
					}
				}
				
				return aurora*visibility;
			}