#version 120

//#define BRIGHT_PORTAL_FIX //Enable this if end portals are 16x brighter than they should be
//#define CROSS_PROCESS //Opposite of desaturation, makes everything more vibrant and saturated.
#define DYNAMIC_LIGHT_VIGNETTE 50 //Reduces the brightness of dynamic light around edges the of your screen [0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100]
#define DYNAMIC_LIGHTS //Holding blocks that emit light will light up their surroundings
#define ENDER_ARCS //Adds bolts of plasma that arc through the nebulae. Requires ender nebulae to be enabled!
#define ENDER_NEBULAE //Adds animated nebulae to the background of the end dimension
#define ENDER_STARS //Adds blinking stars to the background of the end dimension. Stackable with nebulae/plasma.
#define HEAT_REFRACTION 1.00 //How much the screen jiggles around in the nether, or when in lava [0.00 0.25 0.50 0.75 1.00 1.25 1.50 1.75 2.00 2.25 2.50 2.75 3.00 3.25 3.50 3.75 4.00 4.25 4.50 4.75 5.00]
//#define OLD_CLOUDS //Uses old cloud rendering method from earlier versions, for people who don't like pretty things.
#define UNDERWATER_FOG //Applies fog to water
//#define VANILLA_LIGHTMAP //Uses vanilla light colors instead of custom ones. Requires optifine 1.12.2 HD_U_D1 or later!

#define DESATURATE_NIGHT 0.50 //Amount to desaturate the world at night [0.00 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00]
#define DESATURATE_RAIN 0.50 //Amount to desaturate the world when raining [0.00 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00]
#define END_PORTAL_BACKGROUND_OVERWORLD 2 //1: Use overworld fog color. 2: Use end background. [1 2]
#define END_PORTAL_CLOUDS_OVERWORLD 2 //0: No clouds. 1: Use overworld clouds. 2: Use void clouds. [0 1 2]
#define END_PORTAL_EFFECTS_OVERWORLD //Enables fancy effects for end portals
#define END_PORTAL_FOREGROUND_OVERWORLD 2 //0: No foreground image. 1: Use overworld screenshot. 2: Use end island screenshot. [0 1 2]
#define FOG_DENSITY_MULTIPLIER_OVERWORLD 1.00 //How much overall fog there is in the overworld. [0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.60 0.70 0.80 0.90 1.00 1.20 1.40 1.60 1.80 2.00 2.50 3.00 3.50 4.00 4.50 5.00 6.00 7.00 8.00 9.00 10.00]
#define FOG_ENABLED_OVERWORLD //Enables fog in the overworld. It is recommended to have this enabled if you also have infinite oceans enabled!
#define FOG_HEIGHT_MULTIPLIER_OVERWORLD 32.0 //Controls how much denser fog is at lower altitudes [16.0 24.0 32.0 48.0 64.0 96.0 128.0 192.0 256.0 384.0 512.0 768.0 1024.0]
#define OVERWORLD_FOG_SATURATION_BOOST 1.25 //Makes the horizon blue-er [0.00 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.80 1.85 1.90 1.95 2.00 2.05 2.10 2.15 2.20 2.25 2.30 2.35 2.40 2.45 2.50]
#define OVERWORLD_HORIZON_HEIGHT 0.2 //How far above the horizon the fog color will be applied [0.001 0.002 0.003 0.004 0.005 0.0075 0.01 0.02 0.03 0.04 0.05 0.075 0.1 0.2 0.3 0.4 0.5 0.75 1.0]
#define OVERWORLD_SKY_SATURATION_BOOST 1.50 //Makes the sky blue-er [0.00 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.70 0.75 0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.80 1.85 1.90 1.95 2.00 2.05 2.10 2.15 2.20 2.25 2.30 2.35 2.40 2.45 2.50]
#define RAINBOWS //If enabled, rainbows will appear when the weather changes from rainy to clear
#define SEA_LEVEL 63 //Sea level for infinite oceans. Change this if you use custom worldgen. [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256]

