float texture2DShadow(sampler2D shadowtex, vec3 shadowPos) {
    float shadow = texture2D(shadowtex, shadowPos.xy).r;

    return clamp((shadow - shadowPos.z) * 65536.0, 0.0, 1.0);
}

void computeVL(inout vec3 vl, in vec3 translucent, in float dither) {
	vec3 finalVL = vec3(0.0);

	//Depths
	float z0 = texture2D(depthtex0, texCoord).r;
	float z1 = texture2D(depthtex1, texCoord).r;

	//Positions
	vec3 viewPos = ToView(vec3(texCoord.xy, z0));
    vec3 lightVec = sunVec * ((timeAngle < 0.5325 || timeAngle > 0.9675) ? 1.0 : -1.0);
	vec3 nViewPos = normalize(viewPos);

    //Total Visibility
    float indoorFactor = sunVisibility * (1.0 - eBS * eBS);
	float VoU = clamp(dot(nViewPos, upVec), 0.0, 1.0);
		  VoU = 1.0 - pow(VoU, 1.5);
		  VoU = mix(VoU, 1.0, min(indoorFactor + timeBrightness, 1.0) * 0.75);
	float VoL = clamp(dot(nViewPos, lightVec), 0.0, 1.0);

	float vlVisibility = pow(VoU, 4.0 * (1.0 - float(isEyeInWater == 1) * 0.5)) * int(z0 > 0.56);
		  vlVisibility *= mix(0.2 + pow(VoL, 1.5) * 0.2, pow(VoL, 1.5) * 0.2, timeBrightness);
		  vlVisibility = mix(vlVisibility, 1.0, indoorFactor * 0.25);
		  vlVisibility *= shadowFade;

	#if MC_VERSION >= 11900
	vlVisibility *= 1.0 - darknessFactor;
	#endif

	vlVisibility *= 1.0 - blindFactor;

	if (vlVisibility > 0.0) {
		//Linear Depths
		float linearDepth0 = getLinearDepth(z0);
		float linearDepth1 = getLinearDepth(z1);

		//Variables
        int sampleCount = RAY_MARCHED_VOLUME_SAMPLES;

		float fovFactor = gbufferProjection[1][1] / 1.37;
		float x = abs(texCoord.x - 0.5);
			  x = 1.0 - x * x;
			  x = pow(x, max(3.0 - fovFactor, 0.0));
		float maxDist = 128.0 * x;
		float minDist = (maxDist / sampleCount * (1.0 - float(isEyeInWater == 1) * 0.5)) * x;
		float maxCurrentDist = min(linearDepth1, maxDist);

		vec3 shadowCol = vec3(0.0);
		vec3 vlColor = mix(pow(lightCol, vec3(0.75)), lightCol * normalize(skyColor * vec3(1.0, 0.8, 0.5) + 0.000001) * 2.0, timeBrightness);

		//Ray Marching
		for (int i = 0; i < sampleCount; i++) {
			float currentDist = (i + dither) * minDist;
            	  currentDist = mix(currentDist, exp2(i + dither) - 0.95, min(indoorFactor + timeBrightness, 1.0));

			if (currentDist > maxCurrentDist || linearDepth1 < currentDist || (linearDepth0 < currentDist && translucent.rgb == vec3(0.0))) {
				break;
			}

            vec3 worldPos = ToWorld(ToView(vec3(texCoord, getLogarithmicDepth(currentDist))));
            vec3 shadowPos = ToShadow(worldPos);

			if (length(shadowPos.xy * 2.0 - 1.0) < 1.0) {
                float currentSampleIntensity = (currentDist / maxDist) / sampleCount;
                      currentSampleIntensity = pow(currentSampleIntensity, 1.0 - min(indoorFactor + timeBrightness, 1.0) * 0.5);

				float shadow0 = texture2DShadow(shadowtex0, shadowPos);
				float shadow1 = 0.0;

				#ifdef SHADOW_COLOR
				if (shadow0 < 1.0) {
					shadow1 = texture2DShadow(shadowtex1, shadowPos);
					if (shadow1 > 0.0) {
						shadowCol = texture2D(shadowcolor0, shadowPos.xy).rgb;
					}
				}
				#endif

				#ifdef VL_CLOUDY_FOG
				float noise = 1.0;

				if (isEyeInWater == 0) {
					vec3 npos = worldPos + cameraPosition + vec3(frameTimeCounter, 0.0, 0.0);

                    float altitudeFactor = clamp(1.0 - npos.y * 0.0075, 0.0, 1.0);
					float n3da = texture2D(noisetex, npos.xz * 0.00025 + floor(npos.y * 0.1) * 0.1).r;
					float n3db = texture2D(noisetex, npos.xz * 0.00025 + floor(npos.y * 0.1 + 1.0) * 0.1).r;

					noise = mix(n3da, n3db, fract(npos.y * 0.1));
                    noise = max(noise - 0.33, 0.0);
                    noise = min(noise * 8.0, 1.0);
                    noise *= noise;
                    noise = mix(1.0, noise, altitudeFactor);
				}

				shadow0 *= noise;
				#endif

				vec3 shadow = clamp(shadow1 * pow2(shadowCol) + shadow0 * vlColor * float(isEyeInWater == 0), 0.0, 8.0);

				//Translucency Blending
				if (linearDepth0 < currentDist) {
					shadow *= translucent.rgb;
				}

				finalVL += shadow * currentSampleIntensity;
			}
		}

        finalVL *= vlVisibility * VL_STRENGTH;
		if (isEyeInWater == 1.0) finalVL *= mix(waterColorSqrt, waterColorSqrt * weatherCol, wetness) * (4.0 + sunVisibility * 8.0);
	}
    vl += finalVL;
}