From 3f2efea4f2f3c5bfb80ae76e5dde132fea989bd5 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Fri, 23 Sep 2022 15:04:47 +0100
Subject: [PATCH 01/28] Experimental river velocity in shaders

---
 assets/voxygen/i18n/en/hud/settings.ftl       |  5 +-
 assets/voxygen/shaders/clouds-frag.glsl       |  2 +-
 assets/voxygen/shaders/clouds-vert.glsl       |  2 +-
 assets/voxygen/shaders/figure-frag.glsl       |  6 +-
 assets/voxygen/shaders/fluid-frag/cheap.glsl  |  5 +-
 assets/voxygen/shaders/fluid-frag/shiny.glsl  | 80 ++++++++++++++-----
 assets/voxygen/shaders/fluid-vert.glsl        | 10 ++-
 assets/voxygen/shaders/include/constants.glsl |  3 +-
 .../shaders/light-shadows-directed-vert.glsl  |  2 +-
 .../shaders/light-shadows-figure-vert.glsl    |  2 +-
 .../voxygen/shaders/light-shadows-frag.glsl   |  2 +-
 .../voxygen/shaders/light-shadows-geom.glsl   |  2 +-
 .../voxygen/shaders/light-shadows-vert.glsl   |  2 +-
 assets/voxygen/shaders/lod-object-frag.glsl   |  4 +-
 assets/voxygen/shaders/lod-terrain-frag.glsl  |  6 +-
 assets/voxygen/shaders/lod-terrain-vert.glsl  |  2 +-
 assets/voxygen/shaders/particle-frag.glsl     |  6 +-
 .../shaders/point-light-shadows-vert.glsl     |  2 +-
 assets/voxygen/shaders/postprocess-frag.glsl  |  2 +-
 assets/voxygen/shaders/postprocess-vert.glsl  |  2 +-
 .../shaders/rain-occlusion-directed-vert.glsl |  2 +-
 .../shaders/rain-occlusion-figure-vert.glsl   |  2 +-
 assets/voxygen/shaders/skybox-frag.glsl       |  2 +-
 assets/voxygen/shaders/skybox-vert.glsl       |  2 +-
 assets/voxygen/shaders/sprite-frag.glsl       |  6 +-
 assets/voxygen/shaders/terrain-frag.glsl      |  6 +-
 assets/voxygen/shaders/terrain-vert.glsl      |  2 +-
 assets/voxygen/shaders/trail-frag.glsl        |  2 +-
 voxygen/src/hud/settings_window/video.rs      |  6 +-
 voxygen/src/mesh/terrain.rs                   | 21 ++++-
 voxygen/src/render/mod.rs                     |  5 +-
 voxygen/src/render/pipelines/fluid.rs         | 11 ++-
 .../src/render/renderer/pipeline_creation.rs  |  5 +-
 voxygen/src/scene/terrain.rs                  | 10 +--
 34 files changed, 150 insertions(+), 79 deletions(-)

diff --git a/assets/voxygen/i18n/en/hud/settings.ftl b/assets/voxygen/i18n/en/hud/settings.ftl
index cb895bebe5..9dcf8e2988 100644
--- a/assets/voxygen/i18n/en/hud/settings.ftl
+++ b/assets/voxygen/i18n/en/hud/settings.ftl
@@ -73,7 +73,8 @@ hud-settings-upscale_factor = Internal Resolution
 hud-settings-cloud_rendering_mode = Cloud Rendering Mode
 hud-settings-fluid_rendering_mode = Fluid Rendering Mode
 hud-settings-fluid_rendering_mode-cheap = Cheap
-hud-settings-fluid_rendering_mode-shiny = Shiny
+hud-settings-fluid_rendering_mode-medium = Medium
+hud-settings-fluid_rendering_mode-high = High
 hud-settings-cloud_rendering_mode-minimal = Minimal
 hud-settings-cloud_rendering_mode-low = Low
 hud-settings-cloud_rendering_mode-medium = Medium
@@ -93,7 +94,7 @@ hud-settings-resolution = Resolution
 hud-settings-bit_depth = Bit Depth
 hud-settings-refresh_rate = Refresh Rate
 hud-settings-lighting_rendering_mode = Lighting Rendering Mode
-hud-settings-lighting_rendering_mode-ashikhmin = Type A - High    
+hud-settings-lighting_rendering_mode-ashikhmin = Type A - High
 hud-settings-lighting_rendering_mode-blinnphong = Type B - Medium
 hud-settings-lighting_rendering_mode-lambertian = Type L - Cheap
 hud-settings-shadow_rendering_mode = Shadow Rendering Mode
diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 273bf4f4db..933f73c465 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/clouds-vert.glsl b/assets/voxygen/shaders/clouds-vert.glsl
index 933d3a3dc3..727e94b4a8 100644
--- a/assets/voxygen/shaders/clouds-vert.glsl
+++ b/assets/voxygen/shaders/clouds-vert.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl
index 1dbd776170..be30076f8c 100644
--- a/assets/voxygen/shaders/figure-frag.glsl
+++ b/assets/voxygen/shaders/figure-frag.glsl
@@ -10,7 +10,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -135,7 +135,7 @@ void main() {
     // float moon_light = get_moon_brightness(moon_dir);
     /* float sun_shade_frac = horizon_at(f_pos, sun_dir);
     float moon_shade_frac = horizon_at(f_pos, moon_dir); */
-#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
+#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
 #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
     float f_alt = f_pos.z;
@@ -206,7 +206,7 @@ void main() {
     vec3 cam_attenuation = vec3(1);
     float fluid_alt = max(f_pos.z + 1, floor(f_alt + 1));
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
-    #if (FLUID_MODE == FLUID_MODE_SHINY)
+    #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
             medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
             : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl
index 39903c1324..8a598964e2 100644
--- a/assets/voxygen/shaders/fluid-frag/cheap.glsl
+++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -23,6 +23,7 @@
 
 layout(location = 0) in vec3 f_pos;
 layout(location = 1) flat in uint f_pos_norm;
+layout(location = 2) in vec2 f_vel;
 // in vec3 f_col;
 // in float f_light;
 // in vec3 light_pos[2];
@@ -88,7 +89,7 @@ void main() {
 
     /* vec3 sun_dir = get_sun_dir(time_of_day.x);
     vec3 moon_dir = get_moon_dir(time_of_day.x); */
-#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
+#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
 #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
     float f_alt = f_pos.z;
diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 4c4b58a961..5ff512e3e4 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -25,6 +25,7 @@
 
 layout(location = 0) in vec3 f_pos;
 layout(location = 1) flat in uint f_pos_norm;
+layout(location = 2) in vec2 f_vel;
 // in vec3 f_col;
 // in float f_light;
 // in vec3 light_pos[2];
@@ -60,29 +61,66 @@ vec2 wavedx(vec2 position, vec2 direction, float speed, float frequency, float t
 }
 
 // Based on https://www.shadertoy.com/view/MdXyzX
-float wave_height(vec3 pos){
-    pos *= 0.3;
+float wave_height(vec2 pos){
     float iter = 0.0;
-    float phase = 6.0;
-    float speed = 2.0;
-    float weight = 1.0;
+    float phase = 4.0;
+    float weight = 1.5;
     float w = 0.0;
     float ws = 0.0;
-    const float drag_factor = 0.048;
-    for(int i = 0; i < 11; i ++){
+    const float speed_per_iter = 0.1;
+    #if (FLUID_MODE == FLUID_MODE_HIGH)
+        float speed = 1.0;
+        pos *= 0.15;
+        const float drag_factor = 0.03;
+        const float iters = 21;
+    #else
+        float speed = 2.0;
+        pos *= 0.3;
+        const float drag_factor = 0.04;
+        const float iters = 11;
+    #endif
+    const float iter_shift = (3.14159 * 2.0) / 7.3;
+
+    vec2 dir = vec2(
+        sin(pos.y * 0.1),
+        sin(pos.x * 0.1)
+    ) * 0;
+    for(int i = 0; i < iters; i ++) {
         vec2 p = vec2(sin(iter), cos(iter));
-        vec2 res = wavedx(pos.xy, p, speed, phase, tick.x);
-        pos.xy += p * res.y * weight * drag_factor;
-        w += res.x * weight;
-        iter += 10.0;
+        vec2 res = wavedx(pos, p, speed, phase, tick.x);
+        pos += p * res.y * weight * drag_factor;
+        w += res.x * weight * (1.0 + max(dot(p, -dir), 0.0));
+        iter += iter_shift * 1.5;
         ws += weight;
-        weight = mix(weight, 0.0, 0.15);
-        phase *= 1.18;
-        speed *= 1.07;
+        weight = mix(weight, 0.0, 0.2);
+        phase *= 1.23;
+        speed += speed_per_iter;
     }
     return w / ws * 10.0;
 }
 
+float wave_height2(vec2 pos){
+    vec2 vel = vec2(sin(pos.x * 0.2), cos(pos.y * 0.2)) * 2.0;
+    vel = cross(vec3(vel, 0), vec3(0, 0, 1)).xy;
+    vel = lod_norm(f_pos.xy - 16).xy * 10.0;
+    vel = f_vel * 3.5;
+    float hx = mix(
+        wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x) - vec2(0, 1) * tick.x * floor(vel.y)),
+        wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x + 1.0) - vec2(0, 1) * tick.x * floor(vel.y)),
+        fract(vel.x + 1.0)
+    );
+    float hx2 = mix(
+        wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x) - vec2(0, 1) * tick.x * floor(vel.y + 1.0)),
+        wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x + 1.0) - vec2(0, 1) * tick.x * floor(vel.y + 1.0)),
+        fract(vel.x + 1.0)
+    );
+    return mix(
+        hx,
+        hx2,
+        fract(vel.y + 1.0)
+    );
+}
+
 void main() {
     #ifdef EXPERIMENTAL_BAREMINIMUM
         tgt_color = vec4(simple_lighting(f_pos.xyz, MU_SCATTER, 1.0), 0.5);
@@ -127,11 +165,11 @@ void main() {
     }
     vec3 c_norm = cross(f_norm, b_norm);
 
-    vec3 wave_pos = mod(f_pos + focus_off.xyz, vec3(3000.0));
-    float wave_sample_dist = 0.025;
-    float wave00 = wave_height(wave_pos);
-    float wave10 = wave_height(wave_pos + vec3(wave_sample_dist, 0, 0));
-    float wave01 = wave_height(wave_pos + vec3(0, wave_sample_dist, 0));
+    vec3 wave_pos = mod(f_pos + focus_off.xyz, vec3(3000.0)) - (f_pos.z + focus_off.z) * 0.2;
+    float wave_sample_dist = 0.1;
+    float wave00 = wave_height2(wave_pos.xy);
+    float wave10 = wave_height2(wave_pos.xy + vec2(wave_sample_dist, 0));
+    float wave01 = wave_height2(wave_pos.xy + vec2(0, wave_sample_dist));
 
     // Possibility of div by zero when slope = 0,
     // however this only results in no water surface appearing
@@ -175,7 +213,7 @@ void main() {
     //norm = f_norm;
 
     vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;
-#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
+#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
 #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
     float f_alt = f_pos.z;
diff --git a/assets/voxygen/shaders/fluid-vert.glsl b/assets/voxygen/shaders/fluid-vert.glsl
index b078b87407..b9b9a79a21 100644
--- a/assets/voxygen/shaders/fluid-vert.glsl
+++ b/assets/voxygen/shaders/fluid-vert.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -21,6 +21,7 @@
 #include <random.glsl>
 
 layout(location = 0) in uint v_pos_norm;
+layout(location = 1) in uint v_vel;
 // in uint v_col_light;
 
 layout(std140, set = 2, binding = 0)
@@ -42,6 +43,7 @@ uniform u_locals {
 
 layout(location = 0) out vec3 f_pos;
 layout(location = 1) flat out uint f_pos_norm;
+layout(location = 2) out vec2 f_vel;
 // out vec3 f_col;
 // out float f_light;
 // out vec3 light_pos[2];
@@ -50,6 +52,10 @@ const float EXTRA_NEG_Z = 65536.0/*65536.1*/;
 
 void main() {
     f_pos = vec3(v_pos_norm & 0x3Fu, (v_pos_norm >> 6) & 0x3Fu, float((v_pos_norm >> 12) & 0x1FFFFu) - EXTRA_NEG_Z) + model_offs - focus_off.xyz;
+    f_vel = vec2(
+        (float(v_vel & 0xFFFFu) - 32768.0) / 1000.0,
+        (float((v_vel >> 16u) & 0xFFFFu) - 32768.0) / 1000.0
+    );
 
     // f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0));
     // f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0));
@@ -73,7 +79,7 @@ void main() {
     // f_pos.xy += 0.01; // Avoid z-fighting
     // f_pos.x += 0.1 * sin(tick.x / 60 * hash(vec4(f_pos.xyz, 1.0)));
     // f_pos.y += 0.1 * sin(tick.x / 60 * hash(vec4(f_pos.xyz, 2.0)));
-#if (FLUID_MODE == FLUID_MODE_SHINY)
+#if (FLUID_MODE >= FLUID_MODE_MEDIUM)
     // f_pos.z -= 0.1 + 0.1 * (sin(tick.x/* / 60.0*/* 2.0 + f_pos.x * 2.0 + f_pos.y * 2.0) + 1.0) * 0.5;
 #endif
 
diff --git a/assets/voxygen/shaders/include/constants.glsl b/assets/voxygen/shaders/include/constants.glsl
index edd1e27b91..02bd473cb5 100644
--- a/assets/voxygen/shaders/include/constants.glsl
+++ b/assets/voxygen/shaders/include/constants.glsl
@@ -7,7 +7,8 @@
 #define VOXYGEN_COMPUTATION_PREFERENCE_VERTEX 1
 
 #define FLUID_MODE_CHEAP 0
-#define FLUID_MODE_SHINY 1
+#define FLUID_MODE_MEDIUM 1
+#define FLUID_MODE_HIGH 2
 
 #define CLOUD_MODE_NONE 0
 #define CLOUD_MODE_MINIMAL 1
diff --git a/assets/voxygen/shaders/light-shadows-directed-vert.glsl b/assets/voxygen/shaders/light-shadows-directed-vert.glsl
index 1ff6fbffd9..7e2345f649 100644
--- a/assets/voxygen/shaders/light-shadows-directed-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-directed-vert.glsl
@@ -9,7 +9,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/light-shadows-figure-vert.glsl b/assets/voxygen/shaders/light-shadows-figure-vert.glsl
index 1df58ae055..0f44dddfaf 100644
--- a/assets/voxygen/shaders/light-shadows-figure-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-figure-vert.glsl
@@ -11,7 +11,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/light-shadows-frag.glsl b/assets/voxygen/shaders/light-shadows-frag.glsl
index 3671ace978..8adbcae038 100644
--- a/assets/voxygen/shaders/light-shadows-frag.glsl
+++ b/assets/voxygen/shaders/light-shadows-frag.glsl
@@ -13,7 +13,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/light-shadows-geom.glsl b/assets/voxygen/shaders/light-shadows-geom.glsl
index 459232a926..adf22792af 100644
--- a/assets/voxygen/shaders/light-shadows-geom.glsl
+++ b/assets/voxygen/shaders/light-shadows-geom.glsl
@@ -13,7 +13,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/light-shadows-vert.glsl b/assets/voxygen/shaders/light-shadows-vert.glsl
index 4bdbae25f4..14ab18eb1b 100644
--- a/assets/voxygen/shaders/light-shadows-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-vert.glsl
@@ -9,7 +9,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/lod-object-frag.glsl b/assets/voxygen/shaders/lod-object-frag.glsl
index feb33a4362..ddbfcf0c3d 100644
--- a/assets/voxygen/shaders/lod-object-frag.glsl
+++ b/assets/voxygen/shaders/lod-object-frag.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -41,7 +41,7 @@ void main() {
     vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
     vec3 view_dir = -cam_to_frag;
 
-#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
+#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
 #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
     float f_alt = f_pos.z;
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index 2358611b0a..892c57a707 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -445,7 +445,7 @@ void main() {
     vec3 moon_dir = get_moon_dir(time_of_day.x); */
     // voxel_norm = vec3(0.0);
 
-#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
+#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy);//max(alt_at(f_pos.xy), f_pos.z);
     // float shadow_alt = f_pos.z;
 #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
@@ -648,7 +648,7 @@ void main() {
     // vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
     // f_col = f_col + (hash(vec4(floor(vec3(focus_pos.xy + splay(v_pos_orig), f_pos.z)) * 3.0 - round(f_norm) * 0.5, 0)) - 0.5) * 0.05; // Small-scale noise
     vec3 surf_color;
-    #if (FLUID_MODE == FLUID_MODE_SHINY)
+    #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         if (length(f_col_raw - vec3(0.02, 0.06, 0.22)) < 0.025 && dot(vec3(0, 0, 1), f_norm) > 0.9) {
             vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;
 
diff --git a/assets/voxygen/shaders/lod-terrain-vert.glsl b/assets/voxygen/shaders/lod-terrain-vert.glsl
index da9f890bfd..f55bf9a091 100644
--- a/assets/voxygen/shaders/lod-terrain-vert.glsl
+++ b/assets/voxygen/shaders/lod-terrain-vert.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl
index a9259598c8..eaf05fc52b 100644
--- a/assets/voxygen/shaders/particle-frag.glsl
+++ b/assets/voxygen/shaders/particle-frag.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -42,7 +42,7 @@ void main() {
     vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
     vec3 view_dir = -cam_to_frag;
 
-#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
+#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
 #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
     float f_alt = f_pos.z;
@@ -88,7 +88,7 @@ void main() {
     vec3 cam_attenuation = vec3(1);
     float fluid_alt = max(f_pos.z + 1, floor(f_alt + 1));
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
-    #if (FLUID_MODE == FLUID_MODE_SHINY)
+    #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
             medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, MU_WATER, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
             : compute_attenuation_point(f_pos, -view_dir, vec3(0), fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
diff --git a/assets/voxygen/shaders/point-light-shadows-vert.glsl b/assets/voxygen/shaders/point-light-shadows-vert.glsl
index 507c831bc0..3c200474c8 100644
--- a/assets/voxygen/shaders/point-light-shadows-vert.glsl
+++ b/assets/voxygen/shaders/point-light-shadows-vert.glsl
@@ -9,7 +9,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl
index 4276bde368..bdd0dc74cb 100644
--- a/assets/voxygen/shaders/postprocess-frag.glsl
+++ b/assets/voxygen/shaders/postprocess-frag.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/postprocess-vert.glsl b/assets/voxygen/shaders/postprocess-vert.glsl
index 21a6b3d8c2..de7998355b 100644
--- a/assets/voxygen/shaders/postprocess-vert.glsl
+++ b/assets/voxygen/shaders/postprocess-vert.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl b/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl
index c8bfd187e4..605f0a2dff 100644
--- a/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl
+++ b/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl
@@ -9,7 +9,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl b/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl
index b7f940ea2b..f90d444e52 100644
--- a/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl
+++ b/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl
@@ -11,7 +11,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/skybox-frag.glsl b/assets/voxygen/shaders/skybox-frag.glsl
index af28a40aa9..54da8cbc1b 100644
--- a/assets/voxygen/shaders/skybox-frag.glsl
+++ b/assets/voxygen/shaders/skybox-frag.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/skybox-vert.glsl b/assets/voxygen/shaders/skybox-vert.glsl
index 9f362fedb7..d4d34077ea 100644
--- a/assets/voxygen/shaders/skybox-vert.glsl
+++ b/assets/voxygen/shaders/skybox-vert.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/sprite-frag.glsl b/assets/voxygen/shaders/sprite-frag.glsl
index fd3ebdd503..6e276558c6 100644
--- a/assets/voxygen/shaders/sprite-frag.glsl
+++ b/assets/voxygen/shaders/sprite-frag.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -51,7 +51,7 @@ void main() {
     vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
     vec3 view_dir = -cam_to_frag;
 
-#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
+#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
 #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
     float f_alt = f_pos.z;
@@ -94,7 +94,7 @@ void main() {
     vec3 cam_attenuation = vec3(1);
     float fluid_alt = max(f_pos.z + 1, floor(f_alt + 1));
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
-    #if (FLUID_MODE == FLUID_MODE_SHINY)
+    #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
             medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
             : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index 34e38ef55c..4b5497b4ea 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -9,7 +9,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -209,7 +209,7 @@ void main() {
     /* vec3 sun_dir = get_sun_dir(time_of_day.x);
     vec3 moon_dir = get_moon_dir(time_of_day.x); */
 
-#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
+#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
 #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
     float f_alt = f_pos.z;
@@ -396,7 +396,7 @@ void main() {
     reflected_light *= 0.4 + f_ao * 0.6;
 
     #ifndef EXPERIMENTAL_NOCAUSTICS
-        #if (FLUID_MODE == FLUID_MODE_SHINY)
+        #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
             if (faces_fluid) {
                 vec3 wpos = f_pos + vec3(focus_off.xy, 0);
                 vec3 spos = (wpos + (fluid_alt - wpos.z) * vec3(sun_dir.xy, 0)) * 0.25;
diff --git a/assets/voxygen/shaders/terrain-vert.glsl b/assets/voxygen/shaders/terrain-vert.glsl
index d13a240587..166caa87f3 100644
--- a/assets/voxygen/shaders/terrain-vert.glsl
+++ b/assets/voxygen/shaders/terrain-vert.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/assets/voxygen/shaders/trail-frag.glsl b/assets/voxygen/shaders/trail-frag.glsl
index 23d6585f49..f99d4c83a6 100644
--- a/assets/voxygen/shaders/trail-frag.glsl
+++ b/assets/voxygen/shaders/trail-frag.glsl
@@ -8,7 +8,7 @@
 
 #if (FLUID_MODE == FLUID_MODE_CHEAP)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
-#elif (FLUID_MODE == FLUID_MODE_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs
index 81b7ba116d..47de730d48 100644
--- a/voxygen/src/hud/settings_window/video.rs
+++ b/voxygen/src/hud/settings_window/video.rs
@@ -1038,12 +1038,14 @@ impl<'a> Widget for Video<'a> {
         .color(TEXT_COLOR)
         .set(state.ids.fluid_mode_text, ui);
 
-        let mode_list = [FluidMode::Cheap, FluidMode::Shiny];
+        let mode_list = [FluidMode::Cheap, FluidMode::Medium, FluidMode::High];
         let mode_label_list = [
             self.localized_strings
                 .get_msg("hud-settings-fluid_rendering_mode-cheap"),
             self.localized_strings
-                .get_msg("hud-settings-fluid_rendering_mode-shiny"),
+                .get_msg("hud-settings-fluid_rendering_mode-medium"),
+            self.localized_strings
+                .get_msg("hud-settings-fluid_rendering_mode-high"),
         ];
 
         // Get which fluid rendering mode is currently active
diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs
index 13619b51df..6c41e0601a 100644
--- a/voxygen/src/mesh/terrain.rs
+++ b/voxygen/src/mesh/terrain.rs
@@ -9,7 +9,7 @@ use crate::{
     scene::terrain::BlocksOfInterest,
 };
 use common::{
-    terrain::Block,
+    terrain::{Block, TerrainChunk},
     util::either_with,
     vol::{ReadVol, RectRasterableVol},
     volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d},
@@ -226,8 +226,8 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
 }
 
 #[allow(clippy::type_complexity)]
-pub fn generate_mesh<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>(
-    vol: &'a VolGrid2d<V>,
+pub fn generate_mesh<'a>(
+    vol: &'a VolGrid2d<TerrainChunk>,
     (range, max_texture_size, _boi): (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest),
 ) -> MeshGen<
     TerrainVertex,
@@ -390,7 +390,20 @@ pub fn generate_mesh<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + '
     let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32);
     let create_opaque =
         |atlas_pos, pos, norm, meta| TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta);
-    let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm);
+    let create_transparent = |_atlas_pos, pos: Vec3<f32>, norm| {
+        let key = vol.pos_key(pos.map(|e| e.floor() as i32) + range.min);
+        let v00 = vol.get_key(key + Vec2::new(0, 0)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
+        let v10 = vol.get_key(key + Vec2::new(1, 0)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
+        let v01 = vol.get_key(key + Vec2::new(0, 1)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
+        let v11 = vol.get_key(key + Vec2::new(1, 1)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
+        let factor = pos / TerrainChunk::RECT_SIZE.map(|e| e as f32);
+        let vel = Lerp::lerp(
+            Lerp::lerp(v00, v10, factor.x.rem_euclid(1.0)),
+            Lerp::lerp(v01, v11, factor.x.rem_euclid(1.0)),
+            factor.y.rem_euclid(1.0),
+        );
+        FluidVertex::new(pos + mesh_delta, norm, vel.xy())
+    };
 
     let mut greedy =
         GreedyMesh::<guillotiere::SimpleAtlasAllocator>::new(max_size, greedy::general_config());
diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs
index 5ddc5e8974..45d1d04cbd 100644
--- a/voxygen/src/render/mod.rs
+++ b/voxygen/src/render/mod.rs
@@ -155,6 +155,7 @@ pub enum FluidMode {
     /// diffraction, and no light attenuation through water.  As a result,
     /// it can be much cheaper than shiny reflection.
     Cheap,
+    High,
     /// "Shiny" water.  This water implements waves on the surfaces, some
     /// attempt at reflections, and tries to compute accurate light
     /// attenuation through water (this is what results in the
@@ -172,11 +173,11 @@ pub enum FluidMode {
     /// which causes attenuation to be computed incorrectly; this can be
     /// addressed by using shadow maps (at least for terrain).
     #[serde(other)]
-    Shiny,
+    Medium,
 }
 
 impl Default for FluidMode {
-    fn default() -> Self { FluidMode::Shiny }
+    fn default() -> Self { FluidMode::Medium }
 }
 
 /// Lighting modes
diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs
index a5c37fe961..6c89915927 100644
--- a/voxygen/src/render/pipelines/fluid.rs
+++ b/voxygen/src/render/pipelines/fluid.rs
@@ -7,10 +7,11 @@ use vek::*;
 #[derive(Copy, Clone, Debug, Zeroable, Pod)]
 pub struct Vertex {
     pos_norm: u32,
+    vel: u32,
 }
 
 impl Vertex {
-    pub fn new(pos: Vec3<f32>, norm: Vec3<f32>) -> Self {
+    pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, river_velocity: Vec2<f32>) -> Self {
         let (norm_axis, norm_dir) = norm
             .as_slice()
             .iter()
@@ -27,11 +28,17 @@ impl Vertex {
                 | ((pos.y as u32) & 0x003F) << 6
                 | (((pos.z + EXTRA_NEG_Z).max(0.0).min((1 << 17) as f32) as u32) & 0x1FFFF) << 12
                 | (norm_bits & 0x7) << 29,
+            vel: river_velocity
+                .map2(Vec2::new(0, 16), |e, off| (((e * 1000.0 + 32768.9) as u16 as u32) << off))
+                .reduce_bitor(),
         }
     }
 
     fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
-        const ATTRIBUTES: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0 => Uint32];
+        const ATTRIBUTES: [wgpu::VertexAttribute; 2] = wgpu::vertex_attr_array![
+            0 => Uint32,
+            1 => Uint32,
+        ];
         wgpu::VertexBufferLayout {
             array_stride: Self::STRIDE,
             step_mode: wgpu::InputStepMode::Vertex,
diff --git a/voxygen/src/render/renderer/pipeline_creation.rs b/voxygen/src/render/renderer/pipeline_creation.rs
index b7d8a00afe..e36d982b17 100644
--- a/voxygen/src/render/renderer/pipeline_creation.rs
+++ b/voxygen/src/render/renderer/pipeline_creation.rs
@@ -182,7 +182,8 @@ impl ShaderModules {
             "VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT",
             match pipeline_modes.fluid {
                 FluidMode::Cheap => "FLUID_MODE_CHEAP",
-                FluidMode::Shiny => "FLUID_MODE_SHINY",
+                FluidMode::Medium => "FLUID_MODE_MEDIUM",
+                FluidMode::High => "FLUID_MODE_HIGH",
             },
             match pipeline_modes.cloud {
                 CloudMode::None => "CLOUD_MODE_NONE",
@@ -299,7 +300,7 @@ impl ShaderModules {
 
         let selected_fluid_shader = ["fluid-frag.", match pipeline_modes.fluid {
             FluidMode::Cheap => "cheap",
-            FluidMode::Shiny => "shiny",
+            _ => "shiny",
         }]
         .concat();
 
diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs
index c26244ff26..cf23c3bc6a 100644
--- a/voxygen/src/scene/terrain.rs
+++ b/voxygen/src/scene/terrain.rs
@@ -224,12 +224,12 @@ impl assets::Asset for SpriteSpec {
 
 /// skip_remesh is either None (do the full remesh, including recomputing the
 /// light map), or Some((light_map, glow_map)).
-fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + 'static>(
+fn mesh_worker(
     pos: Vec2<i32>,
     z_bounds: (f32, f32),
     skip_remesh: Option<(LightMapFn, LightMapFn)>,
     started_tick: u64,
-    volume: <VolGrid2d<V> as SampleVol<Aabr<i32>>>::Sample,
+    volume: <VolGrid2d<TerrainChunk> as SampleVol<Aabr<i32>>>::Sample,
     max_texture_size: u16,
     chunk: Arc<TerrainChunk>,
     range: Aabb<i32>,
@@ -274,11 +274,11 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
             prof_span!("extract sprite_instances");
             let mut instances = [(); SPRITE_LOD_LEVELS].map(|()| Vec::new());
 
-            for x in 0..V::RECT_SIZE.x as i32 {
-                for y in 0..V::RECT_SIZE.y as i32 {
+            for x in 0..TerrainChunk::RECT_SIZE.x as i32 {
+                for y in 0..TerrainChunk::RECT_SIZE.y as i32 {
                     for z in z_bounds.0 as i32..z_bounds.1 as i32 + 1 {
                         let rel_pos = Vec3::new(x, y, z);
-                        let wpos = Vec3::from(pos * V::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos;
+                        let wpos = Vec3::from(pos * TerrainChunk::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos;
 
                         let block = if let Ok(block) = volume.get(wpos) {
                             block

From db4cc21ec3ea2fe47ec490eaf998303ad8f189a3 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Fri, 23 Sep 2022 16:26:18 +0100
Subject: [PATCH 02/28] Fixed seams

---
 assets/voxygen/shaders/fluid-frag/shiny.glsl | 2 +-
 voxygen/src/mesh/terrain.rs                  | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 5ff512e3e4..7a49a1dcc5 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -103,7 +103,7 @@ float wave_height2(vec2 pos){
     vec2 vel = vec2(sin(pos.x * 0.2), cos(pos.y * 0.2)) * 2.0;
     vel = cross(vec3(vel, 0), vec3(0, 0, 1)).xy;
     vel = lod_norm(f_pos.xy - 16).xy * 10.0;
-    vel = f_vel * 3.5;
+    vel = f_vel * 2.5;
     float hx = mix(
         wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x) - vec2(0, 1) * tick.x * floor(vel.y)),
         wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x + 1.0) - vec2(0, 1) * tick.x * floor(vel.y)),
diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs
index 6c41e0601a..13dc804f79 100644
--- a/voxygen/src/mesh/terrain.rs
+++ b/voxygen/src/mesh/terrain.rs
@@ -391,12 +391,12 @@ pub fn generate_mesh<'a>(
     let create_opaque =
         |atlas_pos, pos, norm, meta| TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta);
     let create_transparent = |_atlas_pos, pos: Vec3<f32>, norm| {
-        let key = vol.pos_key(pos.map(|e| e.floor() as i32) + range.min);
+        let key = vol.pos_key(range.min + pos.as_());
         let v00 = vol.get_key(key + Vec2::new(0, 0)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
         let v10 = vol.get_key(key + Vec2::new(1, 0)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
         let v01 = vol.get_key(key + Vec2::new(0, 1)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
         let v11 = vol.get_key(key + Vec2::new(1, 1)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
-        let factor = pos / TerrainChunk::RECT_SIZE.map(|e| e as f32);
+        let factor = (range.min + pos.as_()).map(|e| e as f32) / TerrainChunk::RECT_SIZE.map(|e| e as f32);
         let vel = Lerp::lerp(
             Lerp::lerp(v00, v10, factor.x.rem_euclid(1.0)),
             Lerp::lerp(v01, v11, factor.x.rem_euclid(1.0)),

From 30de7b4fb8d065d2d6a4de09ce28a3d0ebd06465 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Fri, 23 Sep 2022 16:53:37 +0100
Subject: [PATCH 03/28] Better attenuation

---
 assets/voxygen/shaders/figure-frag.glsl   | 4 ++--
 assets/voxygen/shaders/particle-frag.glsl | 4 ++--
 assets/voxygen/shaders/sprite-frag.glsl   | 4 ++--
 assets/voxygen/shaders/terrain-frag.glsl  | 4 ++--
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl
index be30076f8c..c67bffa436 100644
--- a/assets/voxygen/shaders/figure-frag.glsl
+++ b/assets/voxygen/shaders/figure-frag.glsl
@@ -208,8 +208,8 @@ void main() {
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
     #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
-            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
-            : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
+            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
+            : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
     #endif
 
     // Prevent the sky affecting light when underground
diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl
index eaf05fc52b..158eacf942 100644
--- a/assets/voxygen/shaders/particle-frag.glsl
+++ b/assets/voxygen/shaders/particle-frag.glsl
@@ -90,8 +90,8 @@ void main() {
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
     #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
-            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, MU_WATER, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
-            : compute_attenuation_point(f_pos, -view_dir, vec3(0), fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
+            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, MU_WATER, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
+            : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, vec3(0), fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
     #endif
 
     max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
diff --git a/assets/voxygen/shaders/sprite-frag.glsl b/assets/voxygen/shaders/sprite-frag.glsl
index 6e276558c6..b4d5d93171 100644
--- a/assets/voxygen/shaders/sprite-frag.glsl
+++ b/assets/voxygen/shaders/sprite-frag.glsl
@@ -96,8 +96,8 @@ void main() {
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
     #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
-            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
-            : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
+            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
+            : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
     #endif
 
     // Prevent the sky affecting light when underground
diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index 4b5497b4ea..d08693256d 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -362,8 +362,8 @@ void main() {
     // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water.
     // Computing light attenuation from water.
     vec3 cam_attenuation =
-        medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, MU_WATER, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
-        : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
+        medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, MU_WATER, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
+        : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
 
     // Prevent the sky affecting light when underground
     float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0);

From ce7dda3e2f8e0aa148b574796030a2066818b82f Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 24 Sep 2022 12:33:26 +0100
Subject: [PATCH 04/28] Substantially faster water shaders

---
 assets/voxygen/shaders/fluid-frag/shiny.glsl  | 93 ++++++++++---------
 .../voxygen/shaders/include/cloud/none.glsl   |  6 ++
 assets/voxygen/shaders/include/sky.glsl       | 41 ++++++--
 assets/voxygen/shaders/lod-terrain-frag.glsl  | 10 +-
 4 files changed, 95 insertions(+), 55 deletions(-)

diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 7a49a1dcc5..2d962998c9 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -53,71 +53,69 @@ layout(location = 0) out vec4 tgt_color;
 #include <light.glsl>
 #include <lod.glsl>
 
-vec2 wavedx(vec2 position, vec2 direction, float speed, float frequency, float timeshift) {
-    float x = dot(direction, position) * frequency + timeshift * speed;
-    float wave = pow(sin(x) + 0.5, 2);
-    float dx = wave * cos(x);
-    return vec2(wave, -dx);
+void wave_dx(vec4 posx, vec4 posy, vec2 dir, float speed, float frequency, float timeshift, out vec4 wave, out vec4 dx) {
+    vec4 x = vec4(
+        dot(dir, vec2(posx.x, posy.x)),
+        dot(dir, vec2(posx.y, posy.y)),
+        dot(dir, vec2(posx.z, posy.z)),
+        dot(dir, vec2(posx.w, posy.w))
+    ) * frequency + timeshift * speed;
+    wave = sin(x) + 0.5;
+    wave *= wave;
+    dx = -wave * cos(x);
 }
 
-// Based on https://www.shadertoy.com/view/MdXyzX
-float wave_height(vec2 pos){
+// Based loosely on https://www.shadertoy.com/view/MdXyzX.
+// Modified to allow calculating the wave function 4 times at once using different positions (used for intepolation
+// for moving water). The general idea is to sample the wave function at different positions, where those positions
+// depend on increments of the velocity, and then interpolate between those velocities to get a smooth water velocity.
+vec4 wave_height(vec4 posx, vec4 posy) {
     float iter = 0.0;
     float phase = 4.0;
     float weight = 1.5;
-    float w = 0.0;
+    vec4 w = vec4(0.0);
     float ws = 0.0;
     const float speed_per_iter = 0.1;
     #if (FLUID_MODE == FLUID_MODE_HIGH)
         float speed = 1.0;
-        pos *= 0.15;
+        posx *= 0.2;
+        posy *= 0.2;
         const float drag_factor = 0.03;
         const float iters = 21;
     #else
         float speed = 2.0;
-        pos *= 0.3;
+        posx *= 0.3;
+        posy *= 0.3;
         const float drag_factor = 0.04;
         const float iters = 11;
     #endif
     const float iter_shift = (3.14159 * 2.0) / 7.3;
 
-    vec2 dir = vec2(
-        sin(pos.y * 0.1),
-        sin(pos.x * 0.1)
-    ) * 0;
     for(int i = 0; i < iters; i ++) {
         vec2 p = vec2(sin(iter), cos(iter));
-        vec2 res = wavedx(pos, p, speed, phase, tick.x);
-        pos += p * res.y * weight * drag_factor;
-        w += res.x * weight * (1.0 + max(dot(p, -dir), 0.0));
+        vec4 wave, dx;
+        wave_dx(posx, posy, p, speed, phase, tick.x, wave, dx);
+        posx += p.x * dx * weight * drag_factor;
+        posy += p.y * dx * weight * drag_factor;
+        w += wave * weight;
         iter += iter_shift * 1.5;
         ws += weight;
         weight = mix(weight, 0.0, 0.2);
-        phase *= 1.23;
+        phase *= 1.2;
         speed += speed_per_iter;
     }
-    return w / ws * 10.0;
+    return w / ws * 20.0;
 }
 
 float wave_height2(vec2 pos){
-    vec2 vel = vec2(sin(pos.x * 0.2), cos(pos.y * 0.2)) * 2.0;
-    vel = cross(vec3(vel, 0), vec3(0, 0, 1)).xy;
-    vel = lod_norm(f_pos.xy - 16).xy * 10.0;
-    vel = f_vel * 2.5;
-    float hx = mix(
-        wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x) - vec2(0, 1) * tick.x * floor(vel.y)),
-        wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x + 1.0) - vec2(0, 1) * tick.x * floor(vel.y)),
-        fract(vel.x + 1.0)
-    );
-    float hx2 = mix(
-        wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x) - vec2(0, 1) * tick.x * floor(vel.y + 1.0)),
-        wave_height(pos - vec2(1, 0) * tick.x * floor(vel.x + 1.0) - vec2(0, 1) * tick.x * floor(vel.y + 1.0)),
-        fract(vel.x + 1.0)
+    vec4 heights = wave_height(
+        pos.x - tick.x * floor(f_vel.x) - vec2(0.0, tick.x).xyxy,
+        pos.y - tick.x * floor(f_vel.y) - vec2(0.0, tick.x).xxyy
     );
     return mix(
-        hx,
-        hx2,
-        fract(vel.y + 1.0)
+        mix(heights.x, heights.y, fract(f_vel.x + 1.0)),
+        mix(heights.z, heights.w, fract(f_vel.x + 1.0)),
+        fract(f_vel.y + 1.0)
     );
 }
 
@@ -247,14 +245,6 @@ void main() {
         // TODO: Make this more efficient?
         ray_dir = normalize(max(reflect_ray_dir, vec3(-1.0, -1.0, 0.0)));
     }
-
-    vec3 reflect_color = get_sky_color(/*reflect_ray_dir*/ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, true);
-    reflect_color = get_cloud_color(reflect_color, ray_dir, f_pos.xyz, time_of_day.x, 100000.0, 0.1);
-    reflect_color *= f_light;
-
-    // Prevent the sky affecting light when underground
-    float not_underground = clamp((f_pos.z - f_alt) / 32.0 + 1.0, 0.0, 1.0);
-    reflect_color *= not_underground;
     // /*const */vec3 water_color = srgb_to_linear(vec3(0.2, 0.5, 1.0));
     // /*const */vec3 water_color = srgb_to_linear(vec3(0.8, 0.9, 1.0));
     // NOTE: Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths.
@@ -275,6 +265,21 @@ void main() {
     // float moon_shade_frac = horizon_at(/*f_shadow, f_pos.z, */f_pos, moon_dir);
     // float shade_frac = /*1.0;*/sun_shade_frac + moon_shade_frac;
 
+    vec3 reflect_color;
+    #if (FLUID_MODE == FLUID_MODE_HIGH)
+        reflect_color = get_sky_color(ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, sun_shade_frac > 0.5);
+        reflect_color = get_cloud_color(reflect_color, ray_dir, f_pos.xyz, time_of_day.x, 100000.0, 0.1);
+    #else
+        reflect_color = get_sky_color(ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, sun_shade_frac > 0.5, 1.0, true);
+    #endif
+    // Sort of non-physical, but we try to balance the reflection intensity with the direct light from the sun,
+    // resulting in decent reflection of the ambient environment even after the sun has gone down.
+    reflect_color *= f_light * (sun_shade_frac * 0.75 + 0.25);
+
+    // Prevent the sky affecting light when underground
+    float not_underground = clamp((f_pos.z - f_alt) / 32.0 + 1.0, 0.0, 1.0);
+    reflect_color *= not_underground;
+
     // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos);
     float point_shadow = shadow_at(f_pos, f_norm);
     DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos);
@@ -370,7 +375,7 @@ void main() {
     // diffuse_light += point_light;
     // reflected_light += point_light;
     // vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)) * light * diffuse_light * ambient_light;
-    const float REFLECTANCE = 0.5;
+    const float REFLECTANCE = 1.0;
     vec3 surf_color = illuminate(max_light, view_dir, water_color * emitted_light/* * log(1.0 - MU_WATER)*/, /*cam_attenuation * *//*water_color * */reflect_color * REFLECTANCE + water_color * reflected_light/* * log(1.0 - MU_WATER)*/);
 
     // passthrough = pow(passthrough, 1.0 / (1.0 + water_depth_to_camera));
diff --git a/assets/voxygen/shaders/include/cloud/none.glsl b/assets/voxygen/shaders/include/cloud/none.glsl
index 5d52deb96d..97a6e610e5 100644
--- a/assets/voxygen/shaders/include/cloud/none.glsl
+++ b/assets/voxygen/shaders/include/cloud/none.glsl
@@ -1,8 +1,14 @@
 #include <lod.glsl>
+#include <sky.glsl>
 
 vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, float time_of_day, float max_dist, float quality) {
     // Underwater light attenuation
     surf_color = water_diffuse(surf_color, dir, max_dist);
 
+    if (max_dist < DIST_CAP) {
+        vec3 sky_light = get_sky_light(dir, time_of_day, false);
+        surf_color = mix(sky_light, surf_color, 1.0 / exp(max_dist / 5000.0));
+    }
+
     return surf_color;
 }
diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl
index 15efe21f86..e0d494578f 100644
--- a/assets/voxygen/shaders/include/sky.glsl
+++ b/assets/voxygen/shaders/include/sky.glsl
@@ -26,7 +26,7 @@ const vec3 DAWN_LIGHT   = vec3(5.0, 2.0, 1.15);
 const vec3 SUN_HALO_DAWN = vec3(8.2, 3.0, 2.1);
 
 const vec3 SKY_DAY_TOP = vec3(0.1, 0.5, 0.9);
-const vec3 SKY_DAY_MID = vec3(0.02, 0.28, 0.8);
+const vec3 SKY_DAY_MID = vec3(0.18, 0.28, 0.6);
 const vec3 SKY_DAY_BOT = vec3(0.1, 0.2, 0.3);
 const vec3 DAY_LIGHT   = vec3(3.8, 3.0, 1.8);
 const vec3 SUN_HALO_DAY = vec3(0.25, 0.25, 0.001);
@@ -570,7 +570,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) {
     return sky_color * magnetosphere_tint;
 }
 
-vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_features, float refractionIndex) {
+vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_features, float refractionIndex, bool fake_clouds) {
     // Sky color
     /* vec3 sun_dir = get_sun_dir(time_of_day);
     vec3 moon_dir = get_moon_dir(time_of_day); */
@@ -591,9 +591,13 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
 
     float sun_halo_power = 20.0;
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
-        sun_halo_power = 1000.0;
-        sun_halo_color *= 0.1;
+        if (true) {
+    #else
+        if (fake_clouds || medium.x == MEDIUM_WATER) {
     #endif
+        sun_halo_power = 50.0;
+        sun_halo_color *= 0.025;
+    }
 
     vec3 sun_halo = sun_halo_color * 25 * pow(max(dot(dir, -sun_dir), 0), sun_halo_power);
     vec3 sun_surf = vec3(0);
@@ -601,6 +605,13 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
         float angle = 0.00035;
         sun_surf = clamp((dot(dir, -sun_dir) - (1.0 - angle)) * 4 / angle, 0, 1) * SUN_SURF_COLOR * SUN_COLOR_FACTOR;
     }
+    #if (CLOUD_MODE == CLOUD_MODE_NONE)
+        if (true) {
+    #else
+        if (fake_clouds || medium.x == MEDIUM_WATER) {
+    #endif
+        sun_surf *= 0.1;
+    }
     vec3 sun_light = sun_halo + sun_surf;
 
     // Moon
@@ -611,9 +622,13 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
 
     float moon_halo_power = 20.0;
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
+        if (true) {
+    #else
+        if (fake_clouds || medium.x == MEDIUM_WATER) {
+    #endif
         moon_halo_power = 2500.0;
         moon_halo_color *= 0.1;
-    #endif
+    }
 
     vec3 moon_halo = moon_halo_color * pow(max(dot(dir, -moon_dir), 0), moon_halo_power);
     vec3 moon_surf = vec3(0);
@@ -625,10 +640,14 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
 
     // Replaced all clamp(sun_dir, 0, 1) with max(sun_dir, 0) because sun_dir is calculated from sin and cos, which are never > 1
 
+    vec3 sky_color;
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
-        vec3 sky_color = get_sky_light(dir, time_of_day, true);
+        if (true) {
     #else
-        vec3 sky_color;
+        if (fake_clouds || medium.x == MEDIUM_WATER) {
+    #endif
+        sky_color = get_sky_light(dir, time_of_day, false);
+    } else {
         if (medium.x == MEDIUM_WATER) {
             sky_color = get_sky_light(dir, time_of_day, true);
         } else {
@@ -636,13 +655,17 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
             float star = is_star_at(star_dir);
             sky_color = vec3(0) + star;
         }
-    #endif
+    }
 
     return sky_color + sun_light + moon_light;
 }
 
+vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_features, float refractionIndex) {
+    return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_features, refractionIndex, false);
+}
+
 vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_stars) {
-    return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_stars, 1.0);
+    return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_stars, 1.0, false);
 }
 
 float fog(vec3 f_pos, vec3 focus_pos, uint medium) {
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index 892c57a707..a020a563d9 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -656,8 +656,14 @@ void main() {
 
             float passthrough = dot(faceforward(f_norm, f_norm, cam_to_frag), -cam_to_frag);
 
-            vec3 reflect_color = get_sky_color(reflect_ray, time_of_day.x, f_pos, vec3(-100000), 0.125, true);
-            reflect_color = get_cloud_color(reflect_color, reflect_ray, cam_pos.xyz, time_of_day.x, 100000.0, 0.1);
+            vec3 reflect_color;
+            #if (FLUID_MODE == FLUID_MODE_HIGH)
+                reflect_color = get_sky_color(reflect_ray, time_of_day.x, f_pos, vec3(-100000), 0.125, sun_shade_frac > 0.5);
+                reflect_color = get_cloud_color(reflect_color, reflect_ray, cam_pos.xyz, time_of_day.x, 100000.0, 0.1);
+            #else
+                reflect_color = get_sky_color(reflect_ray, time_of_day.x, f_pos, vec3(-100000), 0.125, sun_shade_frac > 0.5, 1.0, true);
+            #endif
+            reflect_color *= sun_shade_frac * 0.75 + 0.25;
 
             const float REFLECTANCE = 0.5;
             surf_color = illuminate(max_light, view_dir, f_col * emitted_light, reflect_color * REFLECTANCE + water_color * reflected_light);

From b45586902a5ef5b3bb2c6d4c3d89c3179fce8c61 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 24 Sep 2022 12:47:05 +0100
Subject: [PATCH 05/28] Made cheap water display velocity

---
 assets/voxygen/shaders/fluid-frag/cheap.glsl | 27 +++++++++++++++++++-
 assets/voxygen/shaders/fluid-frag/shiny.glsl |  8 +++---
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl
index 8a598964e2..566538c6d9 100644
--- a/assets/voxygen/shaders/fluid-frag/cheap.glsl
+++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl
@@ -51,6 +51,29 @@ layout(location = 0) out vec4 tgt_color;
 #include <light.glsl>
 #include <lod.glsl>
 
+vec4 water_col(vec4 posx, vec4 posy) {
+    posx = (posx + focus_off.x) * 0.1;
+    posy = (posy + focus_off.y) * 0.1;
+    return 0.5 + (vec4(
+        textureLod(sampler2D(t_noise, s_noise), vec2(posx.x, posy.x), 0).x,
+        textureLod(sampler2D(t_noise, s_noise), vec2(posx.y, posy.y), 0).x,
+        textureLod(sampler2D(t_noise, s_noise), vec2(posx.z, posy.z), 0).x,
+        textureLod(sampler2D(t_noise, s_noise), vec2(posx.w, posy.w), 0).x
+    ) - 0.5) * 0.5;
+}
+
+float water_col_vel(vec2 pos){
+    vec4 cols = water_col(
+        pos.x - tick.x * floor(f_vel.x) - vec2(0.0, tick.x).xyxy,
+        pos.y - tick.x * floor(f_vel.y) - vec2(0.0, tick.x).xxyy
+    );
+    return mix(
+        mix(cols.x, cols.y, fract(f_vel.x + 1.0)),
+        mix(cols.z, cols.w, fract(f_vel.x + 1.0)),
+        fract(f_vel.y + 1.0)
+    );
+}
+
 void main() {
     #ifdef EXPERIMENTAL_BAREMINIMUM
         tgt_color = vec4(simple_lighting(f_pos.xyz, MU_SCATTER, 1.0), 0.5);
@@ -87,6 +110,8 @@ void main() {
     /*const */vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;//srgb_to_linear(vec3(0.2, 0.5, 1.0));
     // /*const */vec3 water_color = srgb_to_linear(vec3(0.0, 0.25, 0.5));
 
+    water_color *= water_col_vel(f_pos.xy);
+
     /* vec3 sun_dir = get_sun_dir(time_of_day.x);
     vec3 moon_dir = get_moon_dir(time_of_day.x); */
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
@@ -192,7 +217,7 @@ void main() {
     // float reflected_light_point = /*length*/(diffuse_light_point.r) + f_light * point_shadow;
     // reflected_light += k_d * (diffuse_light_point + f_light * point_shadow * shade_frac) + specular_light_point;
 
-    float passthrough = max(dot(f_norm, -cam_to_frag), 0) * 0.75;
+    float passthrough = max(dot(f_norm, -cam_to_frag), 0) * 0.25;
     float min_refl = 0.0;
     if (medium.x != MEDIUM_WATER) {
         min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 2d962998c9..0c745d8fb7 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -107,7 +107,7 @@ vec4 wave_height(vec4 posx, vec4 posy) {
     return w / ws * 20.0;
 }
 
-float wave_height2(vec2 pos){
+float wave_height_vel(vec2 pos){
     vec4 heights = wave_height(
         pos.x - tick.x * floor(f_vel.x) - vec2(0.0, tick.x).xyxy,
         pos.y - tick.x * floor(f_vel.y) - vec2(0.0, tick.x).xxyy
@@ -165,9 +165,9 @@ void main() {
 
     vec3 wave_pos = mod(f_pos + focus_off.xyz, vec3(3000.0)) - (f_pos.z + focus_off.z) * 0.2;
     float wave_sample_dist = 0.1;
-    float wave00 = wave_height2(wave_pos.xy);
-    float wave10 = wave_height2(wave_pos.xy + vec2(wave_sample_dist, 0));
-    float wave01 = wave_height2(wave_pos.xy + vec2(0, wave_sample_dist));
+    float wave00 = wave_height_vel(wave_pos.xy);
+    float wave10 = wave_height_vel(wave_pos.xy + vec2(wave_sample_dist, 0));
+    float wave01 = wave_height_vel(wave_pos.xy + vec2(0, wave_sample_dist));
 
     // Possibility of div by zero when slope = 0,
     // however this only results in no water surface appearing

From 81ac4f9125194d7beb7c6963efbcde118a13b34a Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 24 Sep 2022 14:08:32 +0100
Subject: [PATCH 06/28] Various water-related lighting fixes

---
 assets/voxygen/shaders/fluid-frag/cheap.glsl | 14 ++++++-------
 assets/voxygen/shaders/fluid-frag/shiny.glsl |  9 +++++----
 assets/voxygen/shaders/fluid-vert.glsl       |  2 +-
 assets/voxygen/shaders/include/sky.glsl      | 21 +++++++++++---------
 assets/voxygen/shaders/include/srgb.glsl     |  2 +-
 assets/voxygen/shaders/lod-terrain-frag.glsl |  6 +++---
 assets/voxygen/shaders/terrain-frag.glsl     |  6 +++++-
 7 files changed, 33 insertions(+), 27 deletions(-)

diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl
index 566538c6d9..8ae661a7b0 100644
--- a/assets/voxygen/shaders/fluid-frag/cheap.glsl
+++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl
@@ -59,7 +59,7 @@ vec4 water_col(vec4 posx, vec4 posy) {
         textureLod(sampler2D(t_noise, s_noise), vec2(posx.y, posy.y), 0).x,
         textureLod(sampler2D(t_noise, s_noise), vec2(posx.z, posy.z), 0).x,
         textureLod(sampler2D(t_noise, s_noise), vec2(posx.w, posy.w), 0).x
-    ) - 0.5) * 0.5;
+    ) - 0.5) * 1.3;
 }
 
 float water_col_vel(vec2 pos){
@@ -107,10 +107,8 @@ void main() {
     // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/);
     vec3 view_dir = -cam_to_frag;
     // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0));
-    /*const */vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;//srgb_to_linear(vec3(0.2, 0.5, 1.0));
-    // /*const */vec3 water_color = srgb_to_linear(vec3(0.0, 0.25, 0.5));
 
-    water_color *= water_col_vel(f_pos.xy);
+    vec3 water_color = (1.0 - mix(MU_WATER, vec3(0.8, 0.24, 0.08), water_col_vel(f_pos.xy))) * MU_SCATTER;
 
     /* vec3 sun_dir = get_sun_dir(time_of_day.x);
     vec3 moon_dir = get_moon_dir(time_of_day.x); */
@@ -217,11 +215,11 @@ void main() {
     // float reflected_light_point = /*length*/(diffuse_light_point.r) + f_light * point_shadow;
     // reflected_light += k_d * (diffuse_light_point + f_light * point_shadow * shade_frac) + specular_light_point;
 
-    float passthrough = max(dot(f_norm, -cam_to_frag), 0) * 0.25;
+    float passthrough = max(dot(f_norm, -cam_to_frag), 0) * 0.65;
     float min_refl = 0.0;
-    if (medium.x != MEDIUM_WATER) {
-        min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
-    }
+    /* if (medium.x != MEDIUM_WATER) { */
+    /*     min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b)); */
+    /* } */
 
     vec3 surf_color = illuminate(max_light, view_dir, water_color * /* fog_color * */emitted_light, /*surf_color * */water_color * reflected_light);
     // vec4 color = vec4(surf_color, passthrough * 1.0 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam)));
diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 0c745d8fb7..5785d31841 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -104,7 +104,7 @@ vec4 wave_height(vec4 posx, vec4 posy) {
         phase *= 1.2;
         speed += speed_per_iter;
     }
-    return w / ws * 20.0;
+    return w / ws * 5.0;
 }
 
 float wave_height_vel(vec2 pos){
@@ -267,10 +267,10 @@ void main() {
 
     vec3 reflect_color;
     #if (FLUID_MODE == FLUID_MODE_HIGH)
-        reflect_color = get_sky_color(ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, sun_shade_frac > 0.5);
+        reflect_color = get_sky_color(ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, true, 1.0, true, sun_shade_frac);
         reflect_color = get_cloud_color(reflect_color, ray_dir, f_pos.xyz, time_of_day.x, 100000.0, 0.1);
     #else
-        reflect_color = get_sky_color(ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, sun_shade_frac > 0.5, 1.0, true);
+        reflect_color = get_sky_color(ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, true, 1.0, true, sun_shade_frac);
     #endif
     // Sort of non-physical, but we try to balance the reflection intensity with the direct light from the sun,
     // resulting in decent reflection of the ambient environment even after the sun has gone down.
@@ -314,6 +314,7 @@ void main() {
     vec3 mu = MU_WATER;
     // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water.
     vec3 cam_attenuation = compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, cam_pos.xyz);
+    //reflect_color *= cam_attenuation;
     // float water_depth_to_vertical = max(/*f_alt - f_pos.z*/f_light, 0.0);
     // For ambient color, we just take the distance to the surface out of laziness.
     // See https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law.
@@ -334,7 +335,7 @@ void main() {
     float passthrough = max(dot(norm, -cam_to_frag), 0) * 0.75;
 
     float max_light = 0.0;
-    max_light += get_sun_diffuse2(sun_info, moon_info, norm, /*time_of_day.x*/sun_view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, vec3(k_d), /*vec3(f_light * point_shadow)*//*reflect_color*/k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
+    max_light += get_sun_diffuse2(sun_info, moon_info, cam_norm, /*time_of_day.x*/sun_view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, vec3(k_d), /*vec3(f_light * point_shadow)*//*reflect_color*/k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
     emitted_light *= not_underground;
     reflected_light *= not_underground;
 
diff --git a/assets/voxygen/shaders/fluid-vert.glsl b/assets/voxygen/shaders/fluid-vert.glsl
index b9b9a79a21..b2878a3c46 100644
--- a/assets/voxygen/shaders/fluid-vert.glsl
+++ b/assets/voxygen/shaders/fluid-vert.glsl
@@ -69,7 +69,7 @@ void main() {
     #endif
 
     float pull_down = pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0) * 0.7;
-    f_pos.z -= pull_down;
+    //f_pos.z -= pull_down;
 
     #ifdef EXPERIMENTAL_CURVEDWORLD
         f_pos.z -= pow(distance(f_pos.xy + focus_off.xy, focus_pos.xy + focus_off.xy) * 0.05, 2);
diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl
index e0d494578f..810282037f 100644
--- a/assets/voxygen/shaders/include/sky.glsl
+++ b/assets/voxygen/shaders/include/sky.glsl
@@ -570,7 +570,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) {
     return sky_color * magnetosphere_tint;
 }
 
-vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_features, float refractionIndex, bool fake_clouds) {
+vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_features, float refractionIndex, bool fake_clouds, float sun_shade_frac) {
     // Sky color
     /* vec3 sun_dir = get_sun_dir(time_of_day);
     vec3 moon_dir = get_moon_dir(time_of_day); */
@@ -595,15 +595,18 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
     #else
         if (fake_clouds || medium.x == MEDIUM_WATER) {
     #endif
-        sun_halo_power = 50.0;
-        sun_halo_color *= 0.025;
+        sun_halo_power = 30.0;
+        sun_halo_color *= 0.01;
     }
 
     vec3 sun_halo = sun_halo_color * 25 * pow(max(dot(dir, -sun_dir), 0), sun_halo_power);
     vec3 sun_surf = vec3(0);
     if (with_features) {
         float angle = 0.00035;
-        sun_surf = clamp((dot(dir, -sun_dir) - (1.0 - angle)) * 4 / angle, 0, 1) * SUN_SURF_COLOR * SUN_COLOR_FACTOR;
+        sun_surf = clamp((dot(dir, -sun_dir) - (1.0 - angle)) * 4 / angle, 0, 1)
+            * SUN_SURF_COLOR
+            * SUN_COLOR_FACTOR
+            * sun_shade_frac;
     }
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
         if (true) {
@@ -626,8 +629,8 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
     #else
         if (fake_clouds || medium.x == MEDIUM_WATER) {
     #endif
-        moon_halo_power = 2500.0;
-        moon_halo_color *= 0.1;
+        moon_halo_power = 50.0;
+        moon_halo_color *= 0.02;
     }
 
     vec3 moon_halo = moon_halo_color * pow(max(dot(dir, -moon_dir), 0), moon_halo_power);
@@ -646,7 +649,7 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
     #else
         if (fake_clouds || medium.x == MEDIUM_WATER) {
     #endif
-        sky_color = get_sky_light(dir, time_of_day, false);
+        sky_color = get_sky_light(dir, time_of_day, !fake_clouds);
     } else {
         if (medium.x == MEDIUM_WATER) {
             sky_color = get_sky_light(dir, time_of_day, true);
@@ -661,11 +664,11 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
 }
 
 vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_features, float refractionIndex) {
-    return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_features, refractionIndex, false);
+    return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_features, refractionIndex, false, 1.0);
 }
 
 vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float quality, bool with_stars) {
-    return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_stars, 1.0, false);
+    return get_sky_color(dir, time_of_day, origin, f_pos, quality, with_stars, 1.0, false, 1.0);
 }
 
 float fog(vec3 f_pos, vec3 focus_pos, uint medium) {
diff --git a/assets/voxygen/shaders/include/srgb.glsl b/assets/voxygen/shaders/include/srgb.glsl
index a04edaf138..c7047404ad 100644
--- a/assets/voxygen/shaders/include/srgb.glsl
+++ b/assets/voxygen/shaders/include/srgb.glsl
@@ -536,7 +536,7 @@ vec3 compute_attenuation(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_alt, ve
 // from the default point.
 vec3 compute_attenuation_point(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_alt, vec3 defaultpos) {
 #if (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_IMPORTANCE)
-    return vec3(1.0);
+    return pow(1.0 - mu, vec3(3));
 #elif (LIGHTING_TRANSPORT_MODE == LIGHTING_TRANSPORT_MODE_RADIANCE)
     // return vec3(1.0);
     /*if (mu == vec3(0.0)) {
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index a020a563d9..5c1bb0fc60 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -658,14 +658,14 @@ void main() {
 
             vec3 reflect_color;
             #if (FLUID_MODE == FLUID_MODE_HIGH)
-                reflect_color = get_sky_color(reflect_ray, time_of_day.x, f_pos, vec3(-100000), 0.125, sun_shade_frac > 0.5);
+                reflect_color = get_sky_color(reflect_ray, time_of_day.x, f_pos, vec3(-100000), 0.125, true, 1.0, true, sun_shade_frac);
                 reflect_color = get_cloud_color(reflect_color, reflect_ray, cam_pos.xyz, time_of_day.x, 100000.0, 0.1);
             #else
-                reflect_color = get_sky_color(reflect_ray, time_of_day.x, f_pos, vec3(-100000), 0.125, sun_shade_frac > 0.5, 1.0, true);
+                reflect_color = get_sky_color(reflect_ray, time_of_day.x, f_pos, vec3(-100000), 0.125, true, 1.0, true, sun_shade_frac);
             #endif
             reflect_color *= sun_shade_frac * 0.75 + 0.25;
 
-            const float REFLECTANCE = 0.5;
+            const float REFLECTANCE = 1.0;
             surf_color = illuminate(max_light, view_dir, f_col * emitted_light, reflect_color * REFLECTANCE + water_color * reflected_light);
 
             const vec3 underwater_col = vec3(0.0);
diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index d08693256d..1a771dbaf0 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -369,7 +369,11 @@ void main() {
     float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0);
 
     // To account for prior saturation
-    /*float */f_light = faces_fluid ? not_underground : f_light * sqrt(f_light);
+    #if (FLUID_MODE == FLUID_MODE_CHEAP)
+        f_light = f_light * sqrt(f_light);
+    #else
+        f_light = faces_fluid ? not_underground : f_light * sqrt(f_light);
+    #endif
 
     vec3 emitted_light = vec3(1.0);
     vec3 reflected_light = vec3(1.0);

From f00cc5f2def764abc23fb897b065eb129b3670a9 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Tue, 4 Oct 2022 18:11:38 +0100
Subject: [PATCH 07/28] The worst SSR

---
 assets/voxygen/shaders/clouds-frag.glsl | 45 +++++++++++++++++++++++++
 voxygen/src/render/pipelines/fluid.rs   |  2 +-
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 933f73c465..c9bab30d1d 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -27,6 +27,7 @@
 #include <light.glsl>
 // This *MUST* come after `cloud.glsl`: it contains a function that depends on `cloud.glsl` when clouds are enabled
 #include <point_glow.glsl>
+#include <random.glsl>
 
 layout(set = 2, binding = 0)
 uniform texture2D t_src_color;
@@ -60,6 +61,16 @@ vec3 wpos_at(vec2 uv) {
     }
 }
 
+vec3 dir_at(vec2 uv) {
+    vec4 view_space = all_mat_inv * vec4((uv * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);
+    view_space /= view_space.w;
+    return normalize(view_space.xyz);
+}
+
+float invlerp(float a, float b, float v) {
+    return (v - a) / (b - a);
+}
+
 void main() {
     vec4 color = texture(sampler2D(t_src_color, s_src_color), uv);
 
@@ -77,6 +88,40 @@ void main() {
     if (color.a < 1.0) {
         cloud_blend = 1.0 - color.a;
         dist = DIST_CAP;
+        //color.rgb = vec3(1, 0, 0);
+
+        vec2 uv_refl = uv;
+        vec3 wpos_refl = wpos;
+        /* for (int i = 0; i < 10; i ++) { */
+        /*     uv_refl.y -= 0.1; */
+        /*     wpos_refl += reflect(dir, vec3(0, 0, 1)) * dist * 0.5; */
+        /*     if (distance(wpos_at(uv_refl), cam_pos.xyz) < distance(wpos_refl, cam_pos.xyz)) { */
+        /*         color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), uv_refl).rgb, 0.9); */
+        /*         break; */
+        /*     } */
+        /* } */
+
+        if (dir.z < 0.0) {
+            vec3 dir_mid = dir_at(vec2(0, 0));//normalize((all_mat_inv * vec4(0, 0, 0, 0)).xyz);
+            vec3 dir_right = dir_at(vec2(1, 0));//normalize((all_mat_inv * vec4(1, 0, 0, 0)).xyz);
+            vec3 dir_up = dir_at(vec2(0, 1));//normalize((all_mat_inv * vec4(0, -1, 0, 0)).xyz);
+            vec3 surf_norm = normalize(vec3((vec2(noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.3)).x, noise_3d(vec3((wpos.xy + focus_off.xy).yx * 0.1, tick.x * 0.3)).x) - 0.5) * 0.03, 1));
+            vec3 refl_dir = reflect(dir, surf_norm);
+
+            float right = invlerp(dir_mid.x, dir_right.x, refl_dir.x);
+            float up = invlerp(dir_mid.z, dir_up.z, refl_dir.z);
+
+            vec2 new_uv = vec2(uv.x, up);
+
+            float merge = clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.5, 0, 0.75);
+
+            //vec2 new_uv = uv * vec2(1, -1) + vec2(0, 1.1) / (1.0 + dist * 0.000001) + vec2(0, dir.z);
+            color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
+            wpos = wpos_at(new_uv);
+            dist = distance(wpos, cam_pos.xyz);
+            dir = (wpos - cam_pos.xyz) / dist;
+            cloud_blend = min(merge * 2.0, 1.0);
+        }
     }
     color.rgb = mix(color.rgb, get_cloud_color(color.rgb, dir, cam_pos.xyz, time_of_day.x, dist, 1.0), cloud_blend);
 
diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs
index 6c89915927..a7fcccaf58 100644
--- a/voxygen/src/render/pipelines/fluid.rs
+++ b/voxygen/src/render/pipelines/fluid.rs
@@ -131,7 +131,7 @@ impl FluidPipeline {
                         alpha: wgpu::BlendComponent {
                             src_factor: wgpu::BlendFactor::One,
                             dst_factor: wgpu::BlendFactor::One,
-                            operation: wgpu::BlendOperation::Add,
+                            operation: wgpu::BlendOperation::Min,
                         },
                     }),
                     write_mask: wgpu::ColorWrite::ALL,

From daef86d813d4e9531cc9ba7b8db435be83f48da0 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sun, 9 Oct 2022 22:44:26 +0100
Subject: [PATCH 08/28] Make screen-space reflections an experimental shader

---
 assets/voxygen/shaders/clouds-frag.glsl | 44 +++++++++++++++----------
 voxygen/src/render/mod.rs               |  2 ++
 2 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index c9bab30d1d..415964cf4f 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -87,7 +87,6 @@ void main() {
     float cloud_blend = 1.0;
     if (color.a < 1.0) {
         cloud_blend = 1.0 - color.a;
-        dist = DIST_CAP;
         //color.rgb = vec3(1, 0, 0);
 
         vec2 uv_refl = uv;
@@ -101,26 +100,37 @@ void main() {
         /*     } */
         /* } */
 
-        if (dir.z < 0.0) {
-            vec3 dir_mid = dir_at(vec2(0, 0));//normalize((all_mat_inv * vec4(0, 0, 0, 0)).xyz);
-            vec3 dir_right = dir_at(vec2(1, 0));//normalize((all_mat_inv * vec4(1, 0, 0, 0)).xyz);
-            vec3 dir_up = dir_at(vec2(0, 1));//normalize((all_mat_inv * vec4(0, -1, 0, 0)).xyz);
-            vec3 surf_norm = normalize(vec3((vec2(noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.3)).x, noise_3d(vec3((wpos.xy + focus_off.xy).yx * 0.1, tick.x * 0.3)).x) - 0.5) * 0.03, 1));
-            vec3 refl_dir = reflect(dir, surf_norm);
+        #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONS
+            if (dir.z < 0.0) {
+                vec3 dir_mid = dir_at(vec2(0, 0));
+                vec3 dir_right = dir_at(vec2(1, 0));
+                vec3 dir_up = dir_at(vec2(0, 1));
+                vec3 surf_norm = normalize(vec3((vec2(noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.3)).x, noise_3d(vec3((wpos.xy + focus_off.xy).yx * 0.1, tick.x * 0.3)).x) - 0.5) * 0.03, 1));
+                vec3 refl_dir = reflect(dir, surf_norm);
 
-            float right = invlerp(dir_mid.x, dir_right.x, refl_dir.x);
-            float up = invlerp(dir_mid.z, dir_up.z, refl_dir.z);
+                //float right = invlerp(atan2(dir_mid.x, dir_mid.y), atan2(dir_right.x, dir_right.y), atan2(refl_dir.x, refl_dir.y));
+                float up = invlerp(dir_mid.z, dir_up.z, refl_dir.z);
 
-            vec2 new_uv = vec2(uv.x, up);
+                vec2 new_uv = vec2(uv.x, up);
 
-            float merge = clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.5, 0, 0.75);
+                float new_dist = distance(wpos_at(new_uv), cam_pos.xyz);
+                if (new_dist > dist) {
+                    float merge = clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.5, 0, 0.75);
 
-            //vec2 new_uv = uv * vec2(1, -1) + vec2(0, 1.1) / (1.0 + dist * 0.000001) + vec2(0, dir.z);
-            color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
-            wpos = wpos_at(new_uv);
-            dist = distance(wpos, cam_pos.xyz);
-            dir = (wpos - cam_pos.xyz) / dist;
-            cloud_blend = min(merge * 2.0, 1.0);
+                    //vec2 new_uv = uv * vec2(1, -1) + vec2(0, 1.1) / (1.0 + dist * 0.000001) + vec2(0, dir.z);
+                    color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
+                    wpos = wpos_at(new_uv);
+                    dist = distance(wpos, cam_pos.xyz);
+                    dir = (wpos - cam_pos.xyz) / dist;
+                    cloud_blend = min(merge * 2.0, 1.0);
+                } else {
+                    cloud_blend = 1.0;
+                }
+            } else {
+        #else
+            {
+        #endif
+            dist = DIST_CAP;
         }
     }
     color.rgb = mix(color.rgb, get_cloud_color(color.rgb, dir, cam_pos.xyz, time_of_day.x, dist, 1.0), cloud_blend);
diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs
index 45d1d04cbd..e938fccf4e 100644
--- a/voxygen/src/render/mod.rs
+++ b/voxygen/src/render/mod.rs
@@ -500,4 +500,6 @@ pub enum ExperimentalShader {
     NoRainbows,
     /// Make objects appear wet when appropriate.
     Wetness,
+    /// Add screen-space reflections to water.
+    ScreenSpaceReflections,
 }

From 387d7e65d2d72fb38e077662f0b003e15c1d263a Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 00:30:20 +0100
Subject: [PATCH 09/28] Improved SSR quality

---
 assets/voxygen/shaders/clouds-frag.glsl      | 29 +++++++++++++++-----
 assets/voxygen/shaders/fluid-frag/shiny.glsl |  2 +-
 assets/voxygen/shaders/lod-terrain-frag.glsl |  4 ++-
 3 files changed, 26 insertions(+), 9 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 415964cf4f..43d046aef9 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -49,7 +49,9 @@ uniform u_locals {
 layout(location = 0) out vec4 tgt_color;
 
 vec3 wpos_at(vec2 uv) {
-    float buf_depth = texture(sampler2D(t_src_depth, s_src_depth), uv).x;
+    uvec2 sz = textureSize(sampler2D(t_src_depth, s_src_depth), 0);
+    float buf_depth = texelFetch(sampler2D(t_src_depth, s_src_depth), clamp(ivec2(uv * sz), ivec2(0), ivec2(sz) - 1), 0).x;
+    //float buf_depth = texture(sampler2D(t_src_depth, s_src_depth), uv).x;
     vec4 clip_space = vec4((uv * 2.0 - 1.0) * vec2(1, -1), buf_depth, 1.0);
     vec4 view_space = all_mat_inv * clip_space;
     view_space /= view_space.w;
@@ -105,17 +107,30 @@ void main() {
                 vec3 dir_mid = dir_at(vec2(0, 0));
                 vec3 dir_right = dir_at(vec2(1, 0));
                 vec3 dir_up = dir_at(vec2(0, 1));
-                vec3 surf_norm = normalize(vec3((vec2(noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.3)).x, noise_3d(vec3((wpos.xy + focus_off.xy).yx * 0.1, tick.x * 0.3)).x) - 0.5) * 0.03, 1));
+                vec2 nz = (vec2(
+                    noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.2 + wpos.x * 0.01)).x,
+                    noise_3d(vec3((wpos.yx + focus_off.yx) * 0.1, tick.x * 0.2 + wpos.y * 0.01)).x
+                ) - 0.5) * 1.5;
+                vec3 surf_norm = normalize(vec3(nz * 0.03 / (1.0 + dist * 0.01), 1));
                 vec3 refl_dir = reflect(dir, surf_norm);
 
                 //float right = invlerp(atan2(dir_mid.x, dir_mid.y), atan2(dir_right.x, dir_right.y), atan2(refl_dir.x, refl_dir.y));
-                float up = invlerp(dir_mid.z, dir_up.z, refl_dir.z);
+                float up = invlerp(dir_mid.z, dir_up.z, refl_dir.z) + (cam_pos.z - wpos.z) / dist * 0.8;
 
-                vec2 new_uv = vec2(uv.x, up);
+                float look_z = dir_at(vec2(0.5, 0.5)).z;
+                float x_shift = (up - uv.y) * pow(abs(look_z), 0.5)
+                    * sign(look_z) * 0.6
+                    * (uv.x - 0.5) * (1.0 - pow(abs(uv.x - 0.5) * 2.0, 3.0));
+                vec2 new_uv = vec2(uv.x + x_shift, up);
 
                 float new_dist = distance(wpos_at(new_uv), cam_pos.xyz);
-                if (new_dist > dist) {
-                    float merge = clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.5, 0, 0.75);
+                if (new_dist > dist * 0.5) {
+                    float merge = min(
+                        // Off-screen merge factor
+                        clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 5.0, 0, 1.0),
+                        // Depth merge factor
+                        clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0)
+                    );
 
                     //vec2 new_uv = uv * vec2(1, -1) + vec2(0, 1.1) / (1.0 + dist * 0.000001) + vec2(0, dir.z);
                     color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
@@ -130,7 +145,7 @@ void main() {
         #else
             {
         #endif
-            dist = DIST_CAP;
+            //dist = DIST_CAP;
         }
     }
     color.rgb = mix(color.rgb, get_cloud_color(color.rgb, dir, cam_pos.xyz, time_of_day.x, dist, 1.0), cloud_blend);
diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 5785d31841..1dd9f6cdf6 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -107,7 +107,7 @@ vec4 wave_height(vec4 posx, vec4 posy) {
     return w / ws * 5.0;
 }
 
-float wave_height_vel(vec2 pos){
+float wave_height_vel(vec2 pos) {
     vec4 heights = wave_height(
         pos.x - tick.x * floor(f_vel.x) - vec2(0.0, tick.x).xyxy,
         pos.y - tick.x * floor(f_vel.y) - vec2(0.0, tick.x).xxyy
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index 5c1bb0fc60..d18ca00e97 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -648,6 +648,7 @@ void main() {
     // vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
     // f_col = f_col + (hash(vec4(floor(vec3(focus_pos.xy + splay(v_pos_orig), f_pos.z)) * 3.0 - round(f_norm) * 0.5, 0)) - 0.5) * 0.05; // Small-scale noise
     vec3 surf_color;
+    float surf_alpha = 1.0;
     #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         if (length(f_col_raw - vec3(0.02, 0.06, 0.22)) < 0.025 && dot(vec3(0, 0, 1), f_norm) > 0.9) {
             vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;
@@ -671,6 +672,7 @@ void main() {
             const vec3 underwater_col = vec3(0.0);
             float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
             surf_color = mix(underwater_col, surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));
+            surf_alpha = 0.99;
         } else {
             surf_color = illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light);
         }
@@ -683,5 +685,5 @@ void main() {
     // color = mix(color, vec3(1.0) * /*diffuse_light*/reflected_light, clamp(mist_factor * 0.00005 * distance(f_pos.xy, focus_pos.xy), 0, 0.3));
     // color = surf_color;
 
-    tgt_color = vec4(surf_color, 1.0);
+    tgt_color = vec4(surf_color, surf_alpha);
 }

From 0561a168dee8b8d7711f217fe0311cd7ace36452 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 16:20:14 +0100
Subject: [PATCH 10/28] Added tracing to SSR

---
 assets/voxygen/shaders/clouds-frag.glsl | 79 +++++++++++++++++++------
 voxygen/src/render/mod.rs               |  2 +
 2 files changed, 63 insertions(+), 18 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 43d046aef9..2a2214c8cf 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -63,6 +63,19 @@ vec3 wpos_at(vec2 uv) {
     }
 }
 
+float depth_at(vec2 uv) {
+    uvec2 sz = textureSize(sampler2D(t_src_depth, s_src_depth), 0);
+    float buf_depth = texelFetch(sampler2D(t_src_depth, s_src_depth), clamp(ivec2(uv * sz), ivec2(0), ivec2(sz) - 1), 0).x;
+    if (buf_depth == 0.0) {
+        return 524288.0;
+    } else {
+        vec4 clip_space = vec4((uv * 2.0 - 1.0) * vec2(1, -1), buf_depth, 1.0);
+        vec4 view_space = all_mat_inv * clip_space;
+        view_space /= view_space.w;
+        return -(view_mat * view_space).z;
+    }
+}
+
 vec3 dir_at(vec2 uv) {
     vec4 view_space = all_mat_inv * vec4((uv * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);
     view_space /= view_space.w;
@@ -85,6 +98,10 @@ void main() {
     float dist = distance(wpos, cam_pos.xyz);
     vec3 dir = (wpos - cam_pos.xyz) / dist;
 
+    /* vec4 clip2 = all_mat * vec4(wpos, 1.0); */
+    /* vec2 test_uv = (clip2.xy / clip2.w).xy * 0.5 * vec2(1, -1) + 0.5; */
+    /* color = texture(sampler2D(t_src_color, s_src_color), test_uv); */
+
     // Apply clouds
     float cloud_blend = 1.0;
     if (color.a < 1.0) {
@@ -104,43 +121,68 @@ void main() {
 
         #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONS
             if (dir.z < 0.0) {
-                vec3 dir_mid = dir_at(vec2(0, 0));
-                vec3 dir_right = dir_at(vec2(1, 0));
-                vec3 dir_up = dir_at(vec2(0, 1));
-                vec2 nz = (vec2(
-                    noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.2 + wpos.x * 0.01)).x,
-                    noise_3d(vec3((wpos.yx + focus_off.yx) * 0.1, tick.x * 0.2 + wpos.y * 0.01)).x
-                ) - 0.5) * 1.5;
-                vec3 surf_norm = normalize(vec3(nz * 0.03 / (1.0 + dist * 0.01), 1));
+                #if (FLUID_MODE == FLUID_MODE_CHEAP)
+                    vec2 nz = vec2(0);
+                #else
+                    vec2 nz = (vec2(
+                        noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.2 + wpos.x * 0.01)).x,
+                        noise_3d(vec3((wpos.yx + focus_off.yx) * 0.1, tick.x * 0.2 + wpos.y * 0.01)).x
+                    ) - 0.5) * 2.5;
+                #endif
+                vec3 surf_norm = normalize(vec3(nz * 0.03 / (1.0 + dist * 0.1), 1));
                 vec3 refl_dir = reflect(dir, surf_norm);
 
-                //float right = invlerp(atan2(dir_mid.x, dir_mid.y), atan2(dir_right.x, dir_right.y), atan2(refl_dir.x, refl_dir.y));
-                float up = invlerp(dir_mid.z, dir_up.z, refl_dir.z) + (cam_pos.z - wpos.z) / dist * 0.8;
+                vec3 ray_end = wpos + refl_dir * 50.0;
+                vec2 start_uv = uv;
+                vec4 clip_end = all_mat * vec4(ray_end, 1.0);
+                vec2 end_uv = (clip_end.xy / clip_end.w).xy * 0.5 * vec2(1, -1) + 0.5;
+                float depth_start = dist;
+                float depth_end = (view_mat * vec4(ray_end, 1.0)).z;//depth_at(end_uv);
 
-                float look_z = dir_at(vec2(0.5, 0.5)).z;
-                float x_shift = (up - uv.y) * pow(abs(look_z), 0.5)
-                    * sign(look_z) * 0.6
-                    * (uv.x - 0.5) * (1.0 - pow(abs(uv.x - 0.5) * 2.0, 3.0));
-                vec2 new_uv = vec2(uv.x + x_shift, up);
+                vec4 clip = (all_mat * vec4(cam_pos.xyz + refl_dir, 1.0));
+                vec2 new_uv = (clip.xy / max(clip.w, 0)) * 0.5 * vec2(1, -1) + 0.5;
 
+                #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONSCASTING
+                    const int ITERS = 64;
+                    float t = 0.0;
+                    float lastd = 0.0;
+                    for (int i = 0; i < ITERS; i ++) {
+                        vec3 swpos = mix(wpos, ray_end, t);
+                        vec3 svpos = (view_mat * vec4(swpos, 1)).xyz;
+                        vec4 clippos = proj_mat * vec4(svpos, 1);
+                        vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                        float d = -depth_at(suv);
+                        if (d < svpos.z * 0.85 && d > svpos.z * 0.999) {
+                            new_uv = suv;
+                            break;
+                        }
+                        lastd = d;
+                        t += 1.0 / float(ITERS);
+                    }
+                #endif
+
+                new_uv = clamp(new_uv, vec2(0), vec2(1));
+
+                vec3 new_wpos = wpos_at(new_uv);;
                 float new_dist = distance(wpos_at(new_uv), cam_pos.xyz);
-                if (new_dist > dist * 0.5) {
+                if (new_dist > dist || true) {
                     float merge = min(
                         // Off-screen merge factor
-                        clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 5.0, 0, 1.0),
+                        clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 2.0, 0, 0.75),
                         // Depth merge factor
                         clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0)
                     );
 
                     //vec2 new_uv = uv * vec2(1, -1) + vec2(0, 1.1) / (1.0 + dist * 0.000001) + vec2(0, dir.z);
                     color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
-                    wpos = wpos_at(new_uv);
+                    wpos = new_wpos;
                     dist = distance(wpos, cam_pos.xyz);
                     dir = (wpos - cam_pos.xyz) / dist;
                     cloud_blend = min(merge * 2.0, 1.0);
                 } else {
                     cloud_blend = 1.0;
                 }
+                /* color.rgb = vec3(t); */
             } else {
         #else
             {
@@ -148,6 +190,7 @@ void main() {
             //dist = DIST_CAP;
         }
     }
+    /* color.rgb = vec3(sin(depth_at(uv) * 3.14159 * 2) * 0.5 + 0.5); */
     color.rgb = mix(color.rgb, get_cloud_color(color.rgb, dir, cam_pos.xyz, time_of_day.x, dist, 1.0), cloud_blend);
 
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs
index e938fccf4e..b81b7189b5 100644
--- a/voxygen/src/render/mod.rs
+++ b/voxygen/src/render/mod.rs
@@ -502,4 +502,6 @@ pub enum ExperimentalShader {
     Wetness,
     /// Add screen-space reflections to water.
     ScreenSpaceReflections,
+    /// Use screen-space raycasting for reflections.
+    ScreenSpaceReflectionsCasting,
 }

From bc4c93bc5eb83eb9880eb72b3b1c9f6f3c946e5c Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 17:30:50 +0100
Subject: [PATCH 11/28] More accurate SSR

---
 assets/voxygen/shaders/clouds-frag.glsl | 73 +++++++++++++------------
 1 file changed, 39 insertions(+), 34 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 2a2214c8cf..89099a7243 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -127,37 +127,44 @@ void main() {
                     vec2 nz = (vec2(
                         noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.2 + wpos.x * 0.01)).x,
                         noise_3d(vec3((wpos.yx + focus_off.yx) * 0.1, tick.x * 0.2 + wpos.y * 0.01)).x
-                    ) - 0.5) * 2.5;
+                    ) - 0.5) * 5.0;
                 #endif
                 vec3 surf_norm = normalize(vec3(nz * 0.03 / (1.0 + dist * 0.1), 1));
                 vec3 refl_dir = reflect(dir, surf_norm);
 
-                vec3 ray_end = wpos + refl_dir * 50.0;
-                vec2 start_uv = uv;
-                vec4 clip_end = all_mat * vec4(ray_end, 1.0);
-                vec2 end_uv = (clip_end.xy / clip_end.w).xy * 0.5 * vec2(1, -1) + 0.5;
-                float depth_start = dist;
-                float depth_end = (view_mat * vec4(ray_end, 1.0)).z;//depth_at(end_uv);
-
                 vec4 clip = (all_mat * vec4(cam_pos.xyz + refl_dir, 1.0));
                 vec2 new_uv = (clip.xy / max(clip.w, 0)) * 0.5 * vec2(1, -1) + 0.5;
 
                 #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONSCASTING
-                    const int ITERS = 64;
+                    vec3 ray_end = wpos + refl_dir * 100.0;
                     float t = 0.0;
                     float lastd = 0.0;
+                    const int MAIN_ITERS = 64;
+                    for (int i = 0; i < MAIN_ITERS; i ++) {
+                        vec3 swpos = mix(wpos, ray_end, t);
+                        vec3 svpos = (view_mat * vec4(swpos, 1)).xyz;
+                        vec4 clippos = proj_mat * vec4(svpos, 1);
+                        new_uv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                        float d = -depth_at(new_uv);
+                        if (d < svpos.z * 0.8 && d > svpos.z * 0.999) {
+                            t -= 1.0 / float(MAIN_ITERS);
+                            break;
+                        }
+                        lastd = d;
+                        t += 1.0 / float(MAIN_ITERS);
+                        lastd = d;
+                    }
+
+                    const int ITERS = 8;
+                    float diff = 1.0 / float(MAIN_ITERS);
                     for (int i = 0; i < ITERS; i ++) {
                         vec3 swpos = mix(wpos, ray_end, t);
                         vec3 svpos = (view_mat * vec4(swpos, 1)).xyz;
                         vec4 clippos = proj_mat * vec4(svpos, 1);
-                        vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
-                        float d = -depth_at(suv);
-                        if (d < svpos.z * 0.85 && d > svpos.z * 0.999) {
-                            new_uv = suv;
-                            break;
-                        }
-                        lastd = d;
-                        t += 1.0 / float(ITERS);
+                        new_uv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                        float d = -depth_at(new_uv);
+                        t += ((d > svpos.z * 0.999) ? -1.0 : 1.0) * diff;
+                        diff *= 0.5;
                     }
                 #endif
 
@@ -165,24 +172,22 @@ void main() {
 
                 vec3 new_wpos = wpos_at(new_uv);;
                 float new_dist = distance(wpos_at(new_uv), cam_pos.xyz);
-                if (new_dist > dist || true) {
-                    float merge = min(
-                        // Off-screen merge factor
-                        clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 2.0, 0, 0.75),
-                        // Depth merge factor
-                        clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0)
-                    );
+                float merge = min(
+                    // Off-screen merge factor
+                    clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 2.0, 0, 0.75),
+                    // Depth merge factor
+                    min(
+                        clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0),
+                        max(dot(normalize(new_wpos - wpos), refl_dir) - 0.95, 0.0) / 0.05
+                    )
+                );
 
-                    //vec2 new_uv = uv * vec2(1, -1) + vec2(0, 1.1) / (1.0 + dist * 0.000001) + vec2(0, dir.z);
-                    color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
-                    wpos = new_wpos;
-                    dist = distance(wpos, cam_pos.xyz);
-                    dir = (wpos - cam_pos.xyz) / dist;
-                    cloud_blend = min(merge * 2.0, 1.0);
-                } else {
-                    cloud_blend = 1.0;
-                }
-                /* color.rgb = vec3(t); */
+                //vec2 new_uv = uv * vec2(1, -1) + vec2(0, 1.1) / (1.0 + dist * 0.000001) + vec2(0, dir.z);
+                color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
+                wpos = new_wpos;
+                dist = distance(wpos, cam_pos.xyz);
+                dir = (wpos - cam_pos.xyz) / dist;
+                cloud_blend = min(merge * 2.0, 1.0);
             } else {
         #else
             {

From 8dd63940b042bf753c3fe94edec4ffe256242824 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 20:44:17 +0100
Subject: [PATCH 12/28] Fixed SSR at distance

---
 assets/voxygen/shaders/clouds-frag.glsl | 34 ++++++++++++-------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 89099a7243..7b9bb939bf 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -136,7 +136,7 @@ void main() {
                 vec2 new_uv = (clip.xy / max(clip.w, 0)) * 0.5 * vec2(1, -1) + 0.5;
 
                 #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONSCASTING
-                    vec3 ray_end = wpos + refl_dir * 100.0;
+                    vec3 ray_end = wpos + refl_dir * 5.0 * dist;
                     float t = 0.0;
                     float lastd = 0.0;
                     const int MAIN_ITERS = 64;
@@ -144,28 +144,29 @@ void main() {
                         vec3 swpos = mix(wpos, ray_end, t);
                         vec3 svpos = (view_mat * vec4(swpos, 1)).xyz;
                         vec4 clippos = proj_mat * vec4(svpos, 1);
-                        new_uv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
-                        float d = -depth_at(new_uv);
+                        vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                        float d = -depth_at(suv);
                         if (d < svpos.z * 0.8 && d > svpos.z * 0.999) {
                             t -= 1.0 / float(MAIN_ITERS);
+                            const int ITERS = 8;
+                            float diff = 1.0 / float(MAIN_ITERS);
+                            for (int i = 0; i < ITERS; i ++) {
+                                vec3 swpos = mix(wpos, ray_end, t);
+                                svpos = (view_mat * vec4(swpos, 1)).xyz;
+                                vec4 clippos = proj_mat * vec4(svpos, 1);
+                                vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                                float d = -depth_at(suv);
+                                t += ((d > svpos.z * 0.999) ? -1.0 : 1.0) * diff;
+                                diff *= 0.5;
+
+                                new_uv = suv;
+                            }
                             break;
                         }
                         lastd = d;
                         t += 1.0 / float(MAIN_ITERS);
                         lastd = d;
                     }
-
-                    const int ITERS = 8;
-                    float diff = 1.0 / float(MAIN_ITERS);
-                    for (int i = 0; i < ITERS; i ++) {
-                        vec3 swpos = mix(wpos, ray_end, t);
-                        vec3 svpos = (view_mat * vec4(swpos, 1)).xyz;
-                        vec4 clippos = proj_mat * vec4(svpos, 1);
-                        new_uv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
-                        float d = -depth_at(new_uv);
-                        t += ((d > svpos.z * 0.999) ? -1.0 : 1.0) * diff;
-                        diff *= 0.5;
-                    }
                 #endif
 
                 new_uv = clamp(new_uv, vec2(0), vec2(1));
@@ -182,7 +183,6 @@ void main() {
                     )
                 );
 
-                //vec2 new_uv = uv * vec2(1, -1) + vec2(0, 1.1) / (1.0 + dist * 0.000001) + vec2(0, dir.z);
                 color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
                 wpos = new_wpos;
                 dist = distance(wpos, cam_pos.xyz);
@@ -192,7 +192,7 @@ void main() {
         #else
             {
         #endif
-            //dist = DIST_CAP;
+            dist = DIST_CAP;
         }
     }
     /* color.rgb = vec3(sin(depth_at(uv) * 3.14159 * 2) * 0.5 + 0.5); */

From 9285e48ff0d3ef71df3031f5d2c055f383af6935 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 20:46:17 +0100
Subject: [PATCH 13/28] Don't reflect rain

---
 assets/voxygen/shaders/clouds-frag.glsl | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 7b9bb939bf..2c7d98890d 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -104,6 +104,7 @@ void main() {
 
     // Apply clouds
     float cloud_blend = 1.0;
+    bool is_reflection = false;
     if (color.a < 1.0) {
         cloud_blend = 1.0 - color.a;
         //color.rgb = vec3(1, 0, 0);
@@ -188,6 +189,7 @@ void main() {
                 dist = distance(wpos, cam_pos.xyz);
                 dir = (wpos - cam_pos.xyz) / dist;
                 cloud_blend = min(merge * 2.0, 1.0);
+                is_reflection = true;
             } else {
         #else
             {
@@ -201,7 +203,7 @@ void main() {
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
         color.rgb = apply_point_glow(cam_pos.xyz + focus_off.xyz, dir, dist, color.rgb);
     #else
-        if (medium.x == MEDIUM_AIR && rain_density > 0.001) {
+        if (medium.x == MEDIUM_AIR && rain_density > 0.001 && !is_reflection) {
             vec3 cam_wpos = cam_pos.xyz + focus_off.xyz;
 
             vec3 adjusted_dir = (vec4(dir, 0) * rain_dir_mat).xyz;

From d4c81cf8b84d6ff1f691f08a7715d9d3b25ea23d Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 20:58:32 +0100
Subject: [PATCH 14/28] Handle SSR transition better

---
 assets/voxygen/shaders/clouds-frag.glsl | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 2c7d98890d..c90bc7bbe3 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -172,11 +172,11 @@ void main() {
 
                 new_uv = clamp(new_uv, vec2(0), vec2(1));
 
-                vec3 new_wpos = wpos_at(new_uv);;
-                float new_dist = distance(wpos_at(new_uv), cam_pos.xyz);
+                vec3 new_wpos = wpos_at(new_uv);
+                float new_dist = distance(new_wpos, cam_pos.xyz);
                 float merge = min(
                     // Off-screen merge factor
-                    clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 2.0, 0, 0.75),
+                    clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.0, 0, 0.75),
                     // Depth merge factor
                     min(
                         clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0),
@@ -184,12 +184,14 @@ void main() {
                     )
                 );
 
-                color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
-                wpos = new_wpos;
-                dist = distance(wpos, cam_pos.xyz);
-                dir = (wpos - cam_pos.xyz) / dist;
-                cloud_blend = min(merge * 2.0, 1.0);
-                is_reflection = true;
+                if (merge > 0.0) {
+                    color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
+                    wpos = new_wpos;
+                    dist = distance(wpos, cam_pos.xyz);
+                    dir = (wpos - cam_pos.xyz) / dist;
+                    cloud_blend = merge;
+                    is_reflection = true;
+                }
             } else {
         #else
             {

From d40be0ea5feb0e763fb4e78bd320f2a59c940157 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 21:27:19 +0100
Subject: [PATCH 15/28] Apply clouds more correctly

---
 assets/voxygen/shaders/clouds-frag.glsl | 45 +++++--------------------
 1 file changed, 9 insertions(+), 36 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index c90bc7bbe3..599b541493 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -76,16 +76,6 @@ float depth_at(vec2 uv) {
     }
 }
 
-vec3 dir_at(vec2 uv) {
-    vec4 view_space = all_mat_inv * vec4((uv * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);
-    view_space /= view_space.w;
-    return normalize(view_space.xyz);
-}
-
-float invlerp(float a, float b, float v) {
-    return (v - a) / (b - a);
-}
-
 void main() {
     vec4 color = texture(sampler2D(t_src_color, s_src_color), uv);
 
@@ -98,27 +88,11 @@ void main() {
     float dist = distance(wpos, cam_pos.xyz);
     vec3 dir = (wpos - cam_pos.xyz) / dist;
 
-    /* vec4 clip2 = all_mat * vec4(wpos, 1.0); */
-    /* vec2 test_uv = (clip2.xy / clip2.w).xy * 0.5 * vec2(1, -1) + 0.5; */
-    /* color = texture(sampler2D(t_src_color, s_src_color), test_uv); */
-
     // Apply clouds
     float cloud_blend = 1.0;
     bool is_reflection = false;
-    if (color.a < 1.0) {
+    if (color.a < 1.0 && medium.x != MEDIUM_WATER) {
         cloud_blend = 1.0 - color.a;
-        //color.rgb = vec3(1, 0, 0);
-
-        vec2 uv_refl = uv;
-        vec3 wpos_refl = wpos;
-        /* for (int i = 0; i < 10; i ++) { */
-        /*     uv_refl.y -= 0.1; */
-        /*     wpos_refl += reflect(dir, vec3(0, 0, 1)) * dist * 0.5; */
-        /*     if (distance(wpos_at(uv_refl), cam_pos.xyz) < distance(wpos_refl, cam_pos.xyz)) { */
-        /*         color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), uv_refl).rgb, 0.9); */
-        /*         break; */
-        /*     } */
-        /* } */
 
         #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONS
             if (dir.z < 0.0) {
@@ -139,7 +113,7 @@ void main() {
                 #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONSCASTING
                     vec3 ray_end = wpos + refl_dir * 5.0 * dist;
                     float t = 0.0;
-                    float lastd = 0.0;
+                    // Trace through the screen-space depth buffer to find the ray intersection
                     const int MAIN_ITERS = 64;
                     for (int i = 0; i < MAIN_ITERS; i ++) {
                         vec3 swpos = mix(wpos, ray_end, t);
@@ -149,6 +123,7 @@ void main() {
                         float d = -depth_at(suv);
                         if (d < svpos.z * 0.8 && d > svpos.z * 0.999) {
                             t -= 1.0 / float(MAIN_ITERS);
+                            // Do a bit of extra iteration to try to refine the estimate
                             const int ITERS = 8;
                             float diff = 1.0 / float(MAIN_ITERS);
                             for (int i = 0; i < ITERS; i ++) {
@@ -164,9 +139,7 @@ void main() {
                             }
                             break;
                         }
-                        lastd = d;
                         t += 1.0 / float(MAIN_ITERS);
-                        lastd = d;
                     }
                 #endif
 
@@ -176,7 +149,7 @@ void main() {
                 float new_dist = distance(new_wpos, cam_pos.xyz);
                 float merge = min(
                     // Off-screen merge factor
-                    clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.0, 0, 0.75),
+                    clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.0, 0, 1),
                     // Depth merge factor
                     min(
                         clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0),
@@ -185,17 +158,17 @@ void main() {
                 );
 
                 if (merge > 0.0) {
-                    color.rgb = mix(color.rgb, texture(sampler2D(t_src_color, s_src_color), new_uv).rgb, merge);
-                    wpos = new_wpos;
-                    dist = distance(wpos, cam_pos.xyz);
-                    dir = (wpos - cam_pos.xyz) / dist;
-                    cloud_blend = merge;
+                    vec3 new_col = texture(sampler2D(t_src_color, s_src_color), new_uv).rgb;
+                    new_col = get_cloud_color(new_col.rgb, refl_dir, wpos, time_of_day.x, distance(new_wpos, wpos.xyz), 1.0);
+                    color.rgb = mix(color.rgb, new_col, merge);
+                    cloud_blend = 1;
                     is_reflection = true;
                 }
             } else {
         #else
             {
         #endif
+            cloud_blend = 1;
             dist = DIST_CAP;
         }
     }

From be8ef302bc1de6ca7cdc00b870463b2648f49449 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 22:07:07 +0100
Subject: [PATCH 16/28] Make SSR water verticals behave better

---
 assets/voxygen/shaders/clouds-frag.glsl | 33 ++++++++++++++-----------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 599b541493..38d430b964 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -122,22 +122,25 @@ void main() {
                         vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
                         float d = -depth_at(suv);
                         if (d < svpos.z * 0.8 && d > svpos.z * 0.999) {
-                            t -= 1.0 / float(MAIN_ITERS);
-                            // Do a bit of extra iteration to try to refine the estimate
-                            const int ITERS = 8;
-                            float diff = 1.0 / float(MAIN_ITERS);
-                            for (int i = 0; i < ITERS; i ++) {
-                                vec3 swpos = mix(wpos, ray_end, t);
-                                svpos = (view_mat * vec4(swpos, 1)).xyz;
-                                vec4 clippos = proj_mat * vec4(svpos, 1);
-                                vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
-                                float d = -depth_at(suv);
-                                t += ((d > svpos.z * 0.999) ? -1.0 : 1.0) * diff;
-                                diff *= 0.5;
+                            // Don't cast into water!
+                            if (texture(sampler2D(t_src_color, s_src_color), suv).a >= 1.0) {
+                                t -= 1.0 / float(MAIN_ITERS);
+                                // Do a bit of extra iteration to try to refine the estimate
+                                const int ITERS = 8;
+                                float diff = 1.0 / float(MAIN_ITERS);
+                                for (int i = 0; i < ITERS; i ++) {
+                                    vec3 swpos = mix(wpos, ray_end, t);
+                                    svpos = (view_mat * vec4(swpos, 1)).xyz;
+                                    vec4 clippos = proj_mat * vec4(svpos, 1);
+                                    vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                                    float d = -depth_at(suv);
+                                    t += ((d > svpos.z * 0.999) ? -1.0 : 1.0) * diff;
+                                    diff *= 0.5;
 
-                                new_uv = suv;
+                                    new_uv = suv;
+                                }
+                                break;
                             }
-                            break;
                         }
                         t += 1.0 / float(MAIN_ITERS);
                     }
@@ -155,7 +158,7 @@ void main() {
                         clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0),
                         max(dot(normalize(new_wpos - wpos), refl_dir) - 0.95, 0.0) / 0.05
                     )
-                );
+                ) * 0.85;
 
                 if (merge > 0.0) {
                     vec3 new_col = texture(sampler2D(t_src_color, s_src_color), new_uv).rgb;

From 51c3a25b9a0e5cd82025ec169650c71d6a6fdca5 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Mon, 10 Oct 2022 22:27:09 +0100
Subject: [PATCH 17/28] Handle obscured SSR elements better

---
 assets/voxygen/shaders/clouds-frag.glsl      | 11 ++++-------
 assets/voxygen/shaders/fluid-frag/shiny.glsl |  2 +-
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 38d430b964..cace20475d 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -132,13 +132,13 @@ void main() {
                                     vec3 swpos = mix(wpos, ray_end, t);
                                     svpos = (view_mat * vec4(swpos, 1)).xyz;
                                     vec4 clippos = proj_mat * vec4(svpos, 1);
-                                    vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                                    suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
                                     float d = -depth_at(suv);
                                     t += ((d > svpos.z * 0.999) ? -1.0 : 1.0) * diff;
                                     diff *= 0.5;
-
-                                    new_uv = suv;
                                 }
+                                // Small offset to push us into obscured territory
+                                new_uv = suv - vec2(0, 0.001);
                                 break;
                             }
                         }
@@ -154,10 +154,7 @@ void main() {
                     // Off-screen merge factor
                     clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.0, 0, 1),
                     // Depth merge factor
-                    min(
-                        clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0),
-                        max(dot(normalize(new_wpos - wpos), refl_dir) - 0.95, 0.0) / 0.05
-                    )
+                    clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0)
                 ) * 0.85;
 
                 if (merge > 0.0) {
diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 1dd9f6cdf6..68f1d5ff79 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -406,7 +406,7 @@ void main() {
     if (medium.x != MEDIUM_WATER) {
         min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
     }
-    vec4 color = vec4(surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam)));
+    vec4 color = vec4(surf_color, (1.0 - passthrough) * 0.5 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam)));
     // vec4 color = vec4(surf_color, mix(1.0, 1.0 / (1.0 + /*0.25 * *//*diffuse_light*/(/*f_light * point_shadow*/reflected_light_point)), passthrough));
     // vec4 color = vec4(surf_color, mix(1.0, length(cam_attenuation), passthrough));
 

From 764adb67c76148170b67f207927e3add90e5b122 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Tue, 11 Oct 2022 14:23:20 +0100
Subject: [PATCH 18/28] Fixed no SSR shaders

---
 assets/voxygen/shaders/clouds-frag.glsl | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index cace20475d..8b0512eb63 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -112,10 +112,11 @@ void main() {
 
                 #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONSCASTING
                     vec3 ray_end = wpos + refl_dir * 5.0 * dist;
-                    float t = 0.0;
                     // Trace through the screen-space depth buffer to find the ray intersection
                     const int MAIN_ITERS = 64;
                     for (int i = 0; i < MAIN_ITERS; i ++) {
+                        float t = float(i) / float(MAIN_ITERS);
+                        // TODO: Trace in screen space, not world space
                         vec3 swpos = mix(wpos, ray_end, t);
                         vec3 svpos = (view_mat * vec4(swpos, 1)).xyz;
                         vec4 clippos = proj_mat * vec4(svpos, 1);
@@ -124,7 +125,7 @@ void main() {
                         if (d < svpos.z * 0.8 && d > svpos.z * 0.999) {
                             // Don't cast into water!
                             if (texture(sampler2D(t_src_color, s_src_color), suv).a >= 1.0) {
-                                t -= 1.0 / float(MAIN_ITERS);
+                                /* t -= 1.0 / float(MAIN_ITERS); */
                                 // Do a bit of extra iteration to try to refine the estimate
                                 const int ITERS = 8;
                                 float diff = 1.0 / float(MAIN_ITERS);
@@ -142,7 +143,6 @@ void main() {
                                 break;
                             }
                         }
-                        t += 1.0 / float(MAIN_ITERS);
                     }
                 #endif
 
@@ -169,7 +169,7 @@ void main() {
             {
         #endif
             cloud_blend = 1;
-            dist = DIST_CAP;
+            /* dist = DIST_CAP; */
         }
     }
     /* color.rgb = vec3(sin(depth_at(uv) * 3.14159 * 2) * 0.5 + 0.5); */

From ae66432a1e92ef7d929f6498ea6b5f99a2e2eadb Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Tue, 11 Oct 2022 15:49:31 +0100
Subject: [PATCH 19/28] Make cheap water look better underwater

---
 assets/voxygen/shaders/clouds-frag.glsl      |  3 +--
 assets/voxygen/shaders/fluid-frag/cheap.glsl | 17 ++++++++++-------
 assets/voxygen/shaders/fluid-frag/shiny.glsl |  8 ++++++--
 assets/voxygen/shaders/include/lod.glsl      |  6 +++---
 assets/voxygen/shaders/include/sky.glsl      |  2 +-
 assets/voxygen/shaders/lod-terrain-frag.glsl |  2 +-
 assets/voxygen/shaders/skybox-frag.glsl      |  2 +-
 assets/voxygen/shaders/terrain-frag.glsl     |  2 +-
 8 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 8b0512eb63..0e391b5d7f 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -168,8 +168,7 @@ void main() {
         #else
             {
         #endif
-            cloud_blend = 1;
-            /* dist = DIST_CAP; */
+            dist = DIST_CAP;
         }
     }
     /* color.rgb = vec3(sin(depth_at(uv) * 3.14159 * 2) * 0.5 + 0.5); */
diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl
index 8ae661a7b0..33d0b608e8 100644
--- a/assets/voxygen/shaders/fluid-frag/cheap.glsl
+++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl
@@ -59,7 +59,7 @@ vec4 water_col(vec4 posx, vec4 posy) {
         textureLod(sampler2D(t_noise, s_noise), vec2(posx.y, posy.y), 0).x,
         textureLod(sampler2D(t_noise, s_noise), vec2(posx.z, posy.z), 0).x,
         textureLod(sampler2D(t_noise, s_noise), vec2(posx.w, posy.w), 0).x
-    ) - 0.5) * 1.3;
+    ) - 0.5) * 1.0;
 }
 
 float water_col_vel(vec2 pos){
@@ -108,7 +108,7 @@ void main() {
     vec3 view_dir = -cam_to_frag;
     // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0));
 
-    vec3 water_color = (1.0 - mix(MU_WATER, vec3(0.8, 0.24, 0.08), water_col_vel(f_pos.xy))) * MU_SCATTER;
+    vec3 water_color = (1.0 - mix(pow(MU_WATER, vec3(0.25)), pow(vec3(0.8, 0.24, 0.08), vec3(0.25)), water_col_vel(f_pos.xy))) * MU_SCATTER;
 
     /* vec3 sun_dir = get_sun_dir(time_of_day.x);
     vec3 moon_dir = get_moon_dir(time_of_day.x); */
@@ -215,15 +215,18 @@ void main() {
     // float reflected_light_point = /*length*/(diffuse_light_point.r) + f_light * point_shadow;
     // reflected_light += k_d * (diffuse_light_point + f_light * point_shadow * shade_frac) + specular_light_point;
 
-    float passthrough = max(dot(f_norm, -cam_to_frag), 0) * 0.65;
+    float passthrough = max(dot(cam_norm, -cam_to_frag), 0) * 0.65;
+
     float min_refl = 0.0;
-    /* if (medium.x != MEDIUM_WATER) { */
-    /*     min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b)); */
-    /* } */
+    float opacity = (1.0 - passthrough) * 1.0 / (1.0 + min_refl);
+    if (medium.x == MEDIUM_WATER) {
+        // Hack to make the opacity of the surface fade when underwater to avoid artifacts
+        opacity = min(sqrt(max(opacity, clamp((f_pos.z - cam_pos.z) * 0.05, 0.0, 1.0))), 1.0);
+    }
 
     vec3 surf_color = illuminate(max_light, view_dir, water_color * /* fog_color * */emitted_light, /*surf_color * */water_color * reflected_light);
     // vec4 color = vec4(surf_color, passthrough * 1.0 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam)));
-    vec4 color = vec4(surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));
+    vec4 color = vec4(surf_color, opacity);
 
     tgt_color = color;
 }
diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 68f1d5ff79..c71c6b0b8c 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -332,7 +332,7 @@ void main() {
     // vec3 light, diffuse_light, ambient_light;
     // vec3 light_frac = /*vec3(1.0);*/light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha);
     // 0 = 100% reflection, 1 = translucent water
-    float passthrough = max(dot(norm, -cam_to_frag), 0) * 0.75;
+    float passthrough = max(dot(cam_norm, -cam_to_frag), 0) * 0.75;
 
     float max_light = 0.0;
     max_light += get_sun_diffuse2(sun_info, moon_info, cam_norm, /*time_of_day.x*/sun_view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, vec3(k_d), /*vec3(f_light * point_shadow)*//*reflect_color*/k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
@@ -403,10 +403,14 @@ void main() {
 
     // float log_cam = log(min(cam_attenuation.r, min(cam_attenuation.g, cam_attenuation.b)));
     float min_refl = 0.0;
+    float opacity = (1.0 - passthrough) * 0.5 / (1.0 + min_refl);
     if (medium.x != MEDIUM_WATER) {
         min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
+    } else {
+        // Hack to make the opacity of the surface fade when underwater to avoid artifacts
+        opacity = min(sqrt(max(opacity, clamp((f_pos.z - cam_pos.z) * 0.05, 0.0, 1.0))), 1.0);
     }
-    vec4 color = vec4(surf_color, (1.0 - passthrough) * 0.5 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam)));
+    vec4 color = vec4(surf_color, opacity);// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam)));
     // vec4 color = vec4(surf_color, mix(1.0, 1.0 / (1.0 + /*0.25 * *//*diffuse_light*/(/*f_light * point_shadow*/reflected_light_point)), passthrough));
     // vec4 color = vec4(surf_color, mix(1.0, length(cam_attenuation), passthrough));
 
diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl
index d2efde0737..54ca47e900 100644
--- a/assets/voxygen/shaders/include/lod.glsl
+++ b/assets/voxygen/shaders/include/lod.glsl
@@ -391,13 +391,13 @@ vec3 water_diffuse(vec3 color, vec3 dir, float max_dist) {
         float f_alt = alt_at(cam_pos.xy);
         float fluid_alt = max(cam_pos.z + 1, floor(f_alt + 1));
 
-        float water_dist = clamp((fluid_alt - cam_pos.z) / pow(max(dir.z, 0), 5), 0, max_dist);
+        float water_dist = clamp((fluid_alt - cam_pos.z) / pow(max(dir.z, 0), 2), 0, max_dist);
 
-        float fade = pow(0.97, water_dist);
+        float fade = pow(0.95, water_dist);
 
         return mix(vec3(0.0, 0.2, 0.5)
             * (get_sun_brightness() * get_sun_color() + get_moon_brightness() * get_moon_color())
-            * pow(0.99, max((fluid_alt - cam_pos.z) * 12.0 - dir.z * 200, 0)), color.rgb, fade);
+            * pow(0.99, max((fluid_alt - cam_pos.z) * 12.0 - dir.z * 200, 0)), color.rgb * exp(-MU_WATER * water_dist * 0.1), fade);
     } else {
         return color;
     }
diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl
index 810282037f..1958d31eb3 100644
--- a/assets/voxygen/shaders/include/sky.glsl
+++ b/assets/voxygen/shaders/include/sky.glsl
@@ -581,7 +581,7 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
     // moon_dir = moon_dir.z <= 0 ? refract(moon_dir/*-view_dir*/, vec3(0.0, 0.0, 1.0), refractionIndex) : moon_dir;
 
     // Sun
-    const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 50.0;
+    const vec3 SUN_SURF_COLOR = vec3(1.5, 0.9, 0.35) * 10.0;
 
     vec3 sun_halo_color = mix(
         (sun_dir.x > 0 ? SUN_HALO_DUSK : SUN_HALO_DAWN)* magnetosphere_tint,
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index d18ca00e97..cd9027aa55 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -579,7 +579,7 @@ void main() {
 
     vec3 emitted_light, reflected_light;
 
-    vec3 mu = medium.x == MEDIUM_WATER/* && f_pos.z <= fluid_alt*/ ? MU_WATER : vec3(0.0);
+    vec3 mu = false/* && f_pos.z <= fluid_alt*/ ? MU_WATER : vec3(0.0);
     // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water.
     vec3 cam_attenuation = compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos);
     // Use f_norm here for better shadows.
diff --git a/assets/voxygen/shaders/skybox-frag.glsl b/assets/voxygen/shaders/skybox-frag.glsl
index 54da8cbc1b..71af67f185 100644
--- a/assets/voxygen/shaders/skybox-frag.glsl
+++ b/assets/voxygen/shaders/skybox-frag.glsl
@@ -55,5 +55,5 @@ void main() {
     } */
     vec3 wpos = cam_pos.xyz + /*normalize(f_pos)*/cam_dir * dist;
 
-    tgt_color = vec4(cam_attenuation * get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, wpos, 1.0, true, refractionIndex), 1.0);
+    tgt_color = vec4(cam_attenuation * get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, wpos, 1.0, medium.x != MEDIUM_WATER, refractionIndex), 1.0);
 }
diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index 1a771dbaf0..8d86b6bd47 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -362,7 +362,7 @@ void main() {
     // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water.
     // Computing light attenuation from water.
     vec3 cam_attenuation =
-        medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, MU_WATER, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
+        false/*medium.x == MEDIUM_WATER*/ ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, MU_WATER, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
         : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
 
     // Prevent the sky affecting light when underground

From 0a5e257b775b7d846a053dc5cbce59b3ca411abc Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 22 Oct 2022 13:24:43 +0100
Subject: [PATCH 20/28] Remove close-up camera jitter

---
 voxygen/src/mesh/terrain.rs           | 19 +++++++++++----
 voxygen/src/render/pipelines/fluid.rs |  4 +++-
 voxygen/src/scene/camera.rs           | 33 +++++++++++++++------------
 voxygen/src/scene/terrain.rs          |  3 ++-
 4 files changed, 38 insertions(+), 21 deletions(-)

diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs
index 13dc804f79..4f4e6410f9 100644
--- a/voxygen/src/mesh/terrain.rs
+++ b/voxygen/src/mesh/terrain.rs
@@ -392,11 +392,20 @@ pub fn generate_mesh<'a>(
         |atlas_pos, pos, norm, meta| TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta);
     let create_transparent = |_atlas_pos, pos: Vec3<f32>, norm| {
         let key = vol.pos_key(range.min + pos.as_());
-        let v00 = vol.get_key(key + Vec2::new(0, 0)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
-        let v10 = vol.get_key(key + Vec2::new(1, 0)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
-        let v01 = vol.get_key(key + Vec2::new(0, 1)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
-        let v11 = vol.get_key(key + Vec2::new(1, 1)).map_or(Vec3::zero(), |c| c.meta().river_velocity());
-        let factor = (range.min + pos.as_()).map(|e| e as f32) / TerrainChunk::RECT_SIZE.map(|e| e as f32);
+        let v00 = vol
+            .get_key(key + Vec2::new(0, 0))
+            .map_or(Vec3::zero(), |c| c.meta().river_velocity());
+        let v10 = vol
+            .get_key(key + Vec2::new(1, 0))
+            .map_or(Vec3::zero(), |c| c.meta().river_velocity());
+        let v01 = vol
+            .get_key(key + Vec2::new(0, 1))
+            .map_or(Vec3::zero(), |c| c.meta().river_velocity());
+        let v11 = vol
+            .get_key(key + Vec2::new(1, 1))
+            .map_or(Vec3::zero(), |c| c.meta().river_velocity());
+        let factor =
+            (range.min + pos.as_()).map(|e| e as f32) / TerrainChunk::RECT_SIZE.map(|e| e as f32);
         let vel = Lerp::lerp(
             Lerp::lerp(v00, v10, factor.x.rem_euclid(1.0)),
             Lerp::lerp(v01, v11, factor.x.rem_euclid(1.0)),
diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs
index a7fcccaf58..aea36bbfd5 100644
--- a/voxygen/src/render/pipelines/fluid.rs
+++ b/voxygen/src/render/pipelines/fluid.rs
@@ -29,7 +29,9 @@ impl Vertex {
                 | (((pos.z + EXTRA_NEG_Z).max(0.0).min((1 << 17) as f32) as u32) & 0x1FFFF) << 12
                 | (norm_bits & 0x7) << 29,
             vel: river_velocity
-                .map2(Vec2::new(0, 16), |e, off| (((e * 1000.0 + 32768.9) as u16 as u32) << off))
+                .map2(Vec2::new(0, 16), |e, off| {
+                    (((e * 1000.0 + 32768.9) as u16 as u32) << off)
+                })
                 .reduce_bitor(),
         }
     }
diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs
index 26ed0a74a7..6cef70a274 100644
--- a/voxygen/src/scene/camera.rs
+++ b/voxygen/src/scene/camera.rs
@@ -1,6 +1,6 @@
 use common::{terrain::TerrainGrid, vol::ReadVol};
 use common_base::span;
-use core::{f32::consts::PI, fmt::Debug};
+use core::{f32::consts::PI, fmt::Debug, ops::Range};
 use num::traits::{real::Real, FloatConst};
 use treeculler::Frustum;
 use vek::*;
@@ -12,7 +12,7 @@ const FIRST_PERSON_INTERP_TIME: f32 = 0.1;
 const THIRD_PERSON_INTERP_TIME: f32 = 0.1;
 const FREEFLY_INTERP_TIME: f32 = 0.0;
 const LERP_ORI_RATE: f32 = 15.0;
-const CLIPPING_MODE_DISTANCE: f32 = 20.0;
+const CLIPPING_MODE_DISTANCE: Range<f32> = 2.0..20.0;
 pub const MIN_ZOOM: f32 = 0.1;
 
 // Possible TODO: Add more modes
@@ -368,7 +368,7 @@ impl Camera {
     ) {
         span!(_guard, "compute_dependents", "Camera::compute_dependents");
         // TODO: More intelligent function to decide on which strategy to use
-        if self.tgt_dist < CLIPPING_MODE_DISTANCE {
+        if self.tgt_dist < CLIPPING_MODE_DISTANCE.end {
             self.compute_dependents_near(terrain, is_transparent)
         } else {
             self.compute_dependents_far(terrain, is_transparent)
@@ -425,18 +425,23 @@ impl Camera {
                 .unwrap_or(0.0)
         };
 
-        if self.dist >= dist {
-            self.dist = dist;
-        }
-
-        // Recompute only if needed
-        if (dist - self.tgt_dist).abs() > f32::EPSILON {
-            let dependents = self.compute_dependents_helper(dist);
-            self.frustum = self.compute_frustum(&dependents);
-            self.dependents = dependents;
+        // If the camera ends up being too close to the focus point, switch policies.
+        if dist < CLIPPING_MODE_DISTANCE.start {
+            self.compute_dependents_far(terrain, is_transparent);
         } else {
-            self.dependents = local_dependents;
-            self.frustum = frustum;
+            if self.dist >= dist {
+                self.dist = dist;
+            }
+
+            // Recompute only if needed
+            if (dist - self.tgt_dist).abs() > f32::EPSILON {
+                let dependents = self.compute_dependents_helper(dist);
+                self.frustum = self.compute_frustum(&dependents);
+                self.dependents = dependents;
+            } else {
+                self.dependents = local_dependents;
+                self.frustum = frustum;
+            }
         }
     }
 
diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs
index cf23c3bc6a..bbbb9c1111 100644
--- a/voxygen/src/scene/terrain.rs
+++ b/voxygen/src/scene/terrain.rs
@@ -278,7 +278,8 @@ fn mesh_worker(
                 for y in 0..TerrainChunk::RECT_SIZE.y as i32 {
                     for z in z_bounds.0 as i32..z_bounds.1 as i32 + 1 {
                         let rel_pos = Vec3::new(x, y, z);
-                        let wpos = Vec3::from(pos * TerrainChunk::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos;
+                        let wpos = Vec3::from(pos * TerrainChunk::RECT_SIZE.map(|e: u32| e as i32))
+                            + rel_pos;
 
                         let block = if let Ok(block) = volume.get(wpos) {
                             block

From 5c4090f09997f0c9cbaa3ea56c92f791e90731e5 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 22 Oct 2022 13:50:13 +0100
Subject: [PATCH 21/28] Updated README

---
 CHANGELOG.md                          | 4 ++++
 voxygen/src/render/pipelines/fluid.rs | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76714a79b2..d1c274292f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Pets can now be traded with.
 - Crafting recipe for black lantern
 - Added redwood and dead trees
+- Experimental screen-space reflection shader
+- Water will now move according to its apparent flow direction
 
 ### Changed
 - Use fluent for translations
@@ -60,6 +62,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Pets no longer aggro on pet owners after being healed
 - Pets no longer lose their intrinsic weapons/armour when loaded on login.
 - Fixed npcs using `/say` instead of `/tell`
+- Camera jittering in third person has been significantly reduced
+- Many water shader issues have been fixed
 
 ## [0.13.0] - 2022-07-23
 
diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs
index aea36bbfd5..331e73cac0 100644
--- a/voxygen/src/render/pipelines/fluid.rs
+++ b/voxygen/src/render/pipelines/fluid.rs
@@ -30,7 +30,7 @@ impl Vertex {
                 | (norm_bits & 0x7) << 29,
             vel: river_velocity
                 .map2(Vec2::new(0, 16), |e, off| {
-                    (((e * 1000.0 + 32768.9) as u16 as u32) << off)
+                    ((e * 1000.0 + 32768.9) as u16 as u32) << off
                 })
                 .reduce_bitor(),
         }

From a7c24ed5184cb30ebc490c0e54fc5320b30d37cd Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 22 Oct 2022 15:01:23 +0100
Subject: [PATCH 22/28] Added figure noise

---
 assets/voxygen/shaders/clouds-frag.glsl      |  5 ++++-
 assets/voxygen/shaders/figure-frag.glsl      | 18 +++++++++++++++++-
 assets/voxygen/shaders/figure-vert.glsl      |  5 +++++
 assets/voxygen/shaders/lod-terrain-frag.glsl | 14 ++++++--------
 4 files changed, 32 insertions(+), 10 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 0e391b5d7f..c5749efd51 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -163,12 +163,15 @@ void main() {
                     color.rgb = mix(color.rgb, new_col, merge);
                     cloud_blend = 1;
                     is_reflection = true;
+                } else {
+                    cloud_blend = 1;
                 }
             } else {
         #else
             {
         #endif
-            dist = DIST_CAP;
+            cloud_blend = 1;
+            //dist = DIST_CAP;
         }
     }
     /* color.rgb = vec3(sin(depth_at(uv) * 3.14159 * 2) * 0.5 + 0.5); */
diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl
index c67bffa436..92d222f0da 100644
--- a/assets/voxygen/shaders/figure-frag.glsl
+++ b/assets/voxygen/shaders/figure-frag.glsl
@@ -32,6 +32,8 @@ layout(location = 0) in vec3 f_pos;
 // flat in uint f_pos_norm;
 layout(location = 1) flat in vec3 f_norm;
 /*centroid */layout(location = 2) in vec2 f_uv_pos;
+layout(location = 3) in vec3 m_pos;
+layout(location = 4) in float scale;
 // in float f_alt;
 // in vec4 f_shadow;
 // in vec3 light_pos[2];
@@ -161,7 +163,21 @@ void main() {
     DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos);
     DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/);
 
-    vec3 surf_color = /*srgb_to_linear*/f_col;
+    vec3 surf_color;
+    // If the figure is large enough to be 'terrain-like', we apply a noise effect to it
+    if (scale >= 0.5) {
+        float noise = hash(vec4(floor(m_pos * 3.0 - f_norm * 0.5), 0));
+
+        const float A = 0.055;
+        const float W_INV = 1 / (1 + A);
+        const float W_2 = W_INV * W_INV;
+        const float NOISE_FACTOR = 0.015;
+        vec3 noise_delta = (sqrt(f_col) * W_INV + noise * NOISE_FACTOR);
+        surf_color = noise_delta * noise_delta * W_2;
+    } else {
+        surf_color = f_col;
+    }
+
     float alpha = 1.0;
     const float n2 = 1.5;
 
diff --git a/assets/voxygen/shaders/figure-vert.glsl b/assets/voxygen/shaders/figure-vert.glsl
index 602cc51066..d24b299872 100644
--- a/assets/voxygen/shaders/figure-vert.glsl
+++ b/assets/voxygen/shaders/figure-vert.glsl
@@ -70,6 +70,8 @@ layout(location = 0) out vec3 f_pos;
 layout(location = 1) flat out vec3 f_norm;
 // float dummy;
 /*centroid */layout(location = 2) out vec2 f_uv_pos;
+layout(location = 3) out vec3 m_pos;
+layout(location = 4) out float scale;
 // out vec3 f_col;
 // out float f_ao;
 // out float f_alt;
@@ -90,6 +92,9 @@ void main() {
 
     // vec4 bone_pos = bones[bone_idx].bone_mat * vec4(pos, 1);
 
+    m_pos = pos;
+    scale = length(bones[bone_idx].bone_mat[0]);
+
     f_pos = (
         bones[bone_idx].bone_mat *
         vec4(pos, 1.0)
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index cd9027aa55..7669b66c83 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -649,8 +649,8 @@ void main() {
     // f_col = f_col + (hash(vec4(floor(vec3(focus_pos.xy + splay(v_pos_orig), f_pos.z)) * 3.0 - round(f_norm) * 0.5, 0)) - 0.5) * 0.05; // Small-scale noise
     vec3 surf_color;
     float surf_alpha = 1.0;
-    #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
-        if (length(f_col_raw - vec3(0.02, 0.06, 0.22)) < 0.025 && dot(vec3(0, 0, 1), f_norm) > 0.9) {
+    if (length(f_col_raw - vec3(0.02, 0.06, 0.22)) < 0.025 && dot(vec3(0, 0, 1), f_norm) > 0.9) {
+        #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
             vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;
 
             vec3 reflect_ray = cam_to_frag * vec3(1, 1, -1);
@@ -672,13 +672,11 @@ void main() {
             const vec3 underwater_col = vec3(0.0);
             float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
             surf_color = mix(underwater_col, surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));
-            surf_alpha = 0.99;
-        } else {
-            surf_color = illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light);
-        }
-    #else
+        #endif
+        surf_alpha = 0.99;
+    } else {
         surf_color = illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light);
-    #endif
+    }
 
     // float mist_factor = max(1 - (f_pos.z + (texture(t_noise, f_pos.xy * 0.0005 + time_of_day.x * 0.0003).x - 0.5) * 128.0) / 400.0, 0.0);
     // //float mist_factor = f_norm.z * 2.0;

From d0894a189e9a190cc117cf3b7d3d969cb526f671 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 22 Oct 2022 16:37:25 +0100
Subject: [PATCH 23/28] Added SSR to wet surfaces

---
 assets/voxygen/shaders/clouds-frag.glsl      |  8 +++-----
 assets/voxygen/shaders/figure-frag.glsl      | 21 +++++++++++---------
 assets/voxygen/shaders/lod-terrain-frag.glsl |  4 +++-
 assets/voxygen/shaders/terrain-frag.glsl     | 10 ++++++++--
 4 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index c5749efd51..8de4c2dd88 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -90,7 +90,6 @@ void main() {
 
     // Apply clouds
     float cloud_blend = 1.0;
-    bool is_reflection = false;
     if (color.a < 1.0 && medium.x != MEDIUM_WATER) {
         cloud_blend = 1.0 - color.a;
 
@@ -155,14 +154,13 @@ void main() {
                     clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.0, 0, 1),
                     // Depth merge factor
                     clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0)
-                ) * 0.85;
+                );
 
                 if (merge > 0.0) {
                     vec3 new_col = texture(sampler2D(t_src_color, s_src_color), new_uv).rgb;
                     new_col = get_cloud_color(new_col.rgb, refl_dir, wpos, time_of_day.x, distance(new_wpos, wpos.xyz), 1.0);
-                    color.rgb = mix(color.rgb, new_col, merge);
+                    color.rgb = mix(color.rgb, new_col, merge * (1.0 - color.a));
                     cloud_blend = 1;
-                    is_reflection = true;
                 } else {
                     cloud_blend = 1;
                 }
@@ -180,7 +178,7 @@ void main() {
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
         color.rgb = apply_point_glow(cam_pos.xyz + focus_off.xyz, dir, dist, color.rgb);
     #else
-        if (medium.x == MEDIUM_AIR && rain_density > 0.001 && !is_reflection) {
+        if (medium.x == MEDIUM_AIR && rain_density > 0.001) {
             vec3 cam_wpos = cam_pos.xyz + focus_off.xyz;
 
             vec3 adjusted_dir = (vec4(dir, 0) * rain_dir_mat).xyz;
diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl
index 92d222f0da..e9c49300e2 100644
--- a/assets/voxygen/shaders/figure-frag.glsl
+++ b/assets/voxygen/shaders/figure-frag.glsl
@@ -165,16 +165,19 @@ void main() {
 
     vec3 surf_color;
     // If the figure is large enough to be 'terrain-like', we apply a noise effect to it
-    if (scale >= 0.5) {
-        float noise = hash(vec4(floor(m_pos * 3.0 - f_norm * 0.5), 0));
+    #ifndef EXPERIMENTAL_NONOISE
+        if (scale >= 0.5) {
+            float noise = hash(vec4(floor(m_pos * 3.0 - f_norm * 0.5), 0));
 
-        const float A = 0.055;
-        const float W_INV = 1 / (1 + A);
-        const float W_2 = W_INV * W_INV;
-        const float NOISE_FACTOR = 0.015;
-        vec3 noise_delta = (sqrt(f_col) * W_INV + noise * NOISE_FACTOR);
-        surf_color = noise_delta * noise_delta * W_2;
-    } else {
+            const float A = 0.055;
+            const float W_INV = 1 / (1 + A);
+            const float W_2 = W_INV * W_INV;
+            const float NOISE_FACTOR = 0.015;
+            vec3 noise_delta = (sqrt(f_col) * W_INV + noise * NOISE_FACTOR);
+            surf_color = noise_delta * noise_delta * W_2;
+        } else
+    #endif
+    {
         surf_color = f_col;
     }
 
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index 7669b66c83..6384ae12f1 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -672,8 +672,10 @@ void main() {
             const vec3 underwater_col = vec3(0.0);
             float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
             surf_color = mix(underwater_col, surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));
+            surf_alpha = passthrough;
+        #else
+            surf_alpha = 0.5;
         #endif
-        surf_alpha = 0.99;
     } else {
         surf_color = illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light);
     }
diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index 8d86b6bd47..6b6b85e857 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -234,6 +234,11 @@ void main() {
     // Toggle to see rain_occlusion
     // tgt_color = vec4(rain_occlusion_at(f_pos.xyz), 0.0, 0.0, 1.0);
     // return;
+    #ifdef EXPERIMENTAL_WETNESS
+        float f_alpha = 1.0;
+    #else
+        const float f_alpha = 1.0;
+    #endif
     #if (CLOUD_MODE != CLOUD_MODE_NONE)
         if (rain_density > 0 && !faces_fluid && f_norm.z > 0.5) {
             vec3 pos = f_pos + focus_off.xyz;
@@ -247,13 +252,14 @@ void main() {
             #ifdef EXPERIMENTAL_WETNESS
                 float puddle = clamp((noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.03) - 0.5) * 20.0, 0.0, 1.0)
                     * min(rain_density * 10.0, 1.0)
-                    * clamp((f_sky_exposure - 0.9) * 50.0, 0.0, 1.0);
+                    * clamp((f_sky_exposure - 0.95) * 50.0, 0.0, 1.0);
             #else
                 const float puddle = 1.0;
             #endif
 
             #ifdef EXPERIMENTAL_WETNESS
                 if (puddle > 0.0) {
+                    f_alpha = 1.0 - puddle * 0.1;
                     float h = (noise_2d((f_pos.xy + focus_off.xy) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
                         + (noise_2d((f_pos.xy + focus_off.xy) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
                     float hx = (noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
@@ -525,5 +531,5 @@ void main() {
     float f_select = (select_pos.w > 0 && select_pos.xyz == floor(f_pos - f_norm * 0.5)) ? 1.0 : 0.0;
     surf_color += f_select * (surf_color + 0.1) * vec3(0.5, 0.5, 0.5);
 
-    tgt_color = vec4(surf_color, 1.0);
+    tgt_color = vec4(surf_color, f_alpha);
 }

From c072dc4082a885b308cba96d36de16106561e686 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 22 Oct 2022 17:09:23 +0100
Subject: [PATCH 24/28] Place puddle details behind extra experimental shader

---
 assets/voxygen/shaders/terrain-frag.glsl | 24 +++++++++++++-----------
 voxygen/src/render/mod.rs                |  9 ++++++---
 2 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index 6b6b85e857..e1e32966fa 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -234,7 +234,7 @@ void main() {
     // Toggle to see rain_occlusion
     // tgt_color = vec4(rain_occlusion_at(f_pos.xyz), 0.0, 0.0, 1.0);
     // return;
-    #ifdef EXPERIMENTAL_WETNESS
+    #ifdef EXPERIMENTAL_PUDDLES
         float f_alpha = 1.0;
     #else
         const float f_alpha = 1.0;
@@ -249,7 +249,7 @@ void main() {
             drop_pos.z *= 0.5 + hash_fast(uvec3(cell2d, 0));
             vec3 cell = vec3(cell2d, floor(drop_pos.z * drop_density.z));
 
-            #ifdef EXPERIMENTAL_WETNESS
+            #ifdef EXPERIMENTAL_PUDDLES
                 float puddle = clamp((noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.03) - 0.5) * 20.0, 0.0, 1.0)
                     * min(rain_density * 10.0, 1.0)
                     * clamp((f_sky_exposure - 0.95) * 50.0, 0.0, 1.0);
@@ -257,16 +257,18 @@ void main() {
                 const float puddle = 1.0;
             #endif
 
-            #ifdef EXPERIMENTAL_WETNESS
+            #ifdef EXPERIMENTAL_PUDDLES
                 if (puddle > 0.0) {
                     f_alpha = 1.0 - puddle * 0.1;
-                    float h = (noise_2d((f_pos.xy + focus_off.xy) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
-                        + (noise_2d((f_pos.xy + focus_off.xy) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
-                    float hx = (noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
-                        + (noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
-                    float hy = (noise_2d((f_pos.xy + focus_off.xy + vec2(0, 0.1)) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
-                        + (noise_2d((f_pos.xy + focus_off.xy + vec2(0, 0.1)) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
-                    f_norm.xy += mix(vec2(0), vec2(h - hx, h - hy) / 0.1 * 0.03, puddle);
+                    #ifdef EXPERIMENTAL_PUDDLEDETAILS
+                        float h = (noise_2d((f_pos.xy + focus_off.xy) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
+                            + (noise_2d((f_pos.xy + focus_off.xy) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
+                        float hx = (noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
+                            + (noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
+                        float hy = (noise_2d((f_pos.xy + focus_off.xy + vec2(0, 0.1)) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
+                            + (noise_2d((f_pos.xy + focus_off.xy + vec2(0, 0.1)) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
+                        f_norm.xy += mix(vec2(0), vec2(h - hx, h - hy) / 0.1 * 0.03, puddle);
+                    #endif
                     alpha = mix(1.0, 0.2, puddle);
                     f_col.rgb *= mix(1.0, 0.7, puddle);
                     k_s = mix(k_s, vec3(0.7, 0.7, 1.0), puddle);
@@ -285,7 +287,7 @@ void main() {
                     k_d += distort;
                     k_s += distort;
 
-                    #ifdef EXPERIMENTAL_WETNESS
+                    #ifdef EXPERIMENTAL_PUDDLES
                         /* puddle = mix(puddle, 1.0, distort * 10); */
                     #endif
 
diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs
index b81b7189b5..8dfb307538 100644
--- a/voxygen/src/render/mod.rs
+++ b/voxygen/src/render/mod.rs
@@ -498,10 +498,13 @@ pub enum ExperimentalShader {
     DirectionalShadowMapTexelGrid,
     /// Disable rainbows
     NoRainbows,
-    /// Make objects appear wet when appropriate.
-    Wetness,
+    /// Make the ground appear wet when appropriate.
+    Puddles,
+    /// Add extra detailing to puddles (requires [`Puddles`]).
+    PuddleDetails,
     /// Add screen-space reflections to water.
     ScreenSpaceReflections,
-    /// Use screen-space raycasting for reflections.
+    /// Use screen-space raycasting for reflections (requires
+    /// [`ScreenSpaceReflections`]).
     ScreenSpaceReflectionsCasting,
 }

From b85fd11443e58c426fa4d5fa3e597a0fc9aea59a Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sun, 23 Oct 2022 15:15:25 +0100
Subject: [PATCH 25/28] Added refraction

---
 CHANGELOG.md                                 |   2 +-
 assets/voxygen/shaders/clouds-frag.glsl      | 171 +++++++++++--------
 assets/voxygen/shaders/include/sky.glsl      |   2 +-
 assets/voxygen/shaders/lod-terrain-frag.glsl |   4 +-
 assets/voxygen/shaders/terrain-frag.glsl     |   6 +-
 voxygen/src/render/mod.rs                    |   2 +
 6 files changed, 110 insertions(+), 77 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d1c274292f..6a74cb9578 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Pets can now be traded with.
 - Crafting recipe for black lantern
 - Added redwood and dead trees
-- Experimental screen-space reflection shader
+- Experimental screen-space reflection and refraction shaders
 - Water will now move according to its apparent flow direction
 
 ### Changed
diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 8de4c2dd88..713249559f 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -90,86 +90,117 @@ void main() {
 
     // Apply clouds
     float cloud_blend = 1.0;
-    if (color.a < 1.0 && medium.x != MEDIUM_WATER) {
-        cloud_blend = 1.0 - color.a;
+    if (color.a < 1.0) {
+        vec2 nz = vec2(0);
+        #ifdef EXPERIMENTAL_SCREENSPACEREFRACTION
+            nz = (vec2(
+                noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.2 + wpos.x * 0.01)).x,
+                noise_3d(vec3((wpos.yx + focus_off.yx) * 0.1, tick.x * 0.2 + wpos.y * 0.01)).x
+            ) - 0.5) * color.a;
 
-        #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONS
-            if (dir.z < 0.0) {
-                #if (FLUID_MODE == FLUID_MODE_CHEAP)
-                    vec2 nz = vec2(0);
-                #else
-                    vec2 nz = (vec2(
+            const float n2 = 1.3325;
+            vec3 refr_dir;
+            // TODO: Proper refraction
+            // if (medium.x == MEDIUM_WATER) {
+            //     vec3 surf_norm = normalize(vec3(nz * 0.03 / (1.0 + dist * 0.1), 1));
+            //     refr_dir = refract(dir, surf_norm * -sign(dir.z), 1.0 / n2);
+            // } else {
+                refr_dir = normalize(dir + vec3(nz / dist, 0.0));
+            // }
+
+            vec4 clip = (all_mat * vec4(cam_pos.xyz + refr_dir, 1.0));
+            vec2 new_uv = (clip.xy / max(clip.w, 0)) * 0.5 * vec2(1, -1) + 0.5;
+
+            float uv_merge = clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 5.0, 0, 1);
+
+            uvec2 sz = textureSize(sampler2D(t_src_color, s_src_color), 0);
+            vec4 new_col = texelFetch(sampler2D(t_src_color, s_src_color), clamp(ivec2(mix(uv, new_uv, uv_merge) * sz), ivec2(0), ivec2(sz) - 1), 0);
+            if (new_col.a < 1.0) {
+                color = new_col;
+            }
+        #else
+            #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
+                if (dir.z < 0.0) {
+                    nz = (vec2(
                         noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.2 + wpos.x * 0.01)).x,
                         noise_3d(vec3((wpos.yx + focus_off.yx) * 0.1, tick.x * 0.2 + wpos.y * 0.01)).x
-                    ) - 0.5) * 5.0;
-                #endif
-                vec3 surf_norm = normalize(vec3(nz * 0.03 / (1.0 + dist * 0.1), 1));
-                vec3 refl_dir = reflect(dir, surf_norm);
+                    ) - 0.5);
+                }
+            #endif
+        #endif
+            {
+            cloud_blend = 1.0 - color.a;
 
-                vec4 clip = (all_mat * vec4(cam_pos.xyz + refl_dir, 1.0));
-                vec2 new_uv = (clip.xy / max(clip.w, 0)) * 0.5 * vec2(1, -1) + 0.5;
+            #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONS
+                if (dir.z < 0.0) {
+                    vec3 surf_norm = normalize(vec3(nz * 0.3 / (1.0 + dist * 0.1), 1));
+                    vec3 refl_dir = reflect(dir, surf_norm);
 
-                #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONSCASTING
-                    vec3 ray_end = wpos + refl_dir * 5.0 * dist;
-                    // Trace through the screen-space depth buffer to find the ray intersection
-                    const int MAIN_ITERS = 64;
-                    for (int i = 0; i < MAIN_ITERS; i ++) {
-                        float t = float(i) / float(MAIN_ITERS);
-                        // TODO: Trace in screen space, not world space
-                        vec3 swpos = mix(wpos, ray_end, t);
-                        vec3 svpos = (view_mat * vec4(swpos, 1)).xyz;
-                        vec4 clippos = proj_mat * vec4(svpos, 1);
-                        vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
-                        float d = -depth_at(suv);
-                        if (d < svpos.z * 0.8 && d > svpos.z * 0.999) {
-                            // Don't cast into water!
-                            if (texture(sampler2D(t_src_color, s_src_color), suv).a >= 1.0) {
-                                /* t -= 1.0 / float(MAIN_ITERS); */
-                                // Do a bit of extra iteration to try to refine the estimate
-                                const int ITERS = 8;
-                                float diff = 1.0 / float(MAIN_ITERS);
-                                for (int i = 0; i < ITERS; i ++) {
-                                    vec3 swpos = mix(wpos, ray_end, t);
-                                    svpos = (view_mat * vec4(swpos, 1)).xyz;
-                                    vec4 clippos = proj_mat * vec4(svpos, 1);
-                                    suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
-                                    float d = -depth_at(suv);
-                                    t += ((d > svpos.z * 0.999) ? -1.0 : 1.0) * diff;
-                                    diff *= 0.5;
+                    vec4 clip = (all_mat * vec4(cam_pos.xyz + refl_dir, 1.0));
+                    vec2 new_uv = (clip.xy / max(clip.w, 0)) * 0.5 * vec2(1, -1) + 0.5;
+
+                    #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONSCASTING
+                        vec3 ray_end = wpos + refl_dir * 5.0 * dist;
+                        // Trace through the screen-space depth buffer to find the ray intersection
+                        const int MAIN_ITERS = 64;
+                        for (int i = 0; i < MAIN_ITERS; i ++) {
+                            float t = float(i) / float(MAIN_ITERS);
+                            // TODO: Trace in screen space, not world space
+                            vec3 swpos = mix(wpos, ray_end, t);
+                            vec3 svpos = (view_mat * vec4(swpos, 1)).xyz;
+                            vec4 clippos = proj_mat * vec4(svpos, 1);
+                            vec2 suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                            float d = -depth_at(suv);
+                            if (d < svpos.z * 0.8 && d > svpos.z * 0.999) {
+                                // Don't cast into water!
+                                if (texture(sampler2D(t_src_color, s_src_color), suv).a >= 1.0) {
+                                    /* t -= 1.0 / float(MAIN_ITERS); */
+                                    // Do a bit of extra iteration to try to refine the estimate
+                                    const int ITERS = 8;
+                                    float diff = 1.0 / float(MAIN_ITERS);
+                                    for (int i = 0; i < ITERS; i ++) {
+                                        vec3 swpos = mix(wpos, ray_end, t);
+                                        svpos = (view_mat * vec4(swpos, 1)).xyz;
+                                        vec4 clippos = proj_mat * vec4(svpos, 1);
+                                        suv = (clippos.xy / clippos.w) * 0.5 * vec2(1, -1) + 0.5;
+                                        float d = -depth_at(suv);
+                                        t += ((d > svpos.z * 0.999) ? -1.0 : 1.0) * diff;
+                                        diff *= 0.5;
+                                    }
+                                    // Small offset to push us into obscured territory
+                                    new_uv = suv - vec2(0, 0.001);
+                                    break;
                                 }
-                                // Small offset to push us into obscured territory
-                                new_uv = suv - vec2(0, 0.001);
-                                break;
                             }
                         }
+                    #endif
+
+                    new_uv = clamp(new_uv, vec2(0), vec2(1));
+
+                    vec3 new_wpos = wpos_at(new_uv);
+                    float new_dist = distance(new_wpos, cam_pos.xyz);
+                    float merge = min(
+                        // Off-screen merge factor
+                        clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.0, 0, 1),
+                        // Depth merge factor
+                        clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0)
+                    );
+
+                    if (merge > 0.0) {
+                        vec3 new_col = texture(sampler2D(t_src_color, s_src_color), new_uv).rgb;
+                        new_col = get_cloud_color(new_col.rgb, refl_dir, wpos, time_of_day.x, distance(new_wpos, wpos.xyz), 1.0);
+                        color.rgb = mix(color.rgb, new_col, merge * color.a);
+                        cloud_blend = 1;
+                    } else {
+                        cloud_blend = 1;
                     }
-                #endif
-
-                new_uv = clamp(new_uv, vec2(0), vec2(1));
-
-                vec3 new_wpos = wpos_at(new_uv);
-                float new_dist = distance(new_wpos, cam_pos.xyz);
-                float merge = min(
-                    // Off-screen merge factor
-                    clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 3.0, 0, 1),
-                    // Depth merge factor
-                    clamp((new_dist - dist * 0.5) / (dist * 0.5), 0.0, 1.0)
-                );
-
-                if (merge > 0.0) {
-                    vec3 new_col = texture(sampler2D(t_src_color, s_src_color), new_uv).rgb;
-                    new_col = get_cloud_color(new_col.rgb, refl_dir, wpos, time_of_day.x, distance(new_wpos, wpos.xyz), 1.0);
-                    color.rgb = mix(color.rgb, new_col, merge * (1.0 - color.a));
-                    cloud_blend = 1;
                 } else {
-                    cloud_blend = 1;
-                }
-            } else {
-        #else
-            {
-        #endif
-            cloud_blend = 1;
-            //dist = DIST_CAP;
+            #else
+                {
+            #endif
+                cloud_blend = 1;
+                //dist = DIST_CAP;
+            }
         }
     }
     /* color.rgb = vec3(sin(depth_at(uv) * 3.14159 * 2) * 0.5 + 0.5); */
diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl
index 1958d31eb3..76d5e40c1d 100644
--- a/assets/voxygen/shaders/include/sky.glsl
+++ b/assets/voxygen/shaders/include/sky.glsl
@@ -503,7 +503,7 @@ float is_star_at(vec3 dir) {
 
     //return 0.0;
 
-    return 5.0 / (1.0 + pow(dist * 750, 8));
+    return 50.0 * max(sun_dir.z, 0.1) / (1.0 + pow(dist * 750, 8));
 }
 
 vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) {
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index 6384ae12f1..26860b6e2e 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -672,9 +672,9 @@ void main() {
             const vec3 underwater_col = vec3(0.0);
             float min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
             surf_color = mix(underwater_col, surf_color, (1.0 - passthrough) * 1.0 / (1.0 + min_refl));
-            surf_alpha = passthrough;
+            surf_alpha = 1.0 - passthrough;
         #else
-            surf_alpha = 0.5;
+            surf_alpha = 0.9;
         #endif
     } else {
         surf_color = illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light);
diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index e1e32966fa..49703530aa 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -259,7 +259,7 @@ void main() {
 
             #ifdef EXPERIMENTAL_PUDDLES
                 if (puddle > 0.0) {
-                    f_alpha = 1.0 - puddle * 0.1;
+                    f_alpha = puddle * 0.3 * max(1.0 + cam_to_frag.z, 0.3);
                     #ifdef EXPERIMENTAL_PUDDLEDETAILS
                         float h = (noise_2d((f_pos.xy + focus_off.xy) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
                             + (noise_2d((f_pos.xy + focus_off.xy) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
@@ -276,12 +276,12 @@ void main() {
             #endif
 
             if (rain_occlusion_at(f_pos.xyz + vec3(0, 0, 0.25)) > 0.5) {
-                if (fract(hash(fract(vec4(cell, 0) * 0.01))) < rain_density * 2.0 && puddle > 0.3) {
+                if (fract(hash(fract(vec4(cell, 0) * 0.01))) < rain_density * 2.0) {
                     vec3 off = vec3(hash_fast(uvec3(cell * 13)), hash_fast(uvec3(cell * 5)), 0);
                     vec3 near_cell = (cell + 0.5 + (off - 0.5) * 0.5) / drop_density;
 
                     float dist = length((drop_pos - near_cell) / vec3(1, 1, 2));
-                    float drop_rad = 0.1;
+                    float drop_rad = 0.075 + puddle * 0.05;
                     float distort = max(1.0 - abs(dist - drop_rad) * 100, 0) * 1.5 * max(drop_pos.z - near_cell.z, 0);
                     k_a += distort;
                     k_d += distort;
diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs
index 8dfb307538..f32c0eb4c9 100644
--- a/voxygen/src/render/mod.rs
+++ b/voxygen/src/render/mod.rs
@@ -507,4 +507,6 @@ pub enum ExperimentalShader {
     /// Use screen-space raycasting for reflections (requires
     /// [`ScreenSpaceReflections`]).
     ScreenSpaceReflectionsCasting,
+    /// Add screen-space refractions to water.
+    ScreenSpaceRefraction,
 }

From 40e47b4f3e50d6758582c14e90fa0187fe7bda6c Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sun, 23 Oct 2022 20:03:21 +0100
Subject: [PATCH 26/28] Added reflection mode settings

---
 CHANGELOG.md                                  |   3 +-
 assets/voxygen/i18n/en/hud/settings.ftl       |   6 +-
 assets/voxygen/shaders/clouds-frag.glsl       |  39 ++---
 assets/voxygen/shaders/clouds-vert.glsl       |   2 +-
 assets/voxygen/shaders/figure-frag.glsl       |   4 +-
 assets/voxygen/shaders/fluid-frag/cheap.glsl  |   4 +-
 assets/voxygen/shaders/fluid-frag/shiny.glsl  |  22 ++-
 assets/voxygen/shaders/fluid-vert.glsl        |   2 +-
 .../shaders/include/cloud/regular.glsl        | 158 +++++++++---------
 assets/voxygen/shaders/include/constants.glsl |   6 +-
 assets/voxygen/shaders/include/lod.glsl       |   2 +-
 assets/voxygen/shaders/include/sky.glsl       |  22 ++-
 .../shaders/light-shadows-directed-vert.glsl  |   2 +-
 .../shaders/light-shadows-figure-vert.glsl    |   2 +-
 .../voxygen/shaders/light-shadows-frag.glsl   |   2 +-
 .../voxygen/shaders/light-shadows-geom.glsl   |   2 +-
 .../voxygen/shaders/light-shadows-vert.glsl   |   2 +-
 assets/voxygen/shaders/lod-object-frag.glsl   |   4 +-
 assets/voxygen/shaders/lod-terrain-frag.glsl  |   4 +-
 assets/voxygen/shaders/lod-terrain-vert.glsl  |   2 +-
 assets/voxygen/shaders/particle-frag.glsl     |   4 +-
 .../shaders/point-light-shadows-vert.glsl     |   2 +-
 assets/voxygen/shaders/postprocess-frag.glsl  |   4 +-
 assets/voxygen/shaders/postprocess-vert.glsl  |   2 +-
 .../shaders/rain-occlusion-directed-vert.glsl |   2 +-
 .../shaders/rain-occlusion-figure-vert.glsl   |   2 +-
 assets/voxygen/shaders/skybox-frag.glsl       |   4 +-
 assets/voxygen/shaders/skybox-vert.glsl       |   2 +-
 assets/voxygen/shaders/sprite-frag.glsl       |   4 +-
 assets/voxygen/shaders/terrain-frag.glsl      |  20 +--
 assets/voxygen/shaders/terrain-vert.glsl      |   2 +-
 assets/voxygen/shaders/trail-frag.glsl        |   2 +-
 voxygen/src/hud/settings_window/video.rs      |  53 +++++-
 voxygen/src/render/mod.rs                     |  44 +++--
 .../src/render/renderer/pipeline_creation.rs  |  14 +-
 35 files changed, 257 insertions(+), 194 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a74cb9578..63584f78d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,8 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Pets can now be traded with.
 - Crafting recipe for black lantern
 - Added redwood and dead trees
-- Experimental screen-space reflection and refraction shaders
 - Water will now move according to its apparent flow direction
+- Added screen-space reflection and refraction shaders
+- Added reflection quality setting
 
 ### Changed
 - Use fluent for translations
diff --git a/assets/voxygen/i18n/en/hud/settings.ftl b/assets/voxygen/i18n/en/hud/settings.ftl
index 9dcf8e2988..847e428fc9 100644
--- a/assets/voxygen/i18n/en/hud/settings.ftl
+++ b/assets/voxygen/i18n/en/hud/settings.ftl
@@ -72,9 +72,13 @@ hud-settings-antialiasing_mode = AntiAliasing Mode
 hud-settings-upscale_factor = Internal Resolution
 hud-settings-cloud_rendering_mode = Cloud Rendering Mode
 hud-settings-fluid_rendering_mode = Fluid Rendering Mode
-hud-settings-fluid_rendering_mode-cheap = Cheap
+hud-settings-fluid_rendering_mode-low = Low
 hud-settings-fluid_rendering_mode-medium = Medium
 hud-settings-fluid_rendering_mode-high = High
+hud-settings-reflection_rendering_mode = Reflection Rendering Mode
+hud-settings-reflection_rendering_mode-low = Low
+hud-settings-reflection_rendering_mode-medium = Medium
+hud-settings-reflection_rendering_mode-high = High
 hud-settings-cloud_rendering_mode-minimal = Minimal
 hud-settings-cloud_rendering_mode-low = Low
 hud-settings-cloud_rendering_mode-medium = Medium
diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 713249559f..9ae5009df5 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -92,11 +92,12 @@ void main() {
     float cloud_blend = 1.0;
     if (color.a < 1.0) {
         vec2 nz = vec2(0);
-        #ifdef EXPERIMENTAL_SCREENSPACEREFRACTION
+        uvec2 col_sz = textureSize(sampler2D(t_src_color, s_src_color), 0);
+        #if (REFLECTION_MODE >= REFLECTION_MODE_MEDIUM)
             nz = (vec2(
                 noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.2 + wpos.x * 0.01)).x,
                 noise_3d(vec3((wpos.yx + focus_off.yx) * 0.1, tick.x * 0.2 + wpos.y * 0.01)).x
-            ) - 0.5) * color.a;
+            ) - 0.5) * (dir.z < 0.0 ? color.a : 1.0);
 
             const float n2 = 1.3325;
             vec3 refr_dir;
@@ -105,33 +106,25 @@ void main() {
             //     vec3 surf_norm = normalize(vec3(nz * 0.03 / (1.0 + dist * 0.1), 1));
             //     refr_dir = refract(dir, surf_norm * -sign(dir.z), 1.0 / n2);
             // } else {
-                refr_dir = normalize(dir + vec3(nz / dist, 0.0));
+                refr_dir = normalize(dir + vec3(nz * 1.5 / dist, 0.0));
             // }
 
             vec4 clip = (all_mat * vec4(cam_pos.xyz + refr_dir, 1.0));
             vec2 new_uv = (clip.xy / max(clip.w, 0)) * 0.5 * vec2(1, -1) + 0.5;
 
             float uv_merge = clamp((1.0 - abs(new_uv.y - 0.5) * 2) * 5.0, 0, 1);
+            new_uv = mix(uv, new_uv, uv_merge);
 
-            uvec2 sz = textureSize(sampler2D(t_src_color, s_src_color), 0);
-            vec4 new_col = texelFetch(sampler2D(t_src_color, s_src_color), clamp(ivec2(mix(uv, new_uv, uv_merge) * sz), ivec2(0), ivec2(sz) - 1), 0);
+            vec4 new_col = texelFetch(sampler2D(t_src_color, s_src_color), clamp(ivec2(new_uv * col_sz), ivec2(0), ivec2(col_sz) - 1), 0);
             if (new_col.a < 1.0) {
                 color = new_col;
+                dir = refr_dir;
             }
-        #else
-            #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
-                if (dir.z < 0.0) {
-                    nz = (vec2(
-                        noise_3d(vec3((wpos.xy + focus_off.xy) * 0.1, tick.x * 0.2 + wpos.x * 0.01)).x,
-                        noise_3d(vec3((wpos.yx + focus_off.yx) * 0.1, tick.x * 0.2 + wpos.y * 0.01)).x
-                    ) - 0.5);
-                }
-            #endif
         #endif
             {
             cloud_blend = 1.0 - color.a;
 
-            #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONS
+            #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
                 if (dir.z < 0.0) {
                     vec3 surf_norm = normalize(vec3(nz * 0.3 / (1.0 + dist * 0.1), 1));
                     vec3 refl_dir = reflect(dir, surf_norm);
@@ -139,7 +132,7 @@ void main() {
                     vec4 clip = (all_mat * vec4(cam_pos.xyz + refl_dir, 1.0));
                     vec2 new_uv = (clip.xy / max(clip.w, 0)) * 0.5 * vec2(1, -1) + 0.5;
 
-                    #ifdef EXPERIMENTAL_SCREENSPACEREFLECTIONSCASTING
+                    #if (REFLECTION_MODE >= REFLECTION_MODE_HIGH)
                         vec3 ray_end = wpos + refl_dir * 5.0 * dist;
                         // Trace through the screen-space depth buffer to find the ray intersection
                         const int MAIN_ITERS = 64;
@@ -153,7 +146,7 @@ void main() {
                             float d = -depth_at(suv);
                             if (d < svpos.z * 0.8 && d > svpos.z * 0.999) {
                                 // Don't cast into water!
-                                if (texture(sampler2D(t_src_color, s_src_color), suv).a >= 1.0) {
+                                if (texelFetch(sampler2D(t_src_color, s_src_color), clamp(ivec2(suv * col_sz), ivec2(0), ivec2(col_sz) - 1), 0).a >= 1.0) {
                                     /* t -= 1.0 / float(MAIN_ITERS); */
                                     // Do a bit of extra iteration to try to refine the estimate
                                     const int ITERS = 8;
@@ -187,23 +180,19 @@ void main() {
                     );
 
                     if (merge > 0.0) {
-                        vec3 new_col = texture(sampler2D(t_src_color, s_src_color), new_uv).rgb;
+                        vec3 new_col = texelFetch(sampler2D(t_src_color, s_src_color), clamp(ivec2(new_uv * col_sz), ivec2(0), ivec2(col_sz) - 1), 0).rgb;
                         new_col = get_cloud_color(new_col.rgb, refl_dir, wpos, time_of_day.x, distance(new_wpos, wpos.xyz), 1.0);
-                        color.rgb = mix(color.rgb, new_col, merge * color.a);
-                        cloud_blend = 1;
-                    } else {
-                        cloud_blend = 1;
+                        color.rgb = mix(color.rgb, new_col, min(merge * (color.a * 2.0), 1.0));
                     }
+                    cloud_blend = 1;
                 } else {
             #else
                 {
             #endif
                 cloud_blend = 1;
-                //dist = DIST_CAP;
             }
         }
     }
-    /* color.rgb = vec3(sin(depth_at(uv) * 3.14159 * 2) * 0.5 + 0.5); */
     color.rgb = mix(color.rgb, get_cloud_color(color.rgb, dir, cam_pos.xyz, time_of_day.x, dist, 1.0), cloud_blend);
 
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
diff --git a/assets/voxygen/shaders/clouds-vert.glsl b/assets/voxygen/shaders/clouds-vert.glsl
index 727e94b4a8..039af8a38d 100644
--- a/assets/voxygen/shaders/clouds-vert.glsl
+++ b/assets/voxygen/shaders/clouds-vert.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl
index e9c49300e2..4f8c83d112 100644
--- a/assets/voxygen/shaders/figure-frag.glsl
+++ b/assets/voxygen/shaders/figure-frag.glsl
@@ -8,7 +8,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -139,7 +139,7 @@ void main() {
     float moon_shade_frac = horizon_at(f_pos, moon_dir); */
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
-#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl
index 33d0b608e8..a0807cc9b5 100644
--- a/assets/voxygen/shaders/fluid-frag/cheap.glsl
+++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -114,7 +114,7 @@ void main() {
     vec3 moon_dir = get_moon_dir(time_of_day.x); */
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
-#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index c71c6b0b8c..79a930592c 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -80,14 +80,16 @@ vec4 wave_height(vec4 posx, vec4 posy) {
         float speed = 1.0;
         posx *= 0.2;
         posy *= 0.2;
-        const float drag_factor = 0.03;
-        const float iters = 21;
+        const float drag_factor = 0.035;
+        const int iters = 21;
+        const float scale = 25.0;
     #else
         float speed = 2.0;
         posx *= 0.3;
         posy *= 0.3;
         const float drag_factor = 0.04;
-        const float iters = 11;
+        const int iters = 11;
+        const float scale = 5.0;
     #endif
     const float iter_shift = (3.14159 * 2.0) / 7.3;
 
@@ -104,7 +106,7 @@ vec4 wave_height(vec4 posx, vec4 posy) {
         phase *= 1.2;
         speed += speed_per_iter;
     }
-    return w / ws * 5.0;
+    return w / ws * scale;
 }
 
 float wave_height_vel(vec2 pos) {
@@ -213,7 +215,7 @@ void main() {
     vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
-#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
@@ -266,7 +268,7 @@ void main() {
     // float shade_frac = /*1.0;*/sun_shade_frac + moon_shade_frac;
 
     vec3 reflect_color;
-    #if (FLUID_MODE == FLUID_MODE_HIGH)
+    #if (REFLECTION_MODE >= REFLECTION_MODE_MEDIUM)
         reflect_color = get_sky_color(ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, true, 1.0, true, sun_shade_frac);
         reflect_color = get_cloud_color(reflect_color, ray_dir, f_pos.xyz, time_of_day.x, 100000.0, 0.1);
     #else
@@ -408,7 +410,11 @@ void main() {
         min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
     } else {
         // Hack to make the opacity of the surface fade when underwater to avoid artifacts
-        opacity = min(sqrt(max(opacity, clamp((f_pos.z - cam_pos.z) * 0.05, 0.0, 1.0))), 1.0);
+        if (dot(refract_ray_dir, cam_to_frag) > 0.0) {
+            opacity = 0.99;
+        } else {
+            opacity = min(sqrt(max(opacity, clamp((f_pos.z - cam_pos.z) * 0.05, 0.0, 1.0))), 0.99);
+        }
     }
     vec4 color = vec4(surf_color, opacity);// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam)));
     // vec4 color = vec4(surf_color, mix(1.0, 1.0 / (1.0 + /*0.25 * *//*diffuse_light*/(/*f_light * point_shadow*/reflected_light_point)), passthrough));
diff --git a/assets/voxygen/shaders/fluid-vert.glsl b/assets/voxygen/shaders/fluid-vert.glsl
index b2878a3c46..16d3a12dfa 100644
--- a/assets/voxygen/shaders/fluid-vert.glsl
+++ b/assets/voxygen/shaders/fluid-vert.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/include/cloud/regular.glsl b/assets/voxygen/shaders/include/cloud/regular.glsl
index f3155a0416..1dca0d58d3 100644
--- a/assets/voxygen/shaders/include/cloud/regular.glsl
+++ b/assets/voxygen/shaders/include/cloud/regular.glsl
@@ -232,92 +232,86 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
     vec3 moon_color = get_moon_color();
 
     // Clouds aren't visible underwater
-    #ifdef IS_POSTPROCESS
-        if (medium.x != 1) {
-    #endif
-        float cdist = max_dist;
-        float ldist = cdist;
-        // i is an emergency brake
-        float min_dist = clamp(max_dist / 4, 0.25, 24);
-        int i;
+    float cdist = max_dist;
+    float ldist = cdist;
+    // i is an emergency brake
+    float min_dist = clamp(max_dist / 4, 0.25, 24);
+    int i;
 
-        #if (CLOUD_MODE >= CLOUD_MODE_MEDIUM)
+    #if (CLOUD_MODE >= CLOUD_MODE_MEDIUM)
+    #ifndef EXPERIMENTAL_NORAINBOWS
+        // TODO: Make it a double rainbow
+        float rainbow_t = (0.7 - dot(sun_dir.xyz, dir)) * 8 / 0.05;
+        int rainbow_c = int(floor(rainbow_t));
+        rainbow_t = fract(rainbow_t);
+        rainbow_t = rainbow_t * rainbow_t;
+    #endif
+    #endif
+
+    for (i = 0; cdist > min_dist && i < 250; i ++) {
+        ldist = cdist;
+        cdist = step_to_dist(trunc(dist_to_step(cdist - 0.25, quality)), quality);
+
+        vec3 emission;
+        float not_underground; // Used to prevent sunlight leaking underground
+        vec3 pos = origin + dir * ldist * splay;
+        // `sample` is a reserved keyword
+        vec4 sample_ = cloud_at(origin + dir * ldist * splay, ldist, emission, not_underground);
+
+        vec2 density_integrals = max(sample_.zw, vec2(0));
+
+        float sun_access = max(sample_.x, 0);
+        float moon_access = max(sample_.y, 0);
+        float cloud_scatter_factor = density_integrals.x;
+        float global_scatter_factor = density_integrals.y;
+
+        float step = (ldist - cdist) * 0.01;
+        float cloud_darken = pow(1.0 / (1.0 + cloud_scatter_factor), step);
+        float global_darken = pow(1.0 / (1.0 + global_scatter_factor), step);
+        // Proportion of light diffusely scattered instead of absorbed
+        float cloud_diffuse = 0.25;
+
+        surf_color =
+            // Attenuate light passing through the clouds
+            surf_color * cloud_darken * global_darken +
+            // Add the directed light light scattered into the camera by the clouds and the atmosphere (global illumination)
+            sun_color * sun_scatter * get_sun_brightness() * (sun_access * (1.0 - cloud_darken) * cloud_diffuse /*+ sky_color * global_scatter_factor*/) +
+            moon_color * moon_scatter * get_moon_brightness() * (moon_access * (1.0 - cloud_darken) * cloud_diffuse /*+ sky_color * global_scatter_factor*/) +
+            sky_light * (1.0 - global_darken) * not_underground +
+            // A small amount fake ambient light underground
+            (1.0 - not_underground) * vec3(0.2, 0.35, 0.5) * (1.0 - global_darken) / (1.0 + max_dist * 0.003) +
+            emission * density_integrals.y * step;
+
+        // Rainbow
+        #if (CLOUD_MODE >= CLOUD_MODE_ULTRA)
         #ifndef EXPERIMENTAL_NORAINBOWS
-            // TODO: Make it a double rainbow
-            float rainbow_t = (0.7 - dot(sun_dir.xyz, dir)) * 8 / 0.05;
-            int rainbow_c = int(floor(rainbow_t));
-            rainbow_t = fract(rainbow_t);
-            rainbow_t = rainbow_t * rainbow_t;
+            if (rainbow_c >= 0 && rainbow_c < 8) {
+                vec3 colors[9] = {
+                    surf_color,
+                    vec3(0.9, 0.5, 0.9),
+                    vec3(0.25, 0.0, 0.5),
+                    vec3(0.0, 0.0, 1.0),
+                    vec3(0.0, 0.5, 0.0),
+                    vec3(1.0, 1.0, 0.0),
+                    vec3(1.0, 0.6, 0.0),
+                    vec3(1.0, 0.0, 0.0),
+                    surf_color,
+                };
+                float h = max(0.0, min(pos.z, 900.0 - pos.z) / 450.0);
+                float rain = rain_density_at(pos.xy) * pow(h, 0.1);
+
+                float sun = sun_access * get_sun_brightness();
+                float energy = pow(rain * sun * min(cdist / 500.0, 1.0), 2.0) * 0.4;
+
+                surf_color = mix(
+                    surf_color,
+                    mix(colors[rainbow_c], colors[rainbow_c + 1], rainbow_t),
+                    energy
+                );
+            }
         #endif
         #endif
-
-        for (i = 0; cdist > min_dist && i < 250; i ++) {
-            ldist = cdist;
-            cdist = step_to_dist(trunc(dist_to_step(cdist - 0.25, quality)), quality);
-
-            vec3 emission;
-            float not_underground; // Used to prevent sunlight leaking underground
-            vec3 pos = origin + dir * ldist * splay;
-            // `sample` is a reserved keyword
-            vec4 sample_ = cloud_at(origin + dir * ldist * splay, ldist, emission, not_underground);
-
-            vec2 density_integrals = max(sample_.zw, vec2(0));
-
-            float sun_access = max(sample_.x, 0);
-            float moon_access = max(sample_.y, 0);
-            float cloud_scatter_factor = density_integrals.x;
-            float global_scatter_factor = density_integrals.y;
-
-            float step = (ldist - cdist) * 0.01;
-            float cloud_darken = pow(1.0 / (1.0 + cloud_scatter_factor), step);
-            float global_darken = pow(1.0 / (1.0 + global_scatter_factor), step);
-            // Proportion of light diffusely scattered instead of absorbed
-            float cloud_diffuse = 0.25;
-
-            surf_color =
-                // Attenuate light passing through the clouds
-                surf_color * cloud_darken * global_darken +
-                // Add the directed light light scattered into the camera by the clouds and the atmosphere (global illumination)
-                sun_color * sun_scatter * get_sun_brightness() * (sun_access * (1.0 - cloud_darken) * cloud_diffuse /*+ sky_color * global_scatter_factor*/) +
-                moon_color * moon_scatter * get_moon_brightness() * (moon_access * (1.0 - cloud_darken) * cloud_diffuse /*+ sky_color * global_scatter_factor*/) +
-                sky_light * (1.0 - global_darken) * not_underground +
-                // A small amount fake ambient light underground
-                (1.0 - not_underground) * vec3(0.2, 0.35, 0.5) * (1.0 - global_darken) / (1.0 + max_dist * 0.003) +
-                emission * density_integrals.y * step;
-
-            // Rainbow
-            #if (CLOUD_MODE >= CLOUD_MODE_ULTRA)
-            #ifndef EXPERIMENTAL_NORAINBOWS
-                if (rainbow_c >= 0 && rainbow_c < 8) {
-                    vec3 colors[9] = {
-                        surf_color,
-                        vec3(0.9, 0.5, 0.9),
-                        vec3(0.25, 0.0, 0.5),
-                        vec3(0.0, 0.0, 1.0),
-                        vec3(0.0, 0.5, 0.0),
-                        vec3(1.0, 1.0, 0.0),
-                        vec3(1.0, 0.6, 0.0),
-                        vec3(1.0, 0.0, 0.0),
-                        surf_color,
-                    };
-                    float h = max(0.0, min(pos.z, 900.0 - pos.z) / 450.0);
-                    float rain = rain_density_at(pos.xy) * pow(h, 0.1);
-
-                    float sun = sun_access * get_sun_brightness();
-                    float energy = pow(rain * sun * min(cdist / 500.0, 1.0), 2.0) * 0.4;
-
-                    surf_color = mix(
-                        surf_color,
-                        mix(colors[rainbow_c], colors[rainbow_c + 1], rainbow_t),
-                        energy
-                    );
-                }
-            #endif
-            #endif
-        }
-    #ifdef IS_POSTPROCESS
-        }
-    #endif
+    }
 
     // Underwater light attenuation
     surf_color = water_diffuse(surf_color, dir, max_dist);
diff --git a/assets/voxygen/shaders/include/constants.glsl b/assets/voxygen/shaders/include/constants.glsl
index 02bd473cb5..e727e49563 100644
--- a/assets/voxygen/shaders/include/constants.glsl
+++ b/assets/voxygen/shaders/include/constants.glsl
@@ -6,10 +6,14 @@
 #define VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT 0
 #define VOXYGEN_COMPUTATION_PREFERENCE_VERTEX 1
 
-#define FLUID_MODE_CHEAP 0
+#define FLUID_MODE_LOW 0
 #define FLUID_MODE_MEDIUM 1
 #define FLUID_MODE_HIGH 2
 
+#define REFLECTION_MODE_LOW 0
+#define REFLECTION_MODE_MEDIUM 1
+#define REFLECTION_MODE_HIGH 2
+
 #define CLOUD_MODE_NONE 0
 #define CLOUD_MODE_MINIMAL 1
 #define CLOUD_MODE_LOW 2
diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl
index 54ca47e900..782770044a 100644
--- a/assets/voxygen/shaders/include/lod.glsl
+++ b/assets/voxygen/shaders/include/lod.glsl
@@ -138,7 +138,7 @@ float alt_at(vec2 pos) {
 
 float alt_at_real(vec2 pos) {
     // Basic idea: only really need the real altitude for an accurate water height estimation, so if we are in the cheap shader take a shortcut.
-// #if (FLUID_MODE == FLUID_MODE_CHEAP)
+// #if (FLUID_MODE == FLUID_MODE_LOW)
 //  return alt_at(pos);
 // #elif (FLUID_MODE == FLUID_MODE_SHINY)
     return (/*round*/(textureBicubic16(t_alt, s_alt, pos_to_tex(pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z);
diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl
index 76d5e40c1d..46c3336668 100644
--- a/assets/voxygen/shaders/include/sky.glsl
+++ b/assets/voxygen/shaders/include/sky.glsl
@@ -503,7 +503,12 @@ float is_star_at(vec3 dir) {
 
     //return 0.0;
 
-    return 50.0 * max(sun_dir.z, 0.1) / (1.0 + pow(dist * 750, 8));
+    #if (CLOUD_MODE == CLOUD_MODE_NONE)
+        const float power = 5.0;
+    #else
+        const float power = 50.0;
+    #endif
+    return power * max(sun_dir.z, 0.1) / (1.0 + pow(dist * 750, 8));
 }
 
 vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) {
@@ -624,21 +629,22 @@ vec3 get_sky_color(vec3 dir, float time_of_day, vec3 origin, vec3 f_pos, float q
     vec3 moon_halo_color = MOON_HALO_COLOR;
 
     float moon_halo_power = 20.0;
+
+    vec3 moon_surf = vec3(0);
+    if (with_features) {
+        float angle = 0.00035;
+        moon_surf = clamp((dot(dir, -moon_dir) - (1.0 - angle)) * 4 / angle, 0, 1) * MOON_SURF_COLOR;
+    }
     #if (CLOUD_MODE == CLOUD_MODE_NONE)
         if (true) {
     #else
         if (fake_clouds || medium.x == MEDIUM_WATER) {
     #endif
         moon_halo_power = 50.0;
-        moon_halo_color *= 0.02;
+        moon_halo_color *= 0.2;
+        moon_surf *= 0.05;
     }
-
     vec3 moon_halo = moon_halo_color * pow(max(dot(dir, -moon_dir), 0), moon_halo_power);
-    vec3 moon_surf = vec3(0);
-    if (with_features) {
-        float angle = 0.00035;
-        moon_surf = clamp((dot(dir, -moon_dir) - (1.0 - angle)) * 4 / angle, 0, 1) * MOON_SURF_COLOR;
-    }
     vec3 moon_light = moon_halo + moon_surf;
 
     // Replaced all clamp(sun_dir, 0, 1) with max(sun_dir, 0) because sun_dir is calculated from sin and cos, which are never > 1
diff --git a/assets/voxygen/shaders/light-shadows-directed-vert.glsl b/assets/voxygen/shaders/light-shadows-directed-vert.glsl
index 7e2345f649..38486aa808 100644
--- a/assets/voxygen/shaders/light-shadows-directed-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-directed-vert.glsl
@@ -7,7 +7,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/light-shadows-figure-vert.glsl b/assets/voxygen/shaders/light-shadows-figure-vert.glsl
index 0f44dddfaf..c48240c1b3 100644
--- a/assets/voxygen/shaders/light-shadows-figure-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-figure-vert.glsl
@@ -9,7 +9,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/light-shadows-frag.glsl b/assets/voxygen/shaders/light-shadows-frag.glsl
index 8adbcae038..d6aded48d7 100644
--- a/assets/voxygen/shaders/light-shadows-frag.glsl
+++ b/assets/voxygen/shaders/light-shadows-frag.glsl
@@ -11,7 +11,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/light-shadows-geom.glsl b/assets/voxygen/shaders/light-shadows-geom.glsl
index adf22792af..dbc2529c63 100644
--- a/assets/voxygen/shaders/light-shadows-geom.glsl
+++ b/assets/voxygen/shaders/light-shadows-geom.glsl
@@ -11,7 +11,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/light-shadows-vert.glsl b/assets/voxygen/shaders/light-shadows-vert.glsl
index 14ab18eb1b..dd98eeb8e9 100644
--- a/assets/voxygen/shaders/light-shadows-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-vert.glsl
@@ -7,7 +7,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/lod-object-frag.glsl b/assets/voxygen/shaders/lod-object-frag.glsl
index ddbfcf0c3d..9e445870b7 100644
--- a/assets/voxygen/shaders/lod-object-frag.glsl
+++ b/assets/voxygen/shaders/lod-object-frag.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -43,7 +43,7 @@ void main() {
 
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
-#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index 26860b6e2e..99509f4214 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -448,7 +448,7 @@ void main() {
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float shadow_alt = /*f_pos.z;*/alt_at(f_pos.xy);//max(alt_at(f_pos.xy), f_pos.z);
     // float shadow_alt = f_pos.z;
-#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float shadow_alt = f_pos.z;
 #endif
 
diff --git a/assets/voxygen/shaders/lod-terrain-vert.glsl b/assets/voxygen/shaders/lod-terrain-vert.glsl
index f55bf9a091..0cd3eccd11 100644
--- a/assets/voxygen/shaders/lod-terrain-vert.glsl
+++ b/assets/voxygen/shaders/lod-terrain-vert.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl
index 158eacf942..b810992ccc 100644
--- a/assets/voxygen/shaders/particle-frag.glsl
+++ b/assets/voxygen/shaders/particle-frag.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -44,7 +44,7 @@ void main() {
 
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
-#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
diff --git a/assets/voxygen/shaders/point-light-shadows-vert.glsl b/assets/voxygen/shaders/point-light-shadows-vert.glsl
index 3c200474c8..705f2efe8b 100644
--- a/assets/voxygen/shaders/point-light-shadows-vert.glsl
+++ b/assets/voxygen/shaders/point-light-shadows-vert.glsl
@@ -7,7 +7,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl
index bdd0dc74cb..89863db99b 100644
--- a/assets/voxygen/shaders/postprocess-frag.glsl
+++ b/assets/voxygen/shaders/postprocess-frag.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -288,7 +288,7 @@ void main() {
 
     vec4 final_color = aa_color;
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
     if (medium.x == MEDIUM_WATER) {
         final_color *= vec4(0.2, 0.2, 0.8, 1.0);
     }
diff --git a/assets/voxygen/shaders/postprocess-vert.glsl b/assets/voxygen/shaders/postprocess-vert.glsl
index de7998355b..f15ec174b2 100644
--- a/assets/voxygen/shaders/postprocess-vert.glsl
+++ b/assets/voxygen/shaders/postprocess-vert.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl b/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl
index 605f0a2dff..eab9446343 100644
--- a/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl
+++ b/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl
@@ -7,7 +7,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl b/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl
index f90d444e52..34edba46be 100644
--- a/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl
+++ b/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl
@@ -9,7 +9,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/skybox-frag.glsl b/assets/voxygen/shaders/skybox-frag.glsl
index 71af67f185..60930b469d 100644
--- a/assets/voxygen/shaders/skybox-frag.glsl
+++ b/assets/voxygen/shaders/skybox-frag.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -55,5 +55,5 @@ void main() {
     } */
     vec3 wpos = cam_pos.xyz + /*normalize(f_pos)*/cam_dir * dist;
 
-    tgt_color = vec4(cam_attenuation * get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, wpos, 1.0, medium.x != MEDIUM_WATER, refractionIndex), 1.0);
+    tgt_color = vec4(cam_attenuation * get_sky_color(normalize(f_pos), time_of_day.x, cam_pos.xyz, wpos, 1.0, true, refractionIndex, false, 1.0), 1.0);
 }
diff --git a/assets/voxygen/shaders/skybox-vert.glsl b/assets/voxygen/shaders/skybox-vert.glsl
index d4d34077ea..e410cd221f 100644
--- a/assets/voxygen/shaders/skybox-vert.glsl
+++ b/assets/voxygen/shaders/skybox-vert.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_SPECULAR
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/sprite-frag.glsl b/assets/voxygen/shaders/sprite-frag.glsl
index b4d5d93171..92d45dd6b2 100644
--- a/assets/voxygen/shaders/sprite-frag.glsl
+++ b/assets/voxygen/shaders/sprite-frag.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -53,7 +53,7 @@ void main() {
 
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
-#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index 49703530aa..b6b1a84b13 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -7,7 +7,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
@@ -211,7 +211,7 @@ void main() {
 
 #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE >= FLUID_MODE_MEDIUM)
     float f_alt = alt_at(f_pos.xy);
-#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
@@ -234,7 +234,7 @@ void main() {
     // Toggle to see rain_occlusion
     // tgt_color = vec4(rain_occlusion_at(f_pos.xyz), 0.0, 0.0, 1.0);
     // return;
-    #ifdef EXPERIMENTAL_PUDDLES
+    #if (REFLECTION_MODE >= REFLECTION_MODE_HIGH)
         float f_alpha = 1.0;
     #else
         const float f_alpha = 1.0;
@@ -249,17 +249,17 @@ void main() {
             drop_pos.z *= 0.5 + hash_fast(uvec3(cell2d, 0));
             vec3 cell = vec3(cell2d, floor(drop_pos.z * drop_density.z));
 
-            #ifdef EXPERIMENTAL_PUDDLES
-                float puddle = clamp((noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.03) - 0.5) * 20.0, 0.0, 1.0)
+            #if (REFLECTION_MODE >= REFLECTION_MODE_HIGH)
+                float puddle = clamp((noise_2d((f_pos.xy + focus_off.xy + vec2(0.1, 0)) * 0.02) - 0.5) * 20.0, 0.0, 1.0)
                     * min(rain_density * 10.0, 1.0)
                     * clamp((f_sky_exposure - 0.95) * 50.0, 0.0, 1.0);
             #else
                 const float puddle = 1.0;
             #endif
 
-            #ifdef EXPERIMENTAL_PUDDLES
+            #if (REFLECTION_MODE >= REFLECTION_MODE_HIGH)
                 if (puddle > 0.0) {
-                    f_alpha = puddle * 0.3 * max(1.0 + cam_to_frag.z, 0.3);
+                    f_alpha = puddle * 0.2 * max(1.0 + cam_to_frag.z, 0.3);
                     #ifdef EXPERIMENTAL_PUDDLEDETAILS
                         float h = (noise_2d((f_pos.xy + focus_off.xy) * 0.3) - 0.5) * sin(tick.x * 8.0 + f_pos.x * 3)
                             + (noise_2d((f_pos.xy + focus_off.xy) * 0.6) - 0.5) * sin(tick.x * 3.5 - f_pos.y * 6);
@@ -287,10 +287,6 @@ void main() {
                     k_d += distort;
                     k_s += distort;
 
-                    #ifdef EXPERIMENTAL_PUDDLES
-                        /* puddle = mix(puddle, 1.0, distort * 10); */
-                    #endif
-
                     f_norm.xy += (drop_pos - near_cell).xy
                         * max(1.0 - abs(dist - drop_rad) * 30, 0)
                         * 500.0
@@ -377,7 +373,7 @@ void main() {
     float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0);
 
     // To account for prior saturation
-    #if (FLUID_MODE == FLUID_MODE_CHEAP)
+    #if (FLUID_MODE == FLUID_MODE_LOW)
         f_light = f_light * sqrt(f_light);
     #else
         f_light = faces_fluid ? not_underground : f_light * sqrt(f_light);
diff --git a/assets/voxygen/shaders/terrain-vert.glsl b/assets/voxygen/shaders/terrain-vert.glsl
index 166caa87f3..55eea74c1b 100644
--- a/assets/voxygen/shaders/terrain-vert.glsl
+++ b/assets/voxygen/shaders/terrain-vert.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/assets/voxygen/shaders/trail-frag.glsl b/assets/voxygen/shaders/trail-frag.glsl
index f99d4c83a6..dc1ee7e232 100644
--- a/assets/voxygen/shaders/trail-frag.glsl
+++ b/assets/voxygen/shaders/trail-frag.glsl
@@ -6,7 +6,7 @@
 
 #define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
 
-#if (FLUID_MODE == FLUID_MODE_CHEAP)
+#if (FLUID_MODE == FLUID_MODE_LOW)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
 #elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs
index 47de730d48..7c9950adf5 100644
--- a/voxygen/src/hud/settings_window/video.rs
+++ b/voxygen/src/hud/settings_window/video.rs
@@ -7,7 +7,7 @@ use crate::{
     },
     render::{
         AaMode, BloomConfig, BloomFactor, BloomMode, CloudMode, FluidMode, LightingMode,
-        PresentMode, RenderMode, ShadowMapMode, ShadowMode, UpscaleMode,
+        PresentMode, ReflectionMode, RenderMode, ShadowMapMode, ShadowMode, UpscaleMode,
     },
     session::settings_change::Graphics as GraphicsChange,
     settings::Fps,
@@ -90,6 +90,8 @@ widget_ids! {
         cloud_mode_list,
         fluid_mode_text,
         fluid_mode_list,
+        reflection_mode_text,
+        reflection_mode_list,
         fullscreen_mode_text,
         fullscreen_mode_list,
         //
@@ -1038,10 +1040,10 @@ impl<'a> Widget for Video<'a> {
         .color(TEXT_COLOR)
         .set(state.ids.fluid_mode_text, ui);
 
-        let mode_list = [FluidMode::Cheap, FluidMode::Medium, FluidMode::High];
+        let mode_list = [FluidMode::Low, FluidMode::Medium, FluidMode::High];
         let mode_label_list = [
             self.localized_strings
-                .get_msg("hud-settings-fluid_rendering_mode-cheap"),
+                .get_msg("hud-settings-fluid_rendering_mode-low"),
             self.localized_strings
                 .get_msg("hud-settings-fluid_rendering_mode-medium"),
             self.localized_strings
@@ -1065,13 +1067,56 @@ impl<'a> Widget for Video<'a> {
             })));
         }
 
+        // ReflectionMode
+        Text::new(
+            &self
+                .localized_strings
+                .get_msg("hud-settings-reflection_rendering_mode"),
+        )
+        .down_from(state.ids.fluid_mode_list, 8.0)
+        .font_size(self.fonts.cyri.scale(14))
+        .font_id(self.fonts.cyri.conrod_id)
+        .color(TEXT_COLOR)
+        .set(state.ids.reflection_mode_text, ui);
+
+        let mode_list = [
+            ReflectionMode::Low,
+            ReflectionMode::Medium,
+            ReflectionMode::High,
+        ];
+        let mode_label_list = [
+            self.localized_strings
+                .get_msg("hud-settings-reflection_rendering_mode-low"),
+            self.localized_strings
+                .get_msg("hud-settings-reflection_rendering_mode-medium"),
+            self.localized_strings
+                .get_msg("hud-settings-reflection_rendering_mode-high"),
+        ];
+
+        // Get which fluid rendering mode is currently active
+        let selected = mode_list.iter().position(|x| *x == render_mode.reflection);
+
+        if let Some(clicked) = DropDownList::new(&mode_label_list, selected)
+            .w_h(400.0, 22.0)
+            .color(MENU_BG)
+            .label_color(TEXT_COLOR)
+            .label_font_id(self.fonts.cyri.conrod_id)
+            .down_from(state.ids.reflection_mode_text, 8.0)
+            .set(state.ids.reflection_mode_list, ui)
+        {
+            events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
+                reflection: mode_list[clicked],
+                ..render_mode.clone()
+            })));
+        }
+
         // LightingMode
         Text::new(
             &self
                 .localized_strings
                 .get_msg("hud-settings-lighting_rendering_mode"),
         )
-        .down_from(state.ids.fluid_mode_list, 8.0)
+        .down_from(state.ids.reflection_mode_list, 8.0)
         .font_size(self.fonts.cyri.scale(14))
         .font_id(self.fonts.cyri.conrod_id)
         .color(TEXT_COLOR)
diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs
index f32c0eb4c9..2ce83b4c31 100644
--- a/voxygen/src/render/mod.rs
+++ b/voxygen/src/render/mod.rs
@@ -151,15 +151,15 @@ impl Default for CloudMode {
 /// Fluid modes
 #[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
 pub enum FluidMode {
-    /// "Cheap" water.  This water implements no waves, no reflections, no
+    /// "Low" water.  This water implements no waves, no reflections, no
     /// diffraction, and no light attenuation through water.  As a result,
     /// it can be much cheaper than shiny reflection.
-    Cheap,
+    Low,
     High,
-    /// "Shiny" water.  This water implements waves on the surfaces, some
-    /// attempt at reflections, and tries to compute accurate light
-    /// attenuation through water (this is what results in the
-    /// colors changing as you descend into deep water).
+    /// This water implements waves on the surfaces, some attempt at
+    /// reflections, and tries to compute accurate light attenuation through
+    /// water (this is what results in the colors changing as you descend
+    /// into deep water).
     ///
     /// Unfortunately, the way the engine is currently set up, calculating
     /// accurate attenuation is a bit difficult; we use estimates from
@@ -180,6 +180,23 @@ impl Default for FluidMode {
     fn default() -> Self { FluidMode::Medium }
 }
 
+/// Reflection modes
+#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
+pub enum ReflectionMode {
+    /// No or minimal reflections.
+    Low,
+    /// High quality reflections with screen-space raycasting and
+    /// all the bells & whistles.
+    High,
+    // Medium quality screen-space reflections.
+    #[serde(other)]
+    Medium,
+}
+
+impl Default for ReflectionMode {
+    fn default() -> Self { ReflectionMode::Medium }
+}
+
 /// Lighting modes
 #[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
 pub enum LightingMode {
@@ -359,6 +376,7 @@ impl BloomMode {
 pub struct RenderMode {
     pub aa: AaMode,
     pub cloud: CloudMode,
+    pub reflection: ReflectionMode,
     pub fluid: FluidMode,
     pub lighting: LightingMode,
     pub shadow: ShadowMode,
@@ -382,6 +400,7 @@ impl Default for RenderMode {
             aa: AaMode::default(),
             cloud: CloudMode::default(),
             fluid: FluidMode::default(),
+            reflection: ReflectionMode::default(),
             lighting: LightingMode::default(),
             shadow: ShadowMode::default(),
             rain_occlusion: ShadowMapMode::default(),
@@ -403,6 +422,7 @@ impl RenderMode {
                 aa: self.aa,
                 cloud: self.cloud,
                 fluid: self.fluid,
+                reflection: self.reflection,
                 lighting: self.lighting,
                 shadow: self.shadow,
                 rain_occlusion: self.rain_occlusion,
@@ -427,6 +447,7 @@ pub struct PipelineModes {
     aa: AaMode,
     pub cloud: CloudMode,
     fluid: FluidMode,
+    reflection: ReflectionMode,
     lighting: LightingMode,
     pub shadow: ShadowMode,
     pub rain_occlusion: ShadowMapMode,
@@ -498,15 +519,6 @@ pub enum ExperimentalShader {
     DirectionalShadowMapTexelGrid,
     /// Disable rainbows
     NoRainbows,
-    /// Make the ground appear wet when appropriate.
-    Puddles,
-    /// Add extra detailing to puddles (requires [`Puddles`]).
+    /// Add extra detailing to puddles.
     PuddleDetails,
-    /// Add screen-space reflections to water.
-    ScreenSpaceReflections,
-    /// Use screen-space raycasting for reflections (requires
-    /// [`ScreenSpaceReflections`]).
-    ScreenSpaceReflectionsCasting,
-    /// Add screen-space refractions to water.
-    ScreenSpaceRefraction,
 }
diff --git a/voxygen/src/render/renderer/pipeline_creation.rs b/voxygen/src/render/renderer/pipeline_creation.rs
index e36d982b17..0da02dfb2d 100644
--- a/voxygen/src/render/renderer/pipeline_creation.rs
+++ b/voxygen/src/render/renderer/pipeline_creation.rs
@@ -6,8 +6,8 @@ use super::{
             blit, bloom, clouds, debug, figure, fluid, lod_object, lod_terrain, particle,
             postprocess, shadow, skybox, sprite, terrain, trail, ui,
         },
-        AaMode, BloomMode, CloudMode, FluidMode, LightingMode, PipelineModes, RenderError,
-        ShadowMode,
+        AaMode, BloomMode, CloudMode, FluidMode, LightingMode, PipelineModes, ReflectionMode,
+        RenderError, ShadowMode,
     },
     shaders::Shaders,
     ImmutableLayouts, Layouts,
@@ -173,6 +173,7 @@ impl ShaderModules {
 #define VOXYGEN_COMPUTATION_PREFERENCE {}
 #define FLUID_MODE {}
 #define CLOUD_MODE {}
+#define REFLECTION_MODE {}
 #define LIGHTING_ALGORITHM {}
 #define SHADOW_MODE {}
 
@@ -181,7 +182,7 @@ impl ShaderModules {
             // TODO: Configurable vertex/fragment shader preference.
             "VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT",
             match pipeline_modes.fluid {
-                FluidMode::Cheap => "FLUID_MODE_CHEAP",
+                FluidMode::Low => "FLUID_MODE_LOW",
                 FluidMode::Medium => "FLUID_MODE_MEDIUM",
                 FluidMode::High => "FLUID_MODE_HIGH",
             },
@@ -193,6 +194,11 @@ impl ShaderModules {
                 CloudMode::High => "CLOUD_MODE_HIGH",
                 CloudMode::Ultra => "CLOUD_MODE_ULTRA",
             },
+            match pipeline_modes.reflection {
+                ReflectionMode::Low => "REFLECTION_MODE_LOW",
+                ReflectionMode::Medium => "REFLECTION_MODE_MEDIUM",
+                ReflectionMode::High => "REFLECTION_MODE_HIGH",
+            },
             match pipeline_modes.lighting {
                 LightingMode::Ashikhmin => "LIGHTING_ALGORITHM_ASHIKHMIN",
                 LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG",
@@ -299,7 +305,7 @@ impl ShaderModules {
         };
 
         let selected_fluid_shader = ["fluid-frag.", match pipeline_modes.fluid {
-            FluidMode::Cheap => "cheap",
+            FluidMode::Low => "cheap",
             _ => "shiny",
         }]
         .concat();

From 1521b6165f27b09829a6c55c41d86bf234f82d7b Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sun, 23 Oct 2022 21:41:45 +0100
Subject: [PATCH 27/28] Prettier cheap water

---
 assets/voxygen/shaders/clouds-frag.glsl      |  4 ++--
 assets/voxygen/shaders/fluid-frag/cheap.glsl | 17 +++++++++++++----
 assets/voxygen/shaders/lod-terrain-frag.glsl |  3 ++-
 3 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index 9ae5009df5..ccf8b9d568 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -124,7 +124,7 @@ void main() {
             {
             cloud_blend = 1.0 - color.a;
 
-            #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
+            #if (FLUID_MODE >= FLUID_MODE_MEDIUM || REFLECTION_MODE >= REFLECTION_MODE_MEDIUM)
                 if (dir.z < 0.0) {
                     vec3 surf_norm = normalize(vec3(nz * 0.3 / (1.0 + dist * 0.1), 1));
                     vec3 refl_dir = reflect(dir, surf_norm);
@@ -182,7 +182,7 @@ void main() {
                     if (merge > 0.0) {
                         vec3 new_col = texelFetch(sampler2D(t_src_color, s_src_color), clamp(ivec2(new_uv * col_sz), ivec2(0), ivec2(col_sz) - 1), 0).rgb;
                         new_col = get_cloud_color(new_col.rgb, refl_dir, wpos, time_of_day.x, distance(new_wpos, wpos.xyz), 1.0);
-                        color.rgb = mix(color.rgb, new_col, min(merge * (color.a * 2.0), 1.0));
+                        color.rgb = mix(color.rgb, new_col, min(merge * (color.a * 2.0), 0.75));
                     }
                     cloud_blend = 1;
                 } else {
diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl
index a0807cc9b5..a34d079dd8 100644
--- a/assets/voxygen/shaders/fluid-frag/cheap.glsl
+++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl
@@ -108,7 +108,8 @@ void main() {
     vec3 view_dir = -cam_to_frag;
     // vec3 surf_color = /*srgb_to_linear*/(vec3(0.4, 0.7, 2.0));
 
-    vec3 water_color = (1.0 - mix(pow(MU_WATER, vec3(0.25)), pow(vec3(0.8, 0.24, 0.08), vec3(0.25)), water_col_vel(f_pos.xy))) * MU_SCATTER;
+    float water_shade = water_col_vel(f_pos.xy);
+    vec3 water_color = (1.0 - mix(MU_WATER, pow(vec3(0.8, 0.9, 0.08), vec3(0.25)), water_shade)) * MU_SCATTER;
 
     /* vec3 sun_dir = get_sun_dir(time_of_day.x);
     vec3 moon_dir = get_moon_dir(time_of_day.x); */
@@ -158,10 +159,18 @@ void main() {
     vec3 k_d = vec3(1.0);
     vec3 k_s = vec3(R_s);
 
+    vec3 reflect_ray_dir = reflect(cam_to_frag, f_norm);
+
+    vec3 reflect_color = vec3(0.0);
+    #if (REFLECTION_MODE >= REFLECTION_MODE_MEDIUM)
+        reflect_color = get_sky_color(reflect_ray_dir, time_of_day.x, f_pos, vec3(-100000), 0.125, true, 1.0, true, sun_shade_frac);
+    #endif
+
     vec3 emitted_light, reflected_light;
 
     // Prevent the sky affecting light when underground
     float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0);
+    reflect_color *= not_underground;
 
     // float point_shadow = shadow_at(f_pos, f_norm);
     // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
@@ -215,16 +224,16 @@ void main() {
     // float reflected_light_point = /*length*/(diffuse_light_point.r) + f_light * point_shadow;
     // reflected_light += k_d * (diffuse_light_point + f_light * point_shadow * shade_frac) + specular_light_point;
 
-    float passthrough = max(dot(cam_norm, -cam_to_frag), 0) * 0.65;
+    float passthrough = max(dot(cam_norm, -cam_to_frag), 0);
 
     float min_refl = 0.0;
     float opacity = (1.0 - passthrough) * 1.0 / (1.0 + min_refl);
     if (medium.x == MEDIUM_WATER) {
         // Hack to make the opacity of the surface fade when underwater to avoid artifacts
-        opacity = min(sqrt(max(opacity, clamp((f_pos.z - cam_pos.z) * 0.05, 0.0, 1.0))), 1.0);
+        opacity = min(sqrt(max(opacity, clamp((f_pos.z - cam_pos.z) * 0.05, 0.0, 1.0))), 0.99);
     }
 
-    vec3 surf_color = illuminate(max_light, view_dir, water_color * /* fog_color * */emitted_light, /*surf_color * */water_color * reflected_light);
+    vec3 surf_color = illuminate(max_light, view_dir, water_color * /* fog_color * */emitted_light, /*surf_color * */reflect_color * water_shade + water_color * reflected_light);
     // vec4 color = vec4(surf_color, passthrough * 1.0 / (1.0 + min_refl));// * (1.0 - /*log(1.0 + cam_attenuation)*//*cam_attenuation*/1.0 / (2.0 - log_cam)));
     vec4 color = vec4(surf_color, opacity);
 
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index 99509f4214..58691d11e0 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -650,10 +650,10 @@ void main() {
     vec3 surf_color;
     float surf_alpha = 1.0;
     if (length(f_col_raw - vec3(0.02, 0.06, 0.22)) < 0.025 && dot(vec3(0, 0, 1), f_norm) > 0.9) {
+        vec3 reflect_ray = cam_to_frag * vec3(1, 1, -1);
         #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
             vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;
 
-            vec3 reflect_ray = cam_to_frag * vec3(1, 1, -1);
 
             float passthrough = dot(faceforward(f_norm, f_norm, cam_to_frag), -cam_to_frag);
 
@@ -675,6 +675,7 @@ void main() {
             surf_alpha = 1.0 - passthrough;
         #else
             surf_alpha = 0.9;
+            surf_color = get_sky_color(reflect_ray, time_of_day.x, f_pos, vec3(-100000), 0.125, true, 1.0, true, sun_shade_frac);
         #endif
     } else {
         surf_color = illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light);

From 428816c65ee5ae7cfb774098932ee2bf314eb8bc Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sun, 23 Oct 2022 23:54:55 +0100
Subject: [PATCH 28/28] Addressed review comments

---
 assets/voxygen/shaders/clouds-frag.glsl      | 5 +++--
 assets/voxygen/shaders/figure-frag.glsl      | 4 ++--
 assets/voxygen/shaders/fluid-frag/cheap.glsl | 2 +-
 assets/voxygen/shaders/fluid-frag/shiny.glsl | 2 +-
 assets/voxygen/shaders/lod-terrain-frag.glsl | 4 ++--
 assets/voxygen/shaders/particle-frag.glsl    | 4 ++--
 assets/voxygen/shaders/sprite-frag.glsl      | 4 ++--
 assets/voxygen/shaders/terrain-frag.glsl     | 4 ++--
 voxygen/src/mesh/terrain.rs                  | 8 ++++++++
 voxygen/src/scene/camera.rs                  | 6 +++---
 10 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl
index ccf8b9d568..b0ef33fa4e 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -86,7 +86,8 @@ void main() {
 
     vec3 wpos = wpos_at(uv);
     float dist = distance(wpos, cam_pos.xyz);
-    vec3 dir = (wpos - cam_pos.xyz) / dist;
+    vec3 cam_dir = (wpos - cam_pos.xyz) / dist;
+    vec3 dir = cam_dir;
 
     // Apply clouds
     float cloud_blend = 1.0;
@@ -201,7 +202,7 @@ void main() {
         if (medium.x == MEDIUM_AIR && rain_density > 0.001) {
             vec3 cam_wpos = cam_pos.xyz + focus_off.xyz;
 
-            vec3 adjusted_dir = (vec4(dir, 0) * rain_dir_mat).xyz;
+            vec3 adjusted_dir = (vec4(cam_dir, 0) * rain_dir_mat).xyz;
 
             vec2 dir2d = adjusted_dir.xy;
             vec3 rorigin = cam_pos.xyz + focus_off.xyz + 0.5;
diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl
index 4f8c83d112..52da9e37e6 100644
--- a/assets/voxygen/shaders/figure-frag.glsl
+++ b/assets/voxygen/shaders/figure-frag.glsl
@@ -227,8 +227,8 @@ void main() {
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
     #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
-            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
-            : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
+            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
+            : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
     #endif
 
     // Prevent the sky affecting light when underground
diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl
index a34d079dd8..1e4f5b6a8f 100644
--- a/assets/voxygen/shaders/fluid-frag/cheap.glsl
+++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl
@@ -229,7 +229,7 @@ void main() {
     float min_refl = 0.0;
     float opacity = (1.0 - passthrough) * 1.0 / (1.0 + min_refl);
     if (medium.x == MEDIUM_WATER) {
-        // Hack to make the opacity of the surface fade when underwater to avoid artifacts
+        // Hack to make the transparency of the surface fade when underwater to avoid artifacts
         opacity = min(sqrt(max(opacity, clamp((f_pos.z - cam_pos.z) * 0.05, 0.0, 1.0))), 0.99);
     }
 
diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl
index 79a930592c..d1b64fb837 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -409,7 +409,7 @@ void main() {
     if (medium.x != MEDIUM_WATER) {
         min_refl = min(emitted_light.r, min(emitted_light.g, emitted_light.b));
     } else {
-        // Hack to make the opacity of the surface fade when underwater to avoid artifacts
+        // Hack to make the transparency of the surface fade when underwater to avoid artifacts
         if (dot(refract_ray_dir, cam_to_frag) > 0.0) {
             opacity = 0.99;
         } else {
diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl
index 58691d11e0..4318c2967f 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -579,9 +579,9 @@ void main() {
 
     vec3 emitted_light, reflected_light;
 
-    vec3 mu = false/* && f_pos.z <= fluid_alt*/ ? MU_WATER : vec3(0.0);
+    vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
     // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water.
-    vec3 cam_attenuation = compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos);
+    vec3 cam_attenuation = compute_attenuation_point(f_pos, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
     // Use f_norm here for better shadows.
     // vec3 light_frac = light_reflection_factor(f_norm/*l_norm*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(/*1.0*/R_s), alpha);
 
diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl
index b810992ccc..83f8ff6e1c 100644
--- a/assets/voxygen/shaders/particle-frag.glsl
+++ b/assets/voxygen/shaders/particle-frag.glsl
@@ -90,8 +90,8 @@ void main() {
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
     #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
-            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, MU_WATER, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
-            : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, vec3(0), fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
+            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, MU_WATER, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
+            : compute_attenuation_point(f_pos, -view_dir, vec3(0), fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
     #endif
 
     max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
diff --git a/assets/voxygen/shaders/sprite-frag.glsl b/assets/voxygen/shaders/sprite-frag.glsl
index 92d45dd6b2..700a5eee05 100644
--- a/assets/voxygen/shaders/sprite-frag.glsl
+++ b/assets/voxygen/shaders/sprite-frag.glsl
@@ -96,8 +96,8 @@ void main() {
     vec3 mu = medium.x == MEDIUM_WATER ? MU_WATER : vec3(0.0);
     #if (FLUID_MODE >= FLUID_MODE_MEDIUM)
         cam_attenuation =
-            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
-            : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
+            medium.x == MEDIUM_WATER ? compute_attenuation_point(cam_pos.xyz, view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
+            : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
     #endif
 
     // Prevent the sky affecting light when underground
diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl
index b6b1a84b13..16480d77b6 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -366,8 +366,8 @@ void main() {
     // NOTE: Default intersection point is camera position, meaning if we fail to intersect we assume the whole camera is in water.
     // Computing light attenuation from water.
     vec3 cam_attenuation =
-        false/*medium.x == MEDIUM_WATER*/ ? compute_attenuation_point(cam_pos.xyz + focus_off.xyz, view_dir, MU_WATER, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos + focus_off.xyz)
-        : compute_attenuation_point(f_pos + focus_off.xyz, -view_dir, mu, fluid_alt + focus_off.z, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz + focus_off.xyz);
+        false/*medium.x == MEDIUM_WATER*/ ? compute_attenuation_point(cam_pos.xyz, view_dir, MU_WATER, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/f_pos)
+        : compute_attenuation_point(f_pos, -view_dir, mu, fluid_alt, /*cam_pos.z <= fluid_alt ? cam_pos.xyz : f_pos*/cam_pos.xyz);
 
     // Prevent the sky affecting light when underground
     float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0);
diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs
index 4f4e6410f9..baf526febd 100644
--- a/voxygen/src/mesh/terrain.rs
+++ b/voxygen/src/mesh/terrain.rs
@@ -391,6 +391,14 @@ pub fn generate_mesh<'a>(
     let create_opaque =
         |atlas_pos, pos, norm, meta| TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta);
     let create_transparent = |_atlas_pos, pos: Vec3<f32>, norm| {
+        // TODO: It *should* be possible to pull most of this code out of this function
+        // and compute it per-chunk. For some reason, this doesn't work! If you,
+        // dear reader, feel like giving it a go then feel free. For now
+        // it's been kept as-is because I'm lazy and water vertices aren't nearly common
+        // enough for this to matter much. If you want to test whether your
+        // change works, look carefully at how waves interact between water
+        // polygons in different chunks. If the join is smooth, you've solved the
+        // problem!
         let key = vol.pos_key(range.min + pos.as_());
         let v00 = vol
             .get_key(key + Vec2::new(0, 0))
diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs
index 6cef70a274..0cb276d878 100644
--- a/voxygen/src/scene/camera.rs
+++ b/voxygen/src/scene/camera.rs
@@ -12,7 +12,7 @@ const FIRST_PERSON_INTERP_TIME: f32 = 0.1;
 const THIRD_PERSON_INTERP_TIME: f32 = 0.1;
 const FREEFLY_INTERP_TIME: f32 = 0.0;
 const LERP_ORI_RATE: f32 = 15.0;
-const CLIPPING_MODE_DISTANCE: Range<f32> = 2.0..20.0;
+const CLIPPING_MODE_RANGE: Range<f32> = 2.0..20.0;
 pub const MIN_ZOOM: f32 = 0.1;
 
 // Possible TODO: Add more modes
@@ -368,7 +368,7 @@ impl Camera {
     ) {
         span!(_guard, "compute_dependents", "Camera::compute_dependents");
         // TODO: More intelligent function to decide on which strategy to use
-        if self.tgt_dist < CLIPPING_MODE_DISTANCE.end {
+        if self.tgt_dist < CLIPPING_MODE_RANGE.end {
             self.compute_dependents_near(terrain, is_transparent)
         } else {
             self.compute_dependents_far(terrain, is_transparent)
@@ -426,7 +426,7 @@ impl Camera {
         };
 
         // If the camera ends up being too close to the focus point, switch policies.
-        if dist < CLIPPING_MODE_DISTANCE.start {
+        if dist < CLIPPING_MODE_RANGE.start {
             self.compute_dependents_far(terrain, is_transparent);
         } else {
             if self.dist >= dist {