uniform float adjustedTime;
uniform float blindness;
uniform float darknessLightFactor;
uniform float day;
uniform float far;
uniform float fogEnd;
uniform float fov;
uniform float frameTimeCounter;
uniform float night;
uniform float nightVision;
uniform float pixelSizeX;
uniform float pixelSizeY;
uniform float rainStrength;
uniform float screenBrightness;
uniform float sunset;
uniform float wetness;
uniform int blockEntityId;
uniform int isEyeInWater;
uniform ivec2 eyeBrightness;
uniform ivec2 eyeBrightnessSmooth;
uniform sampler2D gaux1; //Overworld texture
uniform sampler2D gaux2; //End island texture
uniform sampler2D lightmap;
uniform sampler2D noisetex;
uniform sampler2D texture;
uniform vec3 actualCameraPosition;
uniform vec3 fogColor;
uniform vec3 skyColor;
uniform vec3 sunPosNorm;

varying float glmult;
varying vec2 lmcoord;
varying vec2 texcoord;

#ifdef CROSS_PROCESS
	varying vec3 crossProcessColor; //Color to use for cross-processing
#endif

varying vec3 shadowColor; //Color of shadows. Sky-colored, to simulate indirect lighting.
varying vec3 skyLightColor; //Color of sky light. Is usually white during the day, and very dark blue at night.
varying vec3 vPosPlayer;
varying vec3 vPosView;
varying vec4 tint;
varying vec4 heldLightColor; //Color of held light source. Alpha = brightness.

struct Position {
	vec3 view;
	vec3 viewNorm;
	vec3 player;
	vec3 playerNorm;
	vec3 world;
	float blockDist;
	float viewDist;
};

const int noiseTextureResolution = 256;
const float invNoiseRes = 1.0 / float(noiseTextureResolution);

//sines and cosines of multiples of the golden angle (~2.4 radians)
const vec2 goldenOffset0 = vec2( 0.675490294261524, -0.73736887807832 ); //2.39996322972865332
const vec2 goldenOffset1 = vec2(-0.996171040864828,  0.087425724716963); //4.79992645945731
const vec2 goldenOffset2 = vec2( 0.793600751291696,  0.608438860978863); //7.19988968918596
const vec2 goldenOffset3 = vec2(-0.174181950379306, -0.98471348531543 ); //9.59985291891461
const vec2 goldenOffset4 = vec2(-0.53672805262632,   0.843755294812399); //11.9998161486433
const vec2 goldenOffset5 = vec2( 0.965715074375778, -0.259604304901489); //14.3997793783719
const vec2 goldenOffset6 = vec2(-0.887448429245268, -0.460907024713344); //16.7997426081006
const vec2 goldenOffset7 = vec2( 0.343038630874082,  0.939321296324125); //19.1997058378292

const vec3 blockLightColorNear   = vec3(1.0,   0.85,  0.7); //color of block lights when the player is near a light source.
const vec3 blockLightColorFar    = vec3(1.0,   0.5,   0.15); //color of block lights when the player is far away from a light source.
const vec3 nightVisionLightColor = vec3(0.375, 0.375, 0.5);

const float vibrantSaturation         = 0.1; //Higher numbers mean more saturation when vibrant colors are enabled.

const float lavaOverlayResolution                     = 24.0;

//Overworld cloud colors are multi-dimensional because they're used by /lib/fastDrawClouds.glsl for end portal blocks.

//At night, this will be 0.
//When raining, this will be fogColor * 0.5
const vec3 cloudBaseColorDuringSunnyDays = vec3(0.48, 0.5, 0.55);

//At night, both of these will be 0.
const vec3 cloudIlluminationColorWhenSunny   = vec3(1.0,  1.0, 1.0);

#ifdef CROSS_PROCESS
	const vec3 nightSkyColor = vec3(0.02,  0.025, 0.05); //Added to sky color at night to avoid it being completely black
#else
	const vec3 nightSkyColor = vec3(0.025, 0.025, 0.05); //Added to sky color at night to avoid it being completely black
#endif

const vec3 sunsetColorForSky = vec3(7.2, 6.7, 6.2); //Subtract 6.0 from this to get the color of the horizon at sunset.

const float rainbowPosition = 0.25; //1.0 will be on top of the sun, 0.0 will be on top of the moon.
const float rainbowThinness = -24.0; //Positive numbers will make red be on the inside and blue on the outside.

