#ifdef OVERWORLD

#ifdef CAVE_FOG_PERBIOME
uniform float isLushCaves, isDripstoneCaves, isDeepDark;
#endif

vec3 GetFogColor(vec3 viewPos) {
	vec3 nViewPos = normalize(viewPos);
	float lViewPos = length(viewPos) / 64.0;
	lViewPos = 1.0 - exp(-lViewPos * lViewPos);

    float VoU = clamp(dot(nViewPos,  upVec), -1.0, 1.0);
    float VoL = clamp(dot(nViewPos, sunVec), -1.0, 1.0);

	float density = 0.4;
    float nightDensity = 0.8;
    float weatherDensity = 1.5;
    float groundDensity = 0.08 * (4.0 - 3.0 * sunVisibility) *
                          (10.0 * rainStrength * rainStrength + 1.0);
    
    float exposure = 0.24 + 0.66 * clamp((dot(sunVec, upVec) + 0.04) * 2.4, 0.0, 1.0) - 0.18 * clamp((dot(-sunVec, upVec) + 0.04) * 8.8, 0.0, 1.0) - 0.04 * clamp((dot(-sunVec, upVec) + 0.04) * 4.4, 0.0, 1.0);
    float nightExposure = 1.08 - 0.88 * clamp((dot(-sunVec, upVec) + 0.04) * 8.8, 0.0, 1.0) - 0.12 * moonVisibility22;

	float baseGradient = exp(-(VoU * 0.5 + 0.5) * 0.5 / density);

	float groundVoU = clamp(-VoU * 0.5 + 0.5, 0.0, 1.0);
    float ground = 1.0 - exp(-groundDensity / groundVoU);

    vec3 fog = fogCol * baseGradient / (SKY_I * SKY_I);
    fog = fog / sqrt(fog * fog + 1.0) * exposure * sunVisibility22 * (SKY_I * SKY_I);

    float sunMix = (VoL * 0.5 + 0.5) * pow(clamp(1.0 - VoU, 0.0, 1.0), 5.0) * pow(1.0 - timeBrightness * 0.6, 6.0) * (0.6 * clamp((dot(sunVec, upVec) + 0.04) * 6.6, 0.0, 1.0) + 0.2);
    float horizonMix = pow(1.0 - abs(VoU), 2.5) * 0.125 * (1.0 - timeBrightness * 0.5);
    float lightMix = (1.0 - (1.0 - sunMix) * (1.0 - horizonMix)) * lViewPos;

	vec3 lightFog = pow(lightSun, vec3(4.0 - sunVisibility22)) * baseGradient;
	lightFog = lightFog / (1.0 + lightFog * rainStrength);

    fog = mix(sqrt(fog * (1.0 - lightMix)), sqrt(lightFog), lightMix);
    fog *= fog;

	float nightGradient = exp(-(VoU * 0.5 + 0.5) * 0.35 / nightDensity);
    vec3 nightFog = lightNight * lightNight * nightGradient * nightExposure;
    fog = mix(nightFog, fog, sunVisibility22 * sunVisibility22);

    float rainGradient = exp(-(VoU * 0.5 + 0.5) * 0.125 / weatherDensity);
    vec3 weatherFog = weatherCol.rgb * weatherCol.rgb;
    weatherFog *= GetLuminance(ambientCol / (weatherFog)) * (0.255 * sunVisibility22 + 0.2);
    fog = mix(fog, weatherFog * rainGradient, rainStrength);

//---- ---- ---- ----

	float viewLength = length(viewPos);
	vec4 worldPos = gbufferModelViewInverse * vec4(viewPos, 1.0);


#if GROUND_FOG >= 1
	vec3 fogLowGround = ambientCol * vec3(1.0, 0.64, 0.88);		//if fog too bright on the horizon - decrease brightness here and increase density
	fogLowGround *= (1.0 - 0.978 * clamp((dot(-sunVec, upVec) + 0.04) * 4.0, 0.0, 1.0));	//brightness tranistion

	#ifdef DISTANT_HORIZONS
	fogLowGround *= pow(exp(-(VoU * 0.5 + 0.5) * 0.5), 16.0);	//reduces distance brightness contrast ; increase pow value to minimize that even more
	#else
	fogLowGround *= pow(exp(-(VoU * 0.5 + 0.5) * 0.5), 8.0);	//reduces distance brightness contrast ; increase pow value to minimize that even more
	#endif

	fogLowGround *= GROUND_FOG_DENSITY * 2.0;

#if GROUND_FOG == 2
	float fogMorningDensity = 0.04;

	if (worldTime < 6006.0){
	fogMorningDensity = 1.0 - 0.96*clamp((dot(sunVecM22, upVec) + 0.04) * 2.0, 0.0, 1.0);
	}
	if (worldTime > 6006.0){
	fogMorningDensity = 0.04 + 0.96*clamp((dot(-sunVecM22, upVec) + 0.04) * 10.0, 0.0, 1.0);
	}

	fogLowGround *= fogMorningDensity;
#endif

	fogLowGround /= pow(clamp(worldPos.y + cameraPosition.y, 64.0, 800.0)/64.0, 4.8);
	fogLowGround /=  pow(clamp(worldPos.y + cameraPosition.y, 64.0, 75.0)/75.0, 2.2);

	fogLowGround *= (1.0 - rainStrength);

    fog += fogLowGround;

#endif

//---- ---- ---- ----


	fog = mix(fogCavesCol * 0.5, fog * eBS, eBS);

	#ifdef CAVE_FOG_PERBIOME
	fog = mix(fogCavesCol * 0.5 * (1.0 - isLushCaves - isDripstoneCaves - isDeepDark)
	 + isLushCaves * fogLushCavesCol * 0.5
	 + isDripstoneCaves * fogDripstoneCavesCol * 0.5
	 + isDeepDark * fogDeepDarkCol * 0.2,
	 fog * eBS, eBS);
	#endif

    //fog *= voidFade;
	#if MC_VERSION >= 11800
	fog *= clamp((cameraPosition.y + 70.0) / 8.0, 0.0, 1.0);
	#else
	fog *= clamp((cameraPosition.y + 6.0) / 8.0, 0.0, 1.0);
	#endif

	return fog;
}
#endif

