// -----
// drops
// -----
// drops are based on this shader:
//
// Heartfelt - by Martijn Steinrucken aka BigWings - 2017
// Email:countfrolic@gmail.com Twitter:@The_ArtOfCode
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
//
#define S(a, b, t) smoothstep(a, b, t)

vec3 N13(float p) {
 //  from DAVE HOSKINS
 vec3 p3 = fract(vec3(p) * vec3(.1031,.11369,.13787));
 p3 += dot(p3, p3.yzx + 19.19);
 return fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
}

vec4 N14(float t) {
  return fract(sin(t*vec4(123., 1024., 1456., 264.))*vec4(6547., 345., 8799., 1564.));
}

float N(float t) {
  return fract(sin(t*12345.564)*7658.76);
}

float Saw(float b, float t) {
	return S(0., b, t)*S(1., b, t);
}

vec2 DropLayer2(vec2 uv, float t) {
  vec2 UV = uv;
    
  uv.y += t*0.75;
  vec2 a = vec2(6., 1.);
  vec2 grid = a*2.;
  vec2 id = floor(uv*grid);
    
  float colShift = N(id.x); 
  uv.y += colShift;
    
  id = floor(uv*grid);
  vec3 n = N13(id.x*35.2+id.y*2376.1);
  vec2 st = fract(uv*grid)-vec2(.5, 0);
    
  float x = n.x-.5;
    
  float y = UV.y*20.;
  float wiggle = sin(y+sin(y));
  x += wiggle*(.5-abs(x))*(n.z-.5);
  x *= .7;
  float ti = fract(t+n.z);
  y = (Saw(.85, ti)-.5)*.9+.5;
  vec2 p = vec2(x, y);
    
  float d = length((st-p)*a.yx);
    
  float mainDrop = S(.4, .0, d);
    
  float r = sqrt(S(1., y, st.y));
  float cd = abs(st.x-x);
  float trail = S(.23*r, .15*r*r, cd);
  float trailFront = S(-.02, .02, st.y-y);
  trail *= trailFront*r*r;
    
  y = UV.y;
  float trail2 = S(.2*r, .0, cd);
  float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z;
  y = fract(y*10.)+(st.y-.5);
  float dd = length(st-vec2(x, y));
  droplets = S(.3, 0., dd);
  float m = mainDrop+droplets*r*trailFront;
    
  return vec2(m, trail);
}

float StaticDrops(vec2 uv, float t) {
  uv *= 40.;
    
  vec2 id = floor(uv);
  uv = fract(uv)-.5;
  vec3 n = N13(id.x*107.45+id.y*3543.654);
  vec2 p = (n.xy-.5)*.7;
  float d = length(uv-p);
    
  float fade = Saw(.025, fract(t+n.z));
  float c = S(.3, 0., d)*fract(n.z*10.)*fade;
  return c;
}

vec2 Drops(vec2 uv, float t, float l0, float l1, float l2) {
  float s = StaticDrops(uv, t)*l0; 
  vec2 m1 = DropLayer2(uv, t)*l1;
  vec2 m2 = DropLayer2(uv*1.85, t)*l2;
    
  float c = s+m1.x+m2.x;
  c = S(.3, 1., c);
    
  return vec2(c, max(m1.y*l0, m2.y*l1));
}

void mainDrops( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = (fragCoord.xy-.5*iResolution.xy) / iResolution.y;
  vec2 UV = fragCoord.xy/iResolution.xy;
  vec3 M = iMouse.xyz/iResolution.xyz;
  float T = 100.0 + iTime+M.y*2.; // make drop move up/down faster/slower if we move the "glass" up/down, like in reality
    
  float t = T*.2;
    
  float rainAmount = 1.0;
  float maxBlur = mix(3., 6., rainAmount);
  float minBlur = 1.0;
    
  float story = 0.;

  float zoom = -0.8;
  uv *= .7+zoom*.3;
  UV = (UV-.5)*(.9+zoom*.1)+.5;
    
  float staticDrops = S(-.5, 1., rainAmount)*2.;
  float layer1 = S(.25, .75, rainAmount);
  float layer2 = S(.0, .5, rainAmount);
    
    
  vec2 c = Drops(uv, t, staticDrops, layer1, layer2);
  vec2 e = vec2(.001, 0.);
  float cx = Drops(uv+e, t, staticDrops, layer1, layer2).x;
  float cy = Drops(uv+e.yx, t, staticDrops, layer1, layer2).x;
  vec2 n = vec2(cx-c.x, cy-c.x); // expensive normals
    
  float focus = mix(maxBlur-c.y, minBlur, S(.1, .2, c.x));
  vec3 col = textureLod(iChannel0, UV+n, focus).rgb;
    
  // post processing
  t = (T+3.)*.5;										// make time sync with first lightning
  float colFade = sin(t*.2)*.5+.5+story;
  //col *= mix(vec3(1.), vec3(.8, .9, 1.3), colFade);	// subtle color shift
  float fade = S(0., 10., T);							// fade in at the start
  float lightning = sin(t*sin(t*10.));					// lighting flicker
  lightning *= pow(max(0., sin(t+sin(t))), 10.);		// lightning flash
  col *= 1.+lightning*fade*mix(1., .1, story*story);	// composite lightning
  col *= 1.-dot(UV-=.5, UV);							// vignette
  col *= fade;											// composite start and end fade
    
  fragColor = vec4(col, 1.);
}
// -----