//Absorb colors are a bit odd in that higher numbers mean
//that the color gets *darker* more quickly with distance.
const vec3 waterAbsorbColorWhenSunny   = vec3(0.2,   0.05,   0.1);
const vec3 waterAbsorbColorWhenRaining = vec3(0.125, 0.0625, 0.25);

const vec3 waterScatterColorWhenSunny   = vec3(0.05,    0.4, 0.5);
const vec3 waterScatterColorWhenRaining = vec3(0.09375, 0.1, 0.15);

float square(float x)        { return x * x; } //potentially faster than pow(x, 2.0).
float lengthSquared2(vec2 v) { return dot(v, v); }

float interpolateSmooth1(float x) { return x * x * (3.0 - 2.0 * x); }
vec2  interpolateSmooth2(vec2  v) { return v * v * (3.0 - 2.0 * v); }
vec3  interpolateSmooth3(vec3  v) { return v * v * (3.0 - 2.0 * v); }

float fogify(float x, float width) { return width / (x * x + width); } //fast, vaguely bell curve-shaped function with variable width

vec3 calcMainLightColor(in float blocklight, in float skylight, inout float heldlight, inout Position pos) {

	#ifdef VANILLA_LIGHTMAP
		vec3 lightclr = texture2D(lightmap, lmcoord).rgb;
	#else
		vec3 lightclr = vec3(0.0);
		lightclr += mix(blockLightColorFar, blockLightColorNear, eyeBrightnessSmooth.x / 240.0) * blocklight; //blocklight
		lightclr += mix(shadowColor, skyLightColor, skylight) * skylight; //skylight
		lightclr += clamp(nightVision, 0.0, 1.0) * nightVisionLightColor;
		lightclr += clamp(screenBrightness, 0.0, 1.0) * 0.1;
	#endif

	#ifdef DYNAMIC_LIGHTS
		if (heldLightColor.a > 0.0) {
			float heldLightDist = pos.blockDist * fov / heldLightColor.a;
			if (heldLightDist < 1.0) {
				heldlight = (heldLightDist - log(heldLightDist) - 1.0) * heldLightColor.a / ((skylight * day + blocklight) * 64.0 + 32.0);
				/*
				#ifdef DYNAMIC_LIGHT_VIGNETTE
				#endif
				*/
				#if DYNAMIC_LIGHT_VIGNETTE != 0
					vec2 screenPos = gl_FragCoord.xy * vec2(pixelSizeX, pixelSizeY); //0 to 1 range
					screenPos = screenPos * 2.0 - 1.0; //-1 to +1 range
					screenPos = 1.0 - screenPos * screenPos;
					float multiplier = screenPos.x * screenPos.y;
					multiplier = mix(1.0, multiplier, DYNAMIC_LIGHT_VIGNETTE / 100.0);
					heldlight *= multiplier;
				#endif
				lightclr += heldLightColor.rgb * heldlight;
			}
		}
	#endif

	return max(lightclr - square(darknessLightFactor), vec3(0.0));
}