void NormalFog(inout vec3 color, vec3 viewPos) {
	#ifdef OVERWORLD
	float fog = length(viewPos) * FOG_DENSITY / 256.0;

	#ifdef WEATHER_PERBIOME
	fog = length(viewPos) * FOG_DENSITY * (1.0 + (3.2 * isDesert + 2.4 * isMesa - 0.16 * isSavanna + 0.64 * isCold) * 2.0 * rainStrength * eBS) / 256.0;
	#endif


#ifdef ALTITUDE_DENSITY_GRADIENT
	float viewLength = length(viewPos);
	vec4 worldPos = gbufferModelViewInverse * vec4(viewPos, 1.0);

	fog /= pow(clamp(worldPos.y + cameraPosition.y, 75.0, 320.0)/75.0, ALTITUDE_DENSITY_GRADIENT_I);
#endif


	float clearDay = sunVisibility22 * (1.0 - rainStrength);

	#ifdef CAVE_FOG_PERBIOME
	fog *= mix(CAVE_FOG_DENSITY * 2.0 + 2.0 * LUSH_CAVES_FOG_DENSITY * isLushCaves + 2.0 * DRIPSTONE_CAVES_FOG_DENSITY * isDripstoneCaves + 2.0 * DEEP_DARK_FOG_DENSITY * isDeepDark, (0.24 * rainStrength + 1.0) / (4.32 * clearDay + 0.88) * eBS, eBS);
	#else
	fog *= mix(CAVE_FOG_DENSITY * 2.0, (0.24 * rainStrength + 1.0) / (4.32 * clearDay + 0.88) * eBS, eBS);
	#endif

	fog = 1.0 - exp(-2.0 * pow(fog, 0.15 * clearDay * eBS + 1.25));
	vec3 fogColor = GetFogColor(viewPos);

	#if DISTANT_FADE > 0
	if(isEyeInWater == 0.0){
		#if DISTANT_FADE == 1
		float fogFactor = length(viewPos);
		#else
		vec4 worldPos = gbufferModelViewInverse * vec4(viewPos, 1.0);
		worldPos.xyz /= worldPos.w;
		float fogFactor = length(worldPos.xz);
		#endif

		float fogFar = far;
		#ifdef DISTANT_HORIZONS
		fogFar = dhFarPlane * 0.5;
		#endif

		#if MC_VERSION >= 11800
		float fogOffset = 0.0;
		#else
		float fogOffset = 12.0;
		#endif
										//0.16 is Density
		float vanillaFog = 1.0 - (fogFar - (fogFactor + fogOffset)) / (0.16 * fogFar);
		vanillaFog = clamp(vanillaFog, 0.0, 1.0);
		if(vanillaFog > 0.0){
			vec3 vanillaFogColor = GetSkyColor(viewPos, false);
			vanillaFogColor *= (4.0 - 3.0 * eBS) * (1.0 + nightVision);

			fogColor *= fog;
			
			fog = mix(fog, 1.0, vanillaFog);
			if(fog > 0.0) fogColor = mix(fogColor, vanillaFogColor, vanillaFog) / fog;
		}
	}
	#endif
	#endif



	#ifdef NETHER
	float viewLength = length(viewPos);

	#ifdef NETHER_PERBIOME
	float netherFogDensity = mix(
	NETHER_WASTES_FOG_DENSITY,
	(
        SOUL_SAND_VALLEY_FOG_DENSITY  * isValley  + CRIMSON_FOREST_FOG_DENSITY * isCrimson +
        WARPED_FOREST_FOG_DENSITY  * isWarped  + BASALT_DELTAS_FOG_DENSITY  * isBasalt
	) / max(nBiomeWeight, 0.0001),
	nBiomeWeight
	);
	#else
	float netherFogDensity = NETHER_WASTES_FOG_DENSITY;
	#endif

	#if NETHER_FOG_MODE == 0
	float fog = 2.0 * pow(viewLength * netherFogDensity / 256.0, 1.5) + 
				6.0 * pow(viewLength * 1.5 / far, 4.0);
	fog = 1.0 - exp(-fog);
	vec3 fogColor = netherCol.rgb * NETHER_BIOME_FOG_I / 3.60;
	#else

	vec4 worldPos = gbufferModelViewInverse * vec4(viewPos, 1.0);
			
								//"512.0" value stands for 32 chunk render distance (512 blocks)
	float fogLava = 1.6 * pow(viewLength * NETHER_LAVA_FOG_DENSITY * netherFogDensity / 512.0, 2.0) +
			6.0 * pow(viewLength * 1.1 / 512.0, 4.8);	//1.1 perfect

	float fogBiome = 6.0 * (viewLength * netherFogDensity / 512.0) + 
			 4.0 * pow(viewLength * 1.5 / 512.0, 4.0);


	fogLava /= pow(clamp(worldPos.y + cameraPosition.y, 32.0, 132.0)/32.0, 5.0) * 0.666;	//main gradient
	fogLava /=  pow(clamp(worldPos.y + cameraPosition.y, 32.0, 42.0)/42.0, 2.0);		//additional gradient

	fogBiome *= pow(clamp(worldPos.y + cameraPosition.y, 32.0, 132.0)/132.0, 3.0);
	fogBiome /= pow(clamp(worldPos.y + cameraPosition.y, 32.0, 132.0)/32.0, 2.2);
	fogBiome *= 24.0;


	float fog = fogLava + fogBiome;
	fog = 1.0 - exp(-fog);
	fogLava =  1.0 - exp(-fogLava);
	fogBiome = 1.0 - exp(-fogBiome);

	vec3 fogColor = (fogLava * netherLavaCol.rgb * NETHER_LAVA_FOG_I * 10.0 
			 + fogBiome * netherCol.rgb * NETHER_BIOME_FOG_I) * 0.04;
	#endif
	#endif



	#ifdef END
	float fog = length(viewPos) * FOG_DENSITY_END / 128.0;
	fog = 1.0 - exp(-0.8 * fog * fog);
	vec3 fogColor = endCol.rgb * 0.01;
	#endif

	color = mix(color, fogColor, fog);
}

void BlindFog(inout vec3 color, vec3 viewPos) {
	float fog = length(viewPos) * max(blindFactor * 0.2, darknessFactor * 0.075);
	fog = (1.0 - exp(-6.0 * fog * fog * fog)) * max(blindFactor, darknessFactor);
	color = mix(color, vec3(0.0), fog);
}

vec3 denseFogColor[2] = vec3[2](
	vec3(1.0, 0.3, 0.01),
	vec3(0.1, 0.16, 0.2) * (1.0 - 0.99 * moonVisibility) * eBS
);

void DenseFog(inout vec3 color, vec3 viewPos) {
	float fog = length(viewPos) * 0.5;
	fog = (1.0 - exp(-4.0 * fog * fog * fog));
	color = mix(color, denseFogColor[isEyeInWater - 2], fog);
}

void Fog(inout vec3 color, vec3 viewPos) {
	NormalFog(color, viewPos);
	if (isEyeInWater > 1) DenseFog(color, viewPos);
	if (blindFactor > 0.0 || darknessFactor > 0.0) BlindFog(color, viewPos);
}