#ifdef volumetricFog
float bayer2(vec2 a){
    a = floor(a);
    return fract( dot(a, vec2(.5, a.y * .75)) );
}

#define bayer4(a)   (bayer2( .5*(a))*.25+bayer2(a))
#define bayer8(a)   (bayer4( .5*(a))*.25+bayer2(a))
#define bayer16(a)  (bayer8( .5*(a))*.25+bayer2(a))
#define bayer32(a)  (bayer16(.5*(a))*.25+bayer2(a))
#define bayer64(a)  (bayer32(.5*(a))*.25+bayer2(a))
#define bayer128(a) (bayer64(.5*(a))*.25+bayer2(a))

float dither64 = bayer64(gl_FragCoord.xy);

float expDepth(float dist){
	return (far * (dist - near)) / (dist * (far - near));
}

float getDepth(float depth) {
    return (near * far) / (near * depth + (far * (1.0 - depth)));
}

float shadowStep(sampler2D shadow, vec3 sPos) {
	return clamp(1.0 - max(sPos.z - texture2D(shadow, sPos.xy).y, 0.0) * 4096, 0.0, 1.0);
}

vec2 DistortPosition(in vec2 position){
    float CenterDistance = length(position);
    float DistortionFactor = mix(1.0f, CenterDistance, 0.9f);
    return position / DistortionFactor;
}

vec4 ShadowSpace(float depth0) {
	vec3 ClipSpace = vec3(texcoord, depth0) * 2.0f - 1.0f;
	vec4 ViewW = gbufferProjectionInverse * vec4(ClipSpace, 1.0);
	vec3 View = ViewW.xyz / ViewW.w;
	vec4 World = gbufferModelViewInverse * vec4(View, 1.0f);
	vec4 ShadowSpace = shadowProjection * shadowModelView * World;
	return ShadowSpace;
}
#endif

vec3 getFog(vec3 color, vec3 cameraPosition, vec3 worldPos, vec3 volumeColor, vec3 skyBoxCol, float iswater, float strength, float glare){
	
	#ifdef volumetricFog
		#ifdef volumetricLight
			float startRay = 0.2;
			const float endRay = 84.0;
			float increment = 12.0/volumetricFogQuality;
			
			#ifdef TAA
		    dither64 = fract(dither64 + frameTimeCounter * 8.0);
			#endif

			startRay += dither64 * increment;

			float weight = -increment / (startRay - 128.0);
			float ray = 0.0;

			for (startRay; startRay < endRay;) {
				if (startRay > getDepth(Depth)) break;

				vec4 shadowCoord = ShadowSpace(expDepth(startRay));
				float distortion = ((1.0 - SHADOW_MAP_BIAS) + length(shadowCoord.xy * 1.25) * SHADOW_MAP_BIAS) * 0.85;
				     shadowCoord.xy /= distortion;
				vec3 SampleCoords = shadowCoord.xyz * 0.5f + 0.5f;
					 SampleCoords.z += 0.0005;
					
				float shadowSampleBack = shadowStep(shadowtex1, SampleCoords);

				ray += shadowSampleBack;

				startRay += increment;
			}

		float startFactor = 0.0;
		#else
			float weight = 1.0;
			float ray = 0.5;
			float startFactor = 0.3;
		#endif

	float closeFactor = 1.0;

	//Altitude factor
	float altitude = FogAltitude; 
	#ifndef volumetricLight
	closeFactor *= 0.2;
	#endif

	float timeFactor = 0.1 * (time[0]) +  
					   0.2 * (time[1]) +
					   0.2 * (time[2]) + 
					   0.2 * (time[3]) + 
					   0.1 * (time[4]) + 
					   0.2 * (time[5]);

	if (isEyeInWater > 0.9){
		closeFactor = 10.0;
	    volumeColor = vec3(0.01, 0.36, 0.51) * (1 - time[5] * 0.7);
	 	volumeColor = pow(volumeColor, vec3(2.0));
		ray *= 6.0;			
		weight *= 3.2;
		ray = clamp(ray, 0.0, 120.0);
		#ifndef volumetricLight
	    volumeColor *= 0.2;
		closeFactor *= 0.1;
		#endif
	}

	float altitudeFactor = (worldPos.y + eyeAltitude + 1000.0 - altitude) * 0.001;
	if (altitudeFactor > 0.965 && altitudeFactor < 1.0) {
	    altitudeFactor = pow(altitudeFactor, 1.0 - (altitudeFactor - 0.965) * 28.57);
	}
		altitudeFactor = pow(altitudeFactor, 20.0);	
	#endif

    float borderFog = clamp(pow(length(worldPos.xz) / far, 21.0) * 0.5, 0.0, 1.0);

	#ifdef volumetricLight
	float altitudeFog = (1.0 - (exp(-50.0 * pow(length(worldPos.xz) / pow(far, startFactor) * closeFactor * 0.125, 3.25))));
		  altitudeFog *= (1.0 - altitudeFactor) * FogStrength * 0.1 * timeFactor;
		  altitudeFog = clamp(altitudeFog, 0.0, 1.9);
		  altitudeFog *= pow(clamp((eyeAltitude - altitude*0.05) / altitude, 0.0, 1.0), 6.0);

	float rainFogDepth = length(worldPos.xz) / 100;
		  rainFogDepth = (1.0 - exp(-0.8 * pow(rainFogDepth, 1.0)))*0.9;
		  rainFogDepth =  clamp(rainFogDepth, 0.0, 0.33)*rainStrength;

	float rayweight = ray * weight * fogStrength * 2.0 * glare;
		  rayweight += altitudeFog;
	if (isEyeInWater < 1.0) rayweight *= undergroundFix;
		  rayweight += rainFogDepth;
		  rayweight = clamp(rayweight, 0.0, 1.0);

	vec3 finalFogCol = mix(color, volumeColor * vec3(0.55*(1.0 - rainStrength*0.84)), rayweight);
		 finalFogCol = clamp(finalFogCol, vec3(0.0), vec3(1.0));
	color = mix(finalFogCol, skyBoxCol, borderFog);

	#else

	float altitudeFog = (1.0 - (exp(-50.0 * pow(length(worldPos.xz) / pow(far, startFactor) * closeFactor * 0.125, 3.25))));
		  altitudeFog *= (1.0 - altitudeFactor) * FogStrength * 1.3 * (0.50*timeFactor);
		  altitudeFog = clamp(altitudeFog, 0.0, 1.9);

		if (isEyeInWater == 1.0){
		  altitudeFog *= undergroundFix;
		  borderFog = 0.0;
		}
	float rayweight = fogStrength * 0.14;
		  rayweight *= undergroundFix;
		  rayweight = clamp(rayweight, 0.0, 1.0);
		  	
	vec3 finalFogCol = mix(color, volumeColor * vec3(0.55), rayweight * altitudeFog);
		 finalFogCol = clamp(finalFogCol, vec3(0.0), vec3(1.0));
	color = mix(finalFogCol, skyBoxCol, borderFog);

	#endif
	
	return color;
}