vec3 calcFogColor(inout Position pos) {
	float sunDot = dot(pos.viewNorm, sunPosNorm) * 0.5 + 0.5;
	float upDot = pos.playerNorm.y * 2.0;
	float rainCoefficient = max(rainStrength, wetness);
	vec3 color;
	vec3 skyclr = skyColor;
	skyclr.rg = mix(skyclr.bb, skyclr.rg, OVERWORLD_SKY_SATURATION_BOOST);
	skyclr = mix(skyclr, fogColor * 0.65, rainCoefficient);
	vec3 fogclr = fogColor;
	fogclr.rg = mix(fogclr.bb, fogclr.rg, OVERWORLD_FOG_SATURATION_BOOST);
	fogclr *= (1.0 - rainCoefficient * 0.25) * (1.0 - nightVision * night * 0.75);

	if (upDot > 0.0) color = skyclr + nightSkyColor * (1.0 - day) * (1.0 - rainStrength); //avoid pitch black sky at night
	else color = fogclr;

	if (sunset > 0.001 && rainCoefficient < 0.999) {
		vec3 sunsetColor = interpolateSmooth3(clamp(sunsetColorForSky - adjustedTime + upDot + sunDot * 0.2 * (1.0 - night), 0.0, 1.0)); //main sunset gradient
		sunsetColor = mix(fogclr, sunsetColor, (sunDot * 0.5 + 0.5) * sunset * (1.0 - rainCoefficient)); //fade in at sunset and out when not looking at the sun
		color = mix(color, sunsetColor, fogify(upDot, OVERWORLD_HORIZON_HEIGHT)); //mix with final color based on how close we are to the horizon
	}
	else if (upDot > 0.0) color = mix(color, fogclr, fogify(upDot, OVERWORLD_HORIZON_HEIGHT));

	#ifdef RAINBOWS
		float rainbowStrength = (wetness - rainStrength) * day * 0.25;
		float rainbowHue = (sunDot - rainbowPosition) * rainbowThinness;
		if (rainbowStrength > 0.01 && rainbowHue > 0.0 && rainbowHue < 1.0) {
			rainbowHue *= 6.0;
			color.r += clamp(1.5 - abs(rainbowHue - 1.5), 0.0, 1.0) * rainbowStrength;
			color.g += clamp(2.0 - abs(rainbowHue - 3.0), 0.0, 1.0) * rainbowStrength;
			color.b += clamp(1.5 - abs(rainbowHue - 4.5), 0.0, 1.0) * rainbowStrength;
		}
	#endif

	return color;
}

vec3 calcUnderwaterFogColor(vec3 color, inout Position pos, float brightness) {
	float rainCoefficient = max(rainStrength, wetness);
	vec3 absorb = exp2(-pos.blockDist * mix(waterAbsorbColorWhenSunny, waterAbsorbColorWhenRaining, rainCoefficient));
	vec3 scatter = mix(waterScatterColorWhenSunny, waterScatterColorWhenRaining, rainCoefficient) * (1.0 - absorb) * (brightness * day);
	return color * absorb + scatter;
}

