Shader "CrispyPin/Sunset Environment (Dynamic)"
{
	Properties
	{
		[Header(Sky)]
		_SkyCol ("Sky color", Color) = (0.22, 0.23, 0.58, 1.0)
		_HorizonTint ("Horizon tint", Range(0, 1)) = 0.1
		[Header(Sun)]
		_SunCol ("Sun color", Color) = (1.0, 0.65, 0.05, 1.0)
		_SunRadius ("Sun radius", Range(0, 0.3)) = 0.06
		_SunCutoff ("Sun cutoff", Range(0, 0.5)) = 0.08
		[Header(Star Layout)]
		[NoScaleOffset]
		_NoiseTex ("Noise source", 2D) = "white" {}
		_StarDensity ("Star density", Range(4, 50)) = 20
		_StarRandom ("Star randomness", Range(0, 1)) = 0.85
		[Header(Star)]
		_StarsMissing ("Stars missing", Range(0, 1)) = 0.75
		_StarSize ("Star size", Range(0, 0.1)) = 0.06
		_StarSizeRandom ("Star size randomness", Range(0, 1)) = 0.5
		_StarTint ("Star tint", Range(0, 1)) = 0.4
		[Header(Water)]
		_HeightOffset ("Height offset", Range(-10, 10)) = -0.5
		_WaterCol ("Water color", Color) = (0.03, 0.08, 0.12, 1.0)
		_WaveStrength ("Wave scale", Range(0, 1)) = 1
		_WaveSpeed ("Wave speed", Range(0, 5)) = 1
		[NoScaleOffset]
		_WaterNormal ("Surface Normal", 2D) = "white" {}
		// [NoScaleOffset]
		// _WaterHeight ("Height", 2D) = "white" {}
		// [NoScaleOffset]
		// _WaterAO ("AO", 2D) = "white" {}
		[Header(Debug)]
		// _TempTex ("Temp", 2D) = "white" {}
		_Grid ("Grid visibility", Range(0, 1)) = 0

	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100
		Cull back

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			#define PI 3.1416f
			#define WHITE 1
			#define RED float3(1, 0, 0)
			#define GREEN float3(0, 1, 0)
			#define BLUE float3(0, 1, 1)
			#define UP float3(0, 1, 0)

			struct appdata
			{
				float4 vertex : POSITION;

				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float3 cam_pos : TEXCOORD0;
				float3 hit_pos : TEXCOORD1;

				UNITY_VERTEX_OUTPUT_STEREO
			};

			sampler2D _NoiseTex;
			float3 _SkyCol;
			float _HorizonTint;

			float _StarsMissing;
			float _StarDensity;
			float _StarRandom;

			float _StarSize;
			float _StarSizeRandom;
			float _StarTint;

			float3 _SunCol;
			float _SunAngle;
			float _SunRadius;
			float _SunCutoff;

			sampler2D _WaterNormal;
			// sampler2D _WaterHeight;
			// sampler2D _WaterAO;
			float3 _WaterCol;
			float _HeightOffset;
			float _WaveStrength;
			float _WaveSpeed;

			float _Grid;

			v2f vert (appdata v)
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID(v);
				UNITY_INITIALIZE_OUTPUT(v2f, o);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

				o.vertex = UnityObjectToClipPos(v.vertex);
				o.cam_pos = _WorldSpaceCameraPos;
				o.hit_pos = mul(unity_ObjectToWorld, v.vertex);
				return o;
			}

			float3 get_water_normal(float2 pos) {
				pos *= 0.5;
				float time = _Time.x * _WaveSpeed + 5; // offset is to make it look better at frame 0
				float t1 = time * 0.18;
				float3 normal = (tex2D(_WaterNormal, pos + float2(t1, -t1 * 0.5)) - 0.5) * 0.5;
				float t2 = time * 0.37;
				normal += (tex2D(_WaterNormal, pos * 0.276 + float2(t2 * 0.8, t2)) - 0.5);
				float t3 = time * 0.08;
				normal += (tex2D(_WaterNormal, pos * 0.07 + float2(t3 * 0.8, -t3)) - 0.5);
				// normal = normalize(normal.zxy); // normal map import settings
				normal = normalize(normal.rbg) * float3(-1, 1, -1); // standard import settings

				// normal = normalize(normal);

				// return UP;
				return lerp(UP, normal, _WaveStrength);
			}

			// float3 get_ao(float2 pos) {
			// 	pos *= 0.1;
			// 	float3 ao = 0;
			// 	float t1 = _Time.x * 0.18;
			// 	ao += (tex2D(_WaterAO, float2(1, -1) * pos + float2(t1, t1 * 0.5)));
			// 	float t2 = _Time.x * 0.37;
			// 	ao += (tex2D(_WaterAO, pos * 0.276 + float2(t2 * 0.8, t2)));
			// 	float t3 = _Time.x * 0.08;
			// 	ao += (tex2D(_WaterAO, pos * 0.07 + float2(t3 * 0.8, -t3)));

			// 	return ao/3;
			// }

			inline float smin(float a, float b, float k)
			{
				float h = max(k - abs(a - b), 0) / k;
				return min(a, b) - h * h * h * k * 1/6;
			}

			inline float smax(float a, float b, float k)
			{
				float h = max(k - abs(a - b), 0) / k;
				return max(a, b) + h * h * h * k * 1/6;
			}

			float3 sun(float3 base_col, float3 dir, float3 sun_dir)
			{
				float alignment = min(acos(dot(dir, sun_dir)), 1);
				float sun_amount = smax(min(_SunRadius / alignment, 5) - _SunCutoff, 0, 0.15);
				return lerp(base_col, _SunCol, sun_amount);
			}

			float3 sky(float3 dir, float3 sun_dir)
			{
				float theta = atan2(dir.x, dir.z); // latitude
				float phi = asin(dir.y); // longitude
				/// background
				float factor = smoothstep(0, 0.5, dir.y + 0.2);
				float3 horizon_col = lerp(_SkyCol, _SunCol, _HorizonTint);
				float3 col = lerp(horizon_col, _SkyCol, factor);

				/// stars
				float2 cells = float2(-1, floor(_StarDensity));
				float cell_x_base = floor(cells.y * PI);
				float celly = phi * cells.y;
				// cells per ring depend on y pos, to reduce warping around the poles:
				cells.x = floor(cos(floor(celly) / _StarDensity) * cell_x_base);
				float cellx = (theta / PI * cells.x);

				float2 pos = float2(cellx, celly); // cell-space pos of this pixel
				float2 cell_pos = float2(floor(cellx), floor(celly)); // position of this cell
				float2 cell_center = cell_pos + 0.5;

				float2 star_pos = cell_center + (tex2D(_NoiseTex, cell_pos / cells + float2(0, 0.1)) - 0.5) * _StarRandom;

				/// star color
				float3 r = tex2D(_NoiseTex, cell_pos / cells);
				float rnum = frac((r.r + r.g - r.b) * 10);
				float rnum2 = frac((r.r - r.g + r.b) * 10);

				float star_size = _StarSize * (rnum * _StarSizeRandom + (1 - _StarSizeRandom));

				float distance = length(pos - star_pos);
				float star_strength = max(min(star_size / distance * 0.5, 1.25) - 0.25, 0); // star glow

				star_strength *= clamp(sin(phi * 2) - 0.1, 0, 1); // fade stars near/under horizon
				star_strength *= length(r) / 2; // fade stars
				star_strength *= rnum2 > _StarsMissing; // remove stars

				float3 star_col = lerp(WHITE, r, _StarTint);

				col = lerp(col, star_col, star_strength);

				col = sun(col, dir, sun_dir);
				// col = sun(col, dir, float3(1,0,0));
				// col = sun(col, dir, float3(-1,0,0));
				// col = sun(col, dir, float3(0,0,1));
				// col = sun(col, dir, float3(0,0,-1));
				// col = sun(col, dir, normalize(float3(-1,0,-1)));
				// col = sun(col, dir, normalize(float3(1,0,-1)));
				// col = sun(col, dir, normalize(float3(-1,0,1)));
				// col = sun(col, dir, normalize(float3(1,0,1)));

				/// debug grid
				col = lerp(col, WHITE, _Grid * (
					pow(frac( cellx), 20) +
					pow(frac( celly), 20) +
					pow(frac(-cellx), 20) +
					pow(frac(-celly), 20)
				));

				return col;
			}

			float4 frag(v2f i) : SV_Target
			{
				// float3 horizon_col = lerp(_SkyCol, _SunCol, _HorizonTint);
				float3 origin = mul(unity_ObjectToWorld, float4(0, 0, 0, 1));
				float3 sun_dir = (mul(unity_ObjectToWorld, float4(0, 0, -1, 1)) - origin) * float3(1, 0, 1);
				// sun_dir.y = sin(_Time.x * 10) * 0.05;
				sun_dir.y = 0.01;
				sun_dir = normalize(sun_dir);

				float3 dir = normalize(i.hit_pos - i.cam_pos);

				float3 col;

				if (dir.y < 0) {
					origin.y += _HeightOffset;
					float3 camera_local_pos = i.cam_pos - origin;
					camera_local_pos.y = max(camera_local_pos.y, 0.01); // don't allow looking under water surface; it renders backwards.
					float3 surface_pos = float3 (
						camera_local_pos.x - camera_local_pos.y / (dir.y / dir.x),
						0,
						camera_local_pos.z - camera_local_pos.y / (dir.y / dir.z)
					);
					float3 water_normal = get_water_normal(surface_pos.xz);

					float3 reflected_dir = reflect(dir, water_normal);
					float3 sky_reflection = sky(reflected_dir, sun_dir) ;

					float3 water_col = lerp(_SkyCol, _SunCol, 0.01) * _WaterCol;
					float3 refracted_dir = normalize(refract(dir, water_normal, 1/1.333));
					float subsurf = (refracted_dir.y + 1);

					col = water_col * max(subsurf * 12 - 2, 0.7);

					float diffuse = max(dot(sun_dir, water_normal), 0);
					col += water_col * _SunCol * diffuse * 10;

					// float3 water_ao = get_ao(surface_pos.xz);
					// col = water_col * lerp(0.1, water_ao, _WaveStrength) * 7;

					float hit_angle = dot(dir, -water_normal);
 					col = lerp(sky_reflection, col, hit_angle);

					// col = sky_reflection;
					// col *= pow(dot(UP, water_normal), 512); // EVIL in the water
					// col = pow((tex2D(_WaterNormal, surface_pos.xz).zxy), 2);
					// col = tex2D(_WaterAO, surface_pos.xz);

					// float distance = length(surface_pos - camera_local_pos);
					// float fog_factor = smoothstep(10, 70, distance) * 0.4;
					// col = lerp(col, horizon_col, fog_factor);
				}
				else {
					col = sky(dir, sun_dir);
				}
				return float4(col, 1);
			}
			ENDCG
		}
	}
}