// -----
// waves
// -----
// waves are based on this shader:
// 
// Very fast procedural ocean by afl_ext
//
#define DRAG_MULT 0.048
#define ITERATIONS_RAYMARCH 13
#define ITERATIONS_NORMAL 48

#define Mouse (iMouse.xy / iResolution.xy)
#define Resolution (iResolution.xy)
#define Time (iTime)

vec2 wavedx(vec2 position, vec2 direction, float speed, float frequency, float timeshift) {
  float x = dot(direction, position) * frequency + timeshift * speed;
  float wave = exp(sin(x) - 1.0);
  float dx = wave * cos(x);
  return vec2(wave, -dx);
}

float getwaves(vec2 position, int iterations){
  float iter = 10.0;
  float phase = 6.0;
  float speed = 0.5;
  float weight = 1.0;
  float w = 0.0;
  float ws = 0.0;
  for(int i=0;i<iterations;i++){
    vec2 p = vec2(sin(iter), cos(iter));
    vec2 res = wavedx(position, p, speed, phase, Time);
    position += normalize(p) * res.y * weight * DRAG_MULT;
    w += res.x * weight;
    iter += 12.0;
    ws += weight;
    weight = mix(weight, 0.0, 0.2);
    phase *= 1.18;
    speed *= 1.07;
  }
  return w / ws;
}

float raymarchwater(vec3 camera, vec3 start, vec3 end, float depth){
  vec3 pos = start;
  float h = 0.0;
  float hupper = depth;
  float hlower = 0.0;
  vec2 zer = vec2(0.0);
  vec3 dir = normalize(end - start);
  for(int i=0;i<318;i++){
    h = getwaves(pos.xz * 0.1, ITERATIONS_RAYMARCH) * depth - depth;
    if(h + 0.01 > pos.y) {
      return distance(pos, camera);
    }
    pos += dir * (pos.y - h);
  }
  return -1.0;
}

float H = 0.0;
vec3 normal(vec2 pos, float e, float depth){
  vec2 ex = vec2(e, 0);
  H = getwaves(pos.xy * 0.1, ITERATIONS_NORMAL) * depth;
  vec3 a = vec3(pos.x, H, pos.y);
  return normalize(cross(normalize(a-vec3(pos.x - e, getwaves(pos.xy * 0.1 - ex.xy * 0.1, ITERATIONS_NORMAL) * depth, pos.y)), 
                         normalize(a-vec3(pos.x, getwaves(pos.xy * 0.1 + ex.yx * 0.1, ITERATIONS_NORMAL) * depth, pos.y + e))));
}

mat3 rotmat(vec3 axis, float angle) {
  axis = normalize(axis);
  float s = sin(angle);
  float c = cos(angle);
  float oc = 1.0 - c;
  return mat3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s,  oc * axis.z * axis.x + axis.y * s, 
  oc * axis.x * axis.y + axis.z * s,  oc * axis.y * axis.y + c,           oc * axis.y * axis.z - axis.x * s, 
  oc * axis.z * axis.x - axis.y * s,  oc * axis.y * axis.z + axis.x * s,  oc * axis.z * axis.z + c);
}

vec3 getRay(vec2 uv){
  uv = (uv * 2.0 - 1.0) * vec2(Resolution.x / Resolution.y, 1.0);
  vec3 proj = normalize(vec3(uv.x, uv.y, 1.0) + vec3(uv.x, uv.y, -1.0) * pow(length(uv), 2.0) * 0.05);	
  if(Resolution.x < 400.0) return proj;
    
    vec2 mouse = vec2(0.5,0.5); // use these default mouse cooridnates so we're not staring at into the water before clicking
    if (0.0 != Mouse.x) {mouse.x = Mouse.x;} // follow mouse if not at default position
    if (0.0 != Mouse.y) {mouse.y = Mouse.y;} // follow mouse if not at default position
  
  vec3 ray = rotmat(vec3(0.0, -1.0, 0.0), 3.0 * (mouse.x * 2.0 - 1.0)) * rotmat(vec3(1.0, 0.0, 0.0), 1.5 * (mouse.y * 2.0 - 1.0)) * proj;
  return ray;
}