#ifdef END_PORTAL_EFFECTS_OVERWORLD
	vec3 hue(float h) {
		h = fract(h) * 6.0;
		return clamp(
			vec3(
				abs(h - 3.0) - 1.0,
				2.0 - abs(h - 2.0),
				2.0 - abs(h - 4.0)
			),
			0.0,
			1.0
		);
	}

	#if END_PORTAL_BACKGROUND_OVERWORLD == 2
		#ifdef ENDER_NEBULAE
			float random(vec2 coord) {
				vec2 middle = fract(coord);
				vec4 corners = vec4(coord - middle + 0.5, 0.0, 0.0);
				corners.zw = corners.xy + 1.0;
				corners *= invNoiseRes;

				float r00 = texture2D(noisetex, corners.xy).r; //random value at the (0, 0) corner
				float r01 = texture2D(noisetex, corners.xw).r; //random value at the (0, 1) corner
				float r10 = texture2D(noisetex, corners.zy).r; //random value at the (1, 0) corner
				float r11 = texture2D(noisetex, corners.zw).r; //random value at the (1, 1) corner

				vec2 mixlvl = interpolateSmooth2(middle); //non-linear interpolation

				return mix(mix(r00, r10, mixlvl.x), mix(r01, r11, mixlvl.x), mixlvl.y); //linear interpolation between the 4 corners
			}

			//base noise algorithm for the overall cloud pattern.
			float densityNoise(vec2 pos) {
				float noise = -0.32992;
				noise += random(pos * 2.0  + goldenOffset0 * frameTimeCounter * 0.125  ) * 0.4;
				noise += random(pos * 4.0  + goldenOffset1 * frameTimeCounter * 0.0625 ) * 0.16;
				noise += random(pos * 8.0  + goldenOffset2 * frameTimeCounter * 0.04166) * 0.064;
				noise += random(pos * 16.0 + goldenOffset3 * frameTimeCounter * 0.03125) * 0.0256;
				noise += random(pos * 32.0 + goldenOffset4 * frameTimeCounter * 0.025  ) * 0.01024;
				return noise;
			}

			//noise algorithm for all the different colors
			//doesn't need as many iterations as densityNoise() because it doesn't need to be as "rough"
			//also has different speed/effect multipliers in order to fit the values I want to get out of hue().
			vec3 colorNoise(vec2 pos) {
				float noise = 0.4;
				noise += random(pos * 2.0 + goldenOffset5 * frameTimeCounter * 0.5 ) * 0.25;
				noise += random(pos * 4.0 + goldenOffset6 * frameTimeCounter * 0.25) * 0.125;
				noise += random(pos * 8.0 + goldenOffset7 * frameTimeCounter * 0.16) * 0.0625;
				return interpolateSmooth3(hue(noise)) * 0.625;
			}

			//both of these functions are quite similar, just with some slight differences in the math.
			#ifdef ENDER_ARCS
				vec4 drawNebulae(vec2 pos) {
					float noise = abs(densityNoise(pos)); //density depends on how close densityNoise() is to 0.0
					if (noise < 0.25) { //alpha calculations work within the range 0 - 0.25
						vec3 baseclr = colorNoise(pos); //nebulae color at this position
						float arclight = square(max(0.7 - noise * 8.0, 0.0)); //brighten areas that are very close to an arc (when cloudNoise() is close to 0.0)
						return vec4(mix(baseclr, vec3(1.0), arclight), square(1.0 - noise * 4.0) * 0.9); //alpha also depends on how close to an arc we are
					}
					else return vec4(0.0); //was not part of a nebula
				}
			#else
				vec4 drawNebulae(vec2 pos) {
					float noise = densityNoise(pos) + 0.125; //density depends on how close densityNoise() is to 1.0
					if (noise > 0.0) {
						vec3 baseclr = colorNoise(pos); //nebulae color at this position
						baseclr += 1.0 - fogify(noise, 0.2); //brighten areas that are close to the "center" of the nebulae (when cloudNoise() is close to 1.0)
						return vec4(baseclr, 1.0 - fogify(noise, 0.05)); //alpha also depends on how close to the "center" of the nebulae we are
					}
					else return vec4(0.0);
				}
			#endif
		#endif

		#ifdef ENDER_STARS
			float fade(float speed, float delay) {
				float newTime = mod(frameTimeCounter * speed, delay);
				//newTime / threshold
				if (newTime < 0.1) return newTime * 10.0;
				//1.0 - (newTime / (1.0 - threshold)) + (threshold / (1.0 - threshold));
				else return newTime * -1.1111111111111111 + 1.1111111111111111;
			}

			vec3 drawStars(vec2 pos) {
				pos *= 16.0; //increase density of stars by a factor of 16x.
				vec2 newpos = floor(pos) + 0.5; //position rounded to the nearest "square". you can immagine this imposing a grid pattern onto the sky.

				//r = random chance that this square will be a star, g = fade animation speed, b = delay before re-appearing
				//r is also used to store "brightness" of the star. if the star is above 75% brightness to start with, it gets to be rendered. (this check is ignored for ender portals)
				vec3 starData1 = texture2D(noisetex, newpos * invNoiseRes).rgb;
				float fadeAmt = fade(starData1.g * 0.1 + 0.15, starData1.b * 8.0 + 1.0);

				if (starData1.r > 0.75 && fadeAmt > 0.0) { //25% of all the "squares" in the sky will be stars
					//r = type (star-shaped vs. circular), g = size multiplier, b = color
					vec3 starData2 = texture2D(noisetex, -newpos * invNoiseRes).rgb;

					float dist;
					//star-shaped stars are smaller than circular ones, so making more of them to compensate.
					if (starData2.r < 0.25) dist = length(pos - newpos) * 2.0; //pythagorean distance
					else { //star-shaped distance (distance increases faster diagonally than cardinally)
						vec2 v = sqrt(abs(pos - newpos));
						dist = v.x + v.y;
					}

					dist *= starData2.g + 1.0; //apply random size modifier. increasing the distance has the effect of decreasing the size, since smaller distances get scaled up to the maximum distance
					dist += 1.0 - fadeAmt; //apply fading animation. again, increasing distance decreases size. when fadeAmt = 0, the star will be invisible.

					float amt = square(max(1.0 - dist, 0.0)); //apply distance calculations to brightness of the star. The closer we are to the center, the brighter it should be.

					vec3 clr = hue(starData2.b * 0.6 - 0.35) * 0.625 + 0.375; //calculate color of star based on random number.
					clr = clr * amt + amt * amt * 0.625; //actually colorize star, and make whiter near the center.

					//make some stars brighter than others
					clr *= starData1.r * 2.0 - 1.0;

					return clr;
				}
				return vec3(0.0);
			}
		#endif
	#endif

	#if END_PORTAL_CLOUDS_OVERWORLD == 1
		#ifdef OLD_CLOUDS
			float cloudNoise(vec2 coord, float size) {
				coord /= size;
				vec4 corners = (vec4(floor(coord), ceil(coord)) + 0.5) * invNoiseRes;

				float r00 = texture2D(noisetex, corners.xy).r; //random value at the (0, 0) corner
				float r01 = texture2D(noisetex, corners.xw).r; //random value at the (0, 1) corner
				float r10 = texture2D(noisetex, corners.zy).r; //random value at the (1, 0) corner
				float r11 = texture2D(noisetex, corners.zw).r; //random value at the (1, 1) corner

				vec2 mixlvl = interpolateSmooth2(fract(coord));

				return mix(mix(r00, r10, mixlvl.x), mix(r01, r11, mixlvl.x), mixlvl.y) * 2.0 - 1.0; //non-linear interpolation between the 4 corners
			}

			vec4 drawClouds(vec2 pos) {
				pos *= 128.0;
				pos.x += frameTimeCounter; //apply wind

				float noise = 0.0;
				noise += cloudNoise(pos, 64.0) * 1.5;
				noise += cloudNoise(pos, 12.0);

				pos *= invNoiseRes;
				float colorNoise = 0.0;
				colorNoise += texture2D(noisetex, pos * 0.25).r - 0.5;
				colorNoise += texture2D(noisetex, pos       ).r * 0.5 - 0.25;
				colorNoise *= noise;

				if (noise > 0.0) { //there are clouds here
					return vec4(
						mix(
							cloudIlluminationColorWhenSunny,
							cloudBaseColorDuringSunnyDays,
							fogify(noise - colorNoise, 0.25)
						),
						1.0 - fogify(noise, 0.0625)
					);
				}
				return vec4(0.0);
			}
		#else
			//returns color and opacity of clouds
			vec4 drawClouds(vec2 pos) {
				float time = frameTimeCounter * invNoiseRes;
				pos.x += time; //apply wind
				pos *= invNoiseRes * 4.0;
				time *= 0.015625;

				float noise = 0.0;
				noise += (texture2D(noisetex, (pos + time * goldenOffset0)        ).r - 0.5) * 2.0;
				noise += (texture2D(noisetex, (pos + time * goldenOffset1) * 2.0  ).r - 0.5);
				noise += (texture2D(noisetex, (pos + time * goldenOffset2) * 4.0  ).r - 0.5) * 0.5;
				noise += (texture2D(noisetex, (pos + time * goldenOffset3) * 8.0  ).r - 0.5) * 0.25;
				noise += (texture2D(noisetex, (pos + time * goldenOffset4) * 16.0 ).r - 0.5) * 0.125;
				noise += (texture2D(noisetex, (pos + time * goldenOffset5) * 32.0 ).r - 0.5) * 0.0625;
				noise += (texture2D(noisetex, (pos + time * goldenOffset6) * 64.0 ).r - 0.5) * 0.03125;
				noise += (texture2D(noisetex, (pos + time * goldenOffset7) * 128.0).r - 0.5) * 0.015625;

				if (noise > 0.0) { //there are clouds here
					return vec4(
						mix(
							cloudIlluminationColorWhenSunny,
							cloudBaseColorDuringSunnyDays,
							fogify(noise, 0.25)
						),
						1.0 - fogify(noise, 0.0625)
					);
				}
				return vec4(0.0);
			}
		#endif
	#elif END_PORTAL_CLOUDS_OVERWORLD == 2
		vec4 drawVoidClouds(vec2 pos) {
			vec2 cloudPos = pos * invNoiseRes;
			float time = frameTimeCounter * invNoiseRes * 2.0;

			float noise = -1.0;
			noise += texture2D(noisetex, (cloudPos + goldenOffset0 * time) * 0.00390625).r;
			noise += texture2D(noisetex, (cloudPos + goldenOffset1 * time) * 0.0078125 ).r * 0.6;
			noise += texture2D(noisetex, (cloudPos + goldenOffset2 * time) * 0.015625  ).r * 0.36;
			noise += texture2D(noisetex, (cloudPos + goldenOffset3 * time) * 0.03125   ).r * 0.216;
			noise += texture2D(noisetex, (cloudPos + goldenOffset4 * time) * 0.0625    ).r * 0.1296;
			noise += texture2D(noisetex, (cloudPos + goldenOffset5 * time) * 0.125     ).r * 0.07776;
			noise += texture2D(noisetex, (cloudPos + goldenOffset6 * time) * 0.25      ).r * 0.046656;
			noise += texture2D(noisetex, (cloudPos + goldenOffset7 * time) * 0.5       ).r * 0.0279936;

			if (noise > 0.0) { //there are indeed clouds here
				vec3 color = vec3(noise * 0.125); //base cloud color

				vec3 data = texture2D(noisetex, (floor((pos + vec2(frameTimeCounter, 0.0)) * 2.0) + 0.5) * invNoiseRes).rgb; //r = hue, gb = another random offset
				float amt = texture2D(noisetex, data.gb * time * 0.125).r; //base brightness of square
				amt = max(amt * 8.0 - 8.0 + square(noise * 1.375), 0.0); //add bias so that there are more squares where cloud density is high
				color += hue(data.r * 0.35 + 0.45) * amt; //color of square

				return vec4(color, interpolateSmooth1(min(noise * 1.5, 1.0)));
			}
			else return vec4(0.0);
		}
	#endif
