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

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

vec4 DistortShadow(vec4 shadowpos, float distortFactor) {
	shadowpos.xy *= 1.0 / distortFactor;
	shadowpos.z = shadowpos.z * 0.2;
	shadowpos = shadowpos * 0.5 + 0.5;

	return shadowpos;
}

vec4 GetWorldSpace(float shadowdepth, vec2 texCoord) {
	vec4 viewPos = gbufferProjectionInverse * (vec4(texCoord, shadowdepth, 1.0) * 2.0 - 1.0);
	viewPos /= viewPos.w;

	vec4 wpos = gbufferModelViewInverse * viewPos;
	wpos /= wpos.w;
	
	return wpos;
}

vec4 GetShadowSpace(vec4 wpos) {
	wpos = shadowModelView * wpos;
	wpos = shadowProjection * wpos;
	wpos /= wpos.w;
	
	float distb = sqrt(wpos.x * wpos.x + wpos.y * wpos.y);
	float distortFactor = 1.0 - shadowMapBias + distb * shadowMapBias;
	wpos = DistortShadow(wpos,distortFactor);
	
	return wpos;
}


//Volumetric light from Robobo1221 (modified)
vec3 getVolumetricRays(float pixeldepth0, float pixeldepth1, vec3 color, float dither) {
	vec3 vl = vec3(0.0);

	#ifdef TAA
	dither = fract(dither + frameCounter / 32.0);
	#endif
	
	vec3 screenPos = vec3(texCoord, pixeldepth0);
	vec4 viewPos = gbufferProjectionInverse * (vec4(screenPos, 1.0) * 2.0 - 1.0);
	viewPos /= viewPos.w;

	vec3 lightVec = sunVec * ((timeAngle < 0.5325 || timeAngle > 0.9675) ? 1.0 : -1.0);
	float VoL = dot(normalize(viewPos.xyz), lightVec);

	#ifdef OVERWORLD
	float visfactor = 0.05 * (-0.8 * timeBrightness + 1.0) * (3.0 * rainStrength + 1.0);
	float invvisfactor = 1.0 - visfactor;


	float visibility = clamp(VoL * 0.5 + 0.5, 0.0, 1.0);
	visibility = visfactor / (1.0 - invvisfactor * visibility) - visfactor;
	visibility = clamp(visibility * 1.015 / invvisfactor - 0.015, 0.0, 1.0);

	if (isEyeInWater == 1)
	visibility = mix(1.0, visibility, 0.25 * (eyeBrightnessSmooth.y / 240.0) + 0.75) * 0.14285 * float(pixeldepth0 > 0.56);
	else
	visibility = mix(1.0, visibility, 0.25 * eBS + 0.75) * 0.14285 * float(pixeldepth0 > 0.56);

	#endif
	
	#ifdef END
//	float visibility = 0.14285 * float(pixeldepth0 > 0.56);
	VoL = pow(VoL * 0.5 + 0.5, 16.0) * 0.75 + 0.25;
	float visibility = VoL;
	#endif

	visibility *= 0.14285 * float(pixeldepth0 > 0.56);

	if (visibility > 0.0) {
		float maxDist = 128.0;
		
		float depth0 = GetLinearDepth2(pixeldepth0);
		float depth1 = GetLinearDepth2(pixeldepth1);
		vec4 worldposition = vec4(0.0);
		vec4 shadowposition = vec4(0.0);
		
		vec3 watercol = mix(vec3(1.0), waterColor.rgb / (waterColor.a * waterColor.a), pow(waterAlpha, 0.25));
		
		for(int i = 0; i < 7; i++) {
			float minDist = exp2(i + dither) - 0.95;
			if (minDist >= maxDist) break;

			if (depth1 < minDist || (depth0 < minDist && color == vec3(0.0))) {
				break;
			}


			worldposition = GetWorldSpace(GetLogarithmicDepth(minDist), texCoord.st);
			shadowposition = GetShadowSpace(worldposition);
			shadowposition.z += 0.0512 / shadowMapResolution;

			if (length(shadowposition.xy * 2.0 - 1.0) < 1.0) {
				float shadow0 = shadow2D(shadowtex0, shadowposition.xyz).z;
				
				vec3 shadowCol = vec3(0.0);
				#ifdef SHADOW_COLOR
				if (shadow0 < 1.0) {
					float shadow1 = shadow2D(shadowtex1, shadowposition.xyz).z;
					if (shadow1 > 0.0) {
						shadowCol = texture2D(shadowcolor0, shadowposition.xy).rgb;
						shadowCol *= shadowCol * shadow1;
						#ifdef WATER_CAUSTICS
						shadowCol *= 3.0 - 2.0 * (1.0 - (1.0 - shadow0) * (1.0 - shadow0));
						shadowCol *= (1.0 + 16.0 * rainStrength + 16.0 * (1.0 - clamp((dot(sunVecM22, upVec) + 0.05) * 8.0, 0.0, 1.0)));
						#endif
					}
				}
				#endif

				vec3 shadow = clamp(shadowCol * (1.0 - shadow0) + shadow0, vec3(0.0), vec3(1.0));

				if (depth0 < minDist) shadow *= color;
				else if (isEyeInWater == 1.0)

				#ifdef OVERWORLD
				shadow *= watercol * 0.01 * (1.0 + (eyeBrightnessSmooth.y / 240.0)) * (1.0 - rainStrength)
	 				+ rainStrength * vec3(weatherCol) * 0.6 * (1.0 + (eyeBrightnessSmooth.y / 240.0));
				shadow *= 0.06 + 0.14 * sunVisibility + 0.8 * clamp((dot(sunVecM22, upVec) + 0.05) * 8.0, 0.0, 1.0);
				#else
				shadow *= watercol * 0.01 * (1.0 + (eyeBrightnessSmooth.y / 240.0));
				#endif
				
				#ifdef END
				#ifdef LIGHT_SHAFT_SMOKEY_END
//				vec3 npos = worldposition.xyz + cameraPosition.xyz + vec3(0, -frametime * LIGHT_SHAFT_SMOKEY_END_ANIMATION_SPEED, 0);
				vec3 npos = worldposition.xyz + cameraPosition.xyz + vec3(0, -frametime * 1.2, 0);
				float n3da = texture2D(noisetex, npos.xz / 512.0 + floor(npos.y / 3.0) * 0.35).r;
				float n3db = texture2D(noisetex, npos.xz / 512.0 + floor(npos.y / 3.0 + 1.0) * 0.35).r;
				float noise = mix(n3da, n3db, fract(npos.y / 3.0));
				noise = sin(noise * 28.0 + frametime * 1.2) * 0.25 + 0.5;
				shadow *= noise;
				#endif
				#endif

#ifdef IS_IRIS
				//Sandstorm
				#ifdef OVERWORLD && WEATHER_PERBIOME
				#ifdef LIGHT_SHAFT_SMOKEY_WEATHER
				vec3 npos = worldposition.xyz + cameraPosition.xyz + vec3(frametime * 10.0, 0, 0);
				float n3da = texture2D(noisetex, npos.xz / 512.0 + floor(npos.y / 3.0) * 0.35).r;
				float n3db = texture2D(noisetex, npos.xz / 512.0 + floor(npos.y / 3.0 + 1.0) * 0.35).r;
				float noise = mix(n3da, n3db, fract(npos.y / 3.0));
				noise = sin(noise * 28.0 + frametime * 10.0) * 0.25 + 0.5;

				if (rainStrength > 0.0 && isEyeInWater < 0.5 && (isDesert > 0.1 || isMesa> 0.1)){
				shadow *= noise;
				}
				#endif
				#endif
#endif

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

		vl = sqrt(vl * visibility);
		if(dot(vl, vl) > 0.0) vl += (dither - 0.25) / 128.0;
	}
	
	return vl;
}