float intersectPlane(vec3 origin, vec3 direction, vec3 point, vec3 normal)
{ 
  return clamp(dot(point - origin, normal) / dot(direction, normal), -1.0, 9991999.0); 
}

vec3 extra_cheap_atmosphere(vec3 raydir, vec3 sundir){
  sundir.y = max(sundir.y, -0.07);
  float special_trick = 1.0 / (raydir.y * 1.0 + 0.1);
  float special_trick2 = 1.0 / (sundir.y * 11.0 + 1.0);
  float raysundt = pow(abs(dot(sundir, raydir)), 2.0);
  float sundt = pow(max(0.0, dot(sundir, raydir)), 8.0);
  float mymie = sundt * special_trick * 0.2;
  vec3 suncolor = mix(vec3(1.0), max(vec3(0.0), vec3(1.0) - vec3(5.5, 13.0, 22.4) / 22.4), special_trick2);
  vec3 bluesky= vec3(5.5, 13.0, 22.4) / 22.4 * suncolor;
  vec3 bluesky2 = max(vec3(0.0), bluesky - vec3(5.5, 13.0, 22.4) * 0.002 * (special_trick + -6.0 * sundir.y * sundir.y));
  bluesky2 *= special_trick * (0.24 + raysundt * 0.24);
  return bluesky2 * (1.0 + 1.0 * pow(1.0 - raydir.y, 3.0)) + mymie * suncolor;
} 

vec3 getatm(vec3 ray){
  return extra_cheap_atmosphere(ray, normalize(vec3(1.0))) * 0.5;
}

float sun(vec3 ray){
  vec3 sd = normalize(vec3(1.0));   
  return pow(max(0.0, dot(ray, sd)), 64.0) * 1100.0; // <moon smaller>) * <moon bigger>;
}

vec3 aces_tonemap(vec3 color){	
  mat3 m1 = mat3(
    0.59719, 0.07600, 0.02840,
    0.35458, 0.90834, 0.13383,
    0.04823, 0.01566, 0.83777
  );
  mat3 m2 = mat3(
    1.60475, -0.10208, -0.00327,
    -0.53108,  1.10813, -0.07276,
    -0.07367, -0.00605,  1.07602
  );
  vec3 v = m1 * color;    
  vec3 a = v * (v + 0.0245786) - 0.000090537;
  vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081;
  return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2));	
}

void mainWaves(out vec4 fragColor, in vec2 fragCoord) {
  vec2 uv = fragCoord.xy / iResolution.xy;
 	
  float waterdepth = 2.1;
  vec3 wfloor = vec3(0.0, -waterdepth, 0.0);
  vec3 wceil = vec3(0.0, 0.0, 0.0);
  vec3 orig = vec3(0.0, 0.0, 0.0); // (<>, <viewer's height above water>, <>)
  vec3 ray = getRay(uv);
  float hihit = intersectPlane(orig, ray, wceil, vec3(0.0, 1.0, 0.0));
  if(ray.y >= -0.01){
    // above the horizon
    vec3 C = getatm(ray) * 1.5 + sun(ray); // <> * <horizon lightness> + <>
    C = aces_tonemap(C);
    fragColor = vec4(C,1.0);   
  } else {
    // below the horizon
    float lohit = intersectPlane(orig, ray, wfloor, vec3(0.0, 1.0, 0.0));
    vec3 hipos = orig + ray * hihit;
    vec3 lopos = orig + ray * lohit;
	float dist = raymarchwater(orig, hipos, lopos, waterdepth);
    vec3 pos = orig + ray * dist;

	vec3 N = normal(pos.xz, 0.001, waterdepth); // (<>, <water roughness>, <>)
    vec2 velocity = N.xz * (1.0 - N.y);
    N = mix(vec3(0.0, 1.0, 0.0), N, 1.0 / (dist * dist * 0.01 + 1.0));
    vec3 R = reflect(ray, N);
    float fresnel = (0.04 + (1.0-0.04)*(pow(1.0 - max(0.0, dot(-N, ray)), 5.0)));
	
    vec3 C = fresnel * getatm(R) * 2.0 + fresnel * sun(R);
    //tonemapping
    C = aces_tonemap(C);
    
	fragColor = vec4(C,1.0);
  }
}
// -----

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
  // waves
  vec4 fragColorWaves;
  mainWaves(fragColorWaves, fragCoord); 
    
  // drops
  vec4 fragColorDrops;
  mainDrops(fragColorDrops, fragCoord);
    
  // combine
  fragColor = fragColorWaves - fragColorDrops;
    
  // make b&w
  //fragColor = vec4(fragColor.x, fragColor.x, fragColor.x, fragColor.x);
  fragColor = vec4(fragColor.y, fragColor.y, fragColor.y, fragColor.y);
  //fragColor = vec4(fragColor.z, fragColor.z, fragColor.z, fragColor.z);
}