#endif

void main() {
	Position pos;
	pos.view = vPosView;
	pos.player = vPosPlayer;
	pos.world = vPosPlayer + actualCameraPosition;
	pos.blockDist = length(pos.view);
	pos.viewDist = pos.blockDist / far;
	pos.viewNorm = pos.view / pos.blockDist;
	pos.playerNorm = pos.player / pos.blockDist;

	vec4 color = texture2D(texture, texcoord);

	float skylight = lmcoord.y * lmcoord.y * (1.0 - rainStrength * 0.5);
	float blocklight = square(max(lmcoord.x - skylight * day * 0.5, 0.0));
	float heldlight = 0.0;

	#ifdef END_PORTAL_EFFECTS_OVERWORLD
		if (blockEntityId == 119) {
			vec2 backgroundPos = pos.player.xz / pos.player.y;

			#if END_PORTAL_BACKGROUND_OVERWORLD == 1 //overworld fog color
				color = vec4(0.75, 0.875, 1.0, 1.0);
			#elif END_PORTAL_BACKGROUND_OVERWORLD == 2 //end background
				//draw background noise, matching vanilla end sky texture as closely as possible
				float n = texture2D(noisetex, (floor(backgroundPos * 256.0) + 0.5) * invNoiseRes).r * 2.0 - 1.0; //get random noise value between -1.0 and 1.0
				float n4 = square(square(n)); //bias towards 0 by raising to the 4'th power. also always positive because 4 is even, so don't need to take absolute value.
				color.rgb = vec3(0.46, 0.34, 0.65); //base color
				if (n > 0.0) color.rgb = mix(color.rgb, vec3(1.0), n4 * 0.55); //bright areas
				else         color.rgb = mix(color.rgb, vec3(0.0), n4 * 0.7); //dark areas
				color.rgb *= 0.16; //match vanilla brightness (40/255 ~= 0.16)
				
				vec2 endProjectionPos = pos.playerNorm.xz / (pos.playerNorm.y + 1.0);
				float multiplier = 8.0 / (lengthSquared2(endProjectionPos) + 8.0); //wrapping behavior produces a mathematical singularity below you, so this hides that.

				#ifdef ENDER_NEBULAE
					vec4 cloudclr = drawNebulae(endProjectionPos);
					color.rgb = mix(color.rgb, cloudclr.rgb, cloudclr.a * multiplier);
				#endif

				#ifdef ENDER_STARS
					vec3 starclr = drawStars(endProjectionPos);
					color.rgb += starclr * multiplier;
				#endif
			#endif

			#if END_PORTAL_FOREGROUND_OVERWORLD == 1 //overworld screenshot
				vec4 islandImage = texture2D(gaux1, backgroundPos * 0.25 + 0.5);
				color.rgb = mix(color.rgb, islandImage.rgb, islandImage.a);
			#elif END_PORTAL_FOREGROUND_OVERWORLD == 2 //end screenshot
				vec4 islandImage = texture2D(gaux2, backgroundPos + 0.5);
				color.rgb = mix(color.rgb, islandImage.rgb, islandImage.a);
			#endif

			#if END_PORTAL_CLOUDS_OVERWORLD == 1 //overworld clouds
				vec2 projectedPos = (pos.player.xz / pos.player.y) * 0.25 + 0.5;
				vec4 cloudColor = drawClouds(projectedPos);
				color.rgb = mix(color.rgb, cloudColor.rgb, cloudColor.a);
			#elif END_PORTAL_CLOUDS_OVERWORLD == 2 //void clouds
				vec4 cloudColor = drawVoidClouds(backgroundPos * 64.0);
				color.rgb = mix(color.rgb, cloudColor.rgb, cloudColor.a);
			#endif

			#ifdef BRIGHT_PORTAL_FIX
				color.rgb *= 0.0625;
			#endif
		}
		else {
	#endif
			color.rgb *= calcMainLightColor(blocklight, skylight, heldlight, pos);
			vec4 multiplier = tint;
			multiplier.rgb *= mix(glmult, 1.0, max(blocklight, heldlight) * 0.5);
			color *= multiplier;
	#ifdef END_PORTAL_EFFECTS_OVERWORLD
		}
	#endif

	float desaturationAmount = mix(night * DESATURATE_NIGHT, 1.0, rainStrength * DESATURATE_RAIN);
	if (desaturationAmount > 0.001) {
		desaturationAmount *= 1.0 - max(blocklight, heldlight);
		float average = dot(color.rgb, vec3(0.25, 0.5, 0.25));
		color.rgb = mix(color.rgb, vec3(average), desaturationAmount);
	}

	#ifdef CROSS_PROCESS
		//vec3(color.g + color.b, color.r + color.b, color.r + color.g)
		color.rgb = clamp(color.rgb * crossProcessColor - (color.grr + color.bbg) * vibrantSaturation, 0.0, 1.0);
	#endif

	#ifdef UNDERWATER_FOG
		if (isEyeInWater == 1) {
			color.rgb = calcUnderwaterFogColor(color.rgb, pos, eyeBrightnessSmooth.y / 240.0);
		}
		else {
	#endif
			#ifdef FOG_ENABLED_OVERWORLD
				float fogDensity = exp2(
					(
						//equivalen to: (pos.world.y - SEA_LEVEL) * (-1.0 / FOG_HEIGHT_MULTIPLIER_OVERWORLD)
						pos.world.y
						* (-1.0 / FOG_HEIGHT_MULTIPLIER_OVERWORLD)
						+ (SEA_LEVEL / FOG_HEIGHT_MULTIPLIER_OVERWORLD)
					)
					* (1.0 - rainStrength)
					+ (rainStrength * 2.0)
				)
				* FOG_DENSITY_MULTIPLIER_OVERWORLD;
				float fogAmount = fogify(pos.blockDist / fogEnd * fogDensity, 1.0);
				color.rgb = mix(calcFogColor(pos) * min(max(lmcoord.y * 2.0, eyeBrightness.y / 120.0), 1.0), color.rgb, fogAmount);
			#endif
	#ifdef UNDERWATER_FOG
		}
	#endif

	if (blindness > 0.0) color.rgb *= interpolateSmooth1(max(1.0 - pos.blockDist * 0.2, 0.0)) * 0.5 * blindness + (1.0 - blindness);

/* DRAWBUFFERS:0 */
	gl_FragData[0] = color; //gcolor
}