diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76714a79b2..63584f78d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +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
+- 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
@@ -60,6 +63,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/assets/voxygen/i18n/en/hud/settings.ftl b/assets/voxygen/i18n/en/hud/settings.ftl
index cb895bebe5..847e428fc9 100644
--- a/assets/voxygen/i18n/en/hud/settings.ftl
+++ b/assets/voxygen/i18n/en/hud/settings.ftl
@@ -72,8 +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-shiny = Shiny
+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
@@ -93,7 +98,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..b0ef33fa4e 100644
--- a/assets/voxygen/shaders/clouds-frag.glsl
+++ b/assets/voxygen/shaders/clouds-frag.glsl
@@ -6,9 +6,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -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;
@@ -48,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;
@@ -60,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;
+    }
+}
+
 void main() {
     vec4 color = texture(sampler2D(t_src_color, s_src_color), uv);
 
@@ -70,13 +86,113 @@ 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;
     if (color.a < 1.0) {
-        cloud_blend = 1.0 - color.a;
-        dist = DIST_CAP;
+        vec2 nz = vec2(0);
+        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) * (dir.z < 0.0 ? color.a : 1.0);
+
+            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 * 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);
+
+            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;
+            }
+        #endif
+            {
+            cloud_blend = 1.0 - color.a;
+
+            #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);
+
+                    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;
+
+                    #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;
+                        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 (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;
+                                    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;
+                                }
+                            }
+                        }
+                    #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 = 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), 0.75));
+                    }
+                    cloud_blend = 1;
+                } else {
+            #else
+                {
+            #endif
+                cloud_blend = 1;
+            }
+        }
     }
     color.rgb = mix(color.rgb, get_cloud_color(color.rgb, dir, cam_pos.xyz, time_of_day.x, dist, 1.0), cloud_blend);
 
@@ -86,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/clouds-vert.glsl b/assets/voxygen/shaders/clouds-vert.glsl
index 933d3a3dc3..039af8a38d 100644
--- a/assets/voxygen/shaders/clouds-vert.glsl
+++ b/assets/voxygen/shaders/clouds-vert.glsl
@@ -6,9 +6,9 @@
 
 #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_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..52da9e37e6 100644
--- a/assets/voxygen/shaders/figure-frag.glsl
+++ b/assets/voxygen/shaders/figure-frag.glsl
@@ -8,9 +8,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -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];
@@ -135,9 +137,9 @@ 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)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
@@ -161,7 +163,24 @@ 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
+    #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
+    #endif
+    {
+        surf_color = f_col;
+    }
+
     float alpha = 1.0;
     const float n2 = 1.5;
 
@@ -206,7 +225,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/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/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl
index 39903c1324..1e4f5b6a8f 100644
--- a/assets/voxygen/shaders/fluid-frag/cheap.glsl
+++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl
@@ -6,9 +6,9 @@
 
 #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_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];
@@ -50,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) * 1.0;
+}
+
+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);
@@ -83,14 +107,15 @@ 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));
+
+    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); */
-#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)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
@@ -134,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);
@@ -191,15 +224,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.75;
+    float passthrough = max(dot(cam_norm, -cam_to_frag), 0);
+
     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 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);
     }
 
-    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, (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 4c4b58a961..d1b64fb837 100644
--- a/assets/voxygen/shaders/fluid-frag/shiny.glsl
+++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl
@@ -6,9 +6,9 @@
 
 #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_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];
@@ -52,35 +53,72 @@ 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(vec3 pos){
-    pos *= 0.3;
+// 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 = 6.0;
-    float speed = 2.0;
-    float weight = 1.0;
-    float w = 0.0;
+    float phase = 4.0;
+    float weight = 1.5;
+    vec4 w = vec4(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;
+        posx *= 0.2;
+        posy *= 0.2;
+        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 int iters = 11;
+        const float scale = 5.0;
+    #endif
+    const float iter_shift = (3.14159 * 2.0) / 7.3;
+
+    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;
+        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.15);
-        phase *= 1.18;
-        speed *= 1.07;
+        weight = mix(weight, 0.0, 0.2);
+        phase *= 1.2;
+        speed += speed_per_iter;
     }
-    return w / ws * 10.0;
+    return w / ws * scale;
+}
+
+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
+    );
+    return mix(
+        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)
+    );
 }
 
 void main() {
@@ -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_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
@@ -175,9 +213,9 @@ 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)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
@@ -209,14 +247,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.
@@ -237,6 +267,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 (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
+        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.
+    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);
@@ -271,6 +316,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.
@@ -288,10 +334,10 @@ 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, 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;
 
@@ -332,7 +378,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));
@@ -359,10 +405,18 @@ 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 transparency of the surface fade when underwater to avoid artifacts
+        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, (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, 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/fluid-vert.glsl b/assets/voxygen/shaders/fluid-vert.glsl
index b078b87407..16d3a12dfa 100644
--- a/assets/voxygen/shaders/fluid-vert.glsl
+++ b/assets/voxygen/shaders/fluid-vert.glsl
@@ -6,9 +6,9 @@
 
 #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_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));
@@ -63,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);
@@ -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/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/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 edd1e27b91..e727e49563 100644
--- a/assets/voxygen/shaders/include/constants.glsl
+++ b/assets/voxygen/shaders/include/constants.glsl
@@ -6,8 +6,13 @@
 #define VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT 0
 #define VOXYGEN_COMPUTATION_PREFERENCE_VERTEX 1
 
-#define FLUID_MODE_CHEAP 0
-#define FLUID_MODE_SHINY 1
+#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
diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl
index d2efde0737..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);
@@ -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 15efe21f86..46c3336668 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);
@@ -503,7 +503,12 @@ float is_star_at(vec3 dir) {
 
     //return 0.0;
 
-    return 5.0 / (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) {
@@ -570,7 +575,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, float sun_shade_frac) {
     // Sky color
     /* vec3 sun_dir = get_sun_dir(time_of_day);
     vec3 moon_dir = get_moon_dir(time_of_day); */
@@ -581,7 +586,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,
@@ -591,15 +596,29 @@ 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 = 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) {
+    #else
+        if (fake_clouds || medium.x == MEDIUM_WATER) {
+    #endif
+        sun_surf *= 0.1;
     }
     vec3 sun_light = sun_halo + sun_surf;
 
@@ -610,25 +629,34 @@ 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;
-    #if (CLOUD_MODE == CLOUD_MODE_NONE)
-        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);
     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.2;
+        moon_surf *= 0.05;
+    }
+    vec3 moon_halo = moon_halo_color * pow(max(dot(dir, -moon_dir), 0), moon_halo_power);
     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
 
+    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, !fake_clouds);
+    } else {
         if (medium.x == MEDIUM_WATER) {
             sky_color = get_sky_light(dir, time_of_day, true);
         } else {
@@ -636,13 +664,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, 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);
+    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/light-shadows-directed-vert.glsl b/assets/voxygen/shaders/light-shadows-directed-vert.glsl
index 1ff6fbffd9..38486aa808 100644
--- a/assets/voxygen/shaders/light-shadows-directed-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-directed-vert.glsl
@@ -7,9 +7,9 @@
 
 #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_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..c48240c1b3 100644
--- a/assets/voxygen/shaders/light-shadows-figure-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-figure-vert.glsl
@@ -9,9 +9,9 @@
 
 #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_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..d6aded48d7 100644
--- a/assets/voxygen/shaders/light-shadows-frag.glsl
+++ b/assets/voxygen/shaders/light-shadows-frag.glsl
@@ -11,9 +11,9 @@
 
 #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_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..dbc2529c63 100644
--- a/assets/voxygen/shaders/light-shadows-geom.glsl
+++ b/assets/voxygen/shaders/light-shadows-geom.glsl
@@ -11,9 +11,9 @@
 
 #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_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..dd98eeb8e9 100644
--- a/assets/voxygen/shaders/light-shadows-vert.glsl
+++ b/assets/voxygen/shaders/light-shadows-vert.glsl
@@ -7,9 +7,9 @@
 
 #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_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..9e445870b7 100644
--- a/assets/voxygen/shaders/lod-object-frag.glsl
+++ b/assets/voxygen/shaders/lod-object-frag.glsl
@@ -6,9 +6,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -41,9 +41,9 @@ 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)
+#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 2358611b0a..4318c2967f 100644
--- a/assets/voxygen/shaders/lod-terrain-frag.glsl
+++ b/assets/voxygen/shaders/lod-terrain-frag.glsl
@@ -6,9 +6,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -445,10 +445,10 @@ 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)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float shadow_alt = f_pos.z;
 #endif
 
@@ -579,9 +579,9 @@ 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 = 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);
 
@@ -648,34 +648,43 @@ 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 (length(f_col_raw - vec3(0.02, 0.06, 0.22)) < 0.025 && dot(vec3(0, 0, 1), f_norm) > 0.9) {
+    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);
 
-            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, 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, 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);
             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));
-        } else {
-            surf_color = illuminate(max_light, view_dir, f_col * emitted_light, f_col * reflected_light);
-        }
-    #else
+            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);
-    #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;
     // 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);
 }
diff --git a/assets/voxygen/shaders/lod-terrain-vert.glsl b/assets/voxygen/shaders/lod-terrain-vert.glsl
index da9f890bfd..0cd3eccd11 100644
--- a/assets/voxygen/shaders/lod-terrain-vert.glsl
+++ b/assets/voxygen/shaders/lod-terrain-vert.glsl
@@ -6,9 +6,9 @@
 
 #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_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..83f8ff6e1c 100644
--- a/assets/voxygen/shaders/particle-frag.glsl
+++ b/assets/voxygen/shaders/particle-frag.glsl
@@ -6,9 +6,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -42,9 +42,9 @@ 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)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
@@ -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..705f2efe8b 100644
--- a/assets/voxygen/shaders/point-light-shadows-vert.glsl
+++ b/assets/voxygen/shaders/point-light-shadows-vert.glsl
@@ -7,9 +7,9 @@
 
 #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_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..89863db99b 100644
--- a/assets/voxygen/shaders/postprocess-frag.glsl
+++ b/assets/voxygen/shaders/postprocess-frag.glsl
@@ -6,9 +6,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -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 21a6b3d8c2..f15ec174b2 100644
--- a/assets/voxygen/shaders/postprocess-vert.glsl
+++ b/assets/voxygen/shaders/postprocess-vert.glsl
@@ -6,9 +6,9 @@
 
 #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_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..eab9446343 100644
--- a/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl
+++ b/assets/voxygen/shaders/rain-occlusion-directed-vert.glsl
@@ -7,9 +7,9 @@
 
 #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_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..34edba46be 100644
--- a/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl
+++ b/assets/voxygen/shaders/rain-occlusion-figure-vert.glsl
@@ -9,9 +9,9 @@
 
 #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_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..60930b469d 100644
--- a/assets/voxygen/shaders/skybox-frag.glsl
+++ b/assets/voxygen/shaders/skybox-frag.glsl
@@ -6,9 +6,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
 #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -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, true, refractionIndex, false, 1.0), 1.0);
 }
diff --git a/assets/voxygen/shaders/skybox-vert.glsl b/assets/voxygen/shaders/skybox-vert.glsl
index 9f362fedb7..e410cd221f 100644
--- a/assets/voxygen/shaders/skybox-vert.glsl
+++ b/assets/voxygen/shaders/skybox-vert.glsl
@@ -6,9 +6,9 @@
 
 #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_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..700a5eee05 100644
--- a/assets/voxygen/shaders/sprite-frag.glsl
+++ b/assets/voxygen/shaders/sprite-frag.glsl
@@ -6,9 +6,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -51,9 +51,9 @@ 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)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
@@ -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..16480d77b6 100644
--- a/assets/voxygen/shaders/terrain-frag.glsl
+++ b/assets/voxygen/shaders/terrain-frag.glsl
@@ -7,9 +7,9 @@
 
 #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_SHINY)
+#elif (FLUID_MODE >= FLUID_MODE_MEDIUM)
     #define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
 #endif
 
@@ -209,9 +209,9 @@ 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)
+#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_LOW)
     float f_alt = f_pos.z;
 #endif
 
@@ -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;
+    #if (REFLECTION_MODE >= REFLECTION_MODE_HIGH)
+        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;
@@ -244,23 +249,26 @@ 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
-                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.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 (REFLECTION_MODE >= REFLECTION_MODE_HIGH)
                 if (puddle > 0.0) {
-                    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);
+                    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);
+                        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);
@@ -268,21 +276,17 @@ 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;
                     k_s += distort;
 
-                    #ifdef EXPERIMENTAL_WETNESS
-                        /* 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
@@ -362,14 +366,18 @@ 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)
+        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);
 
     // To account for prior saturation
-    /*float */f_light = faces_fluid ? not_underground : f_light * sqrt(f_light);
+    #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);
+    #endif
 
     vec3 emitted_light = vec3(1.0);
     vec3 reflected_light = vec3(1.0);
@@ -396,7 +404,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;
@@ -521,5 +529,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);
 }
diff --git a/assets/voxygen/shaders/terrain-vert.glsl b/assets/voxygen/shaders/terrain-vert.glsl
index d13a240587..55eea74c1b 100644
--- a/assets/voxygen/shaders/terrain-vert.glsl
+++ b/assets/voxygen/shaders/terrain-vert.glsl
@@ -6,9 +6,9 @@
 
 #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_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..dc1ee7e232 100644
--- a/assets/voxygen/shaders/trail-frag.glsl
+++ b/assets/voxygen/shaders/trail-frag.glsl
@@ -6,9 +6,9 @@
 
 #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_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..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,12 +1040,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::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-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
@@ -1063,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/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs
index 13619b51df..baf526febd 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,37 @@ 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| {
+        // 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))
+            .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)),
+            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..2ce83b4c31 100644
--- a/voxygen/src/render/mod.rs
+++ b/voxygen/src/render/mod.rs
@@ -151,14 +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,
-    /// "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).
+    Low,
+    High,
+    /// 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
@@ -172,11 +173,28 @@ 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 }
+}
+
+/// 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
@@ -358,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,
@@ -381,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(),
@@ -402,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,
@@ -426,6 +447,7 @@ pub struct PipelineModes {
     aa: AaMode,
     pub cloud: CloudMode,
     fluid: FluidMode,
+    reflection: ReflectionMode,
     lighting: LightingMode,
     pub shadow: ShadowMode,
     pub rain_occlusion: ShadowMapMode,
@@ -497,6 +519,6 @@ pub enum ExperimentalShader {
     DirectionalShadowMapTexelGrid,
     /// Disable rainbows
     NoRainbows,
-    /// Make objects appear wet when appropriate.
-    Wetness,
+    /// Add extra detailing to puddles.
+    PuddleDetails,
 }
diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs
index a5c37fe961..331e73cac0 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,19 @@ 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,
@@ -124,7 +133,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,
diff --git a/voxygen/src/render/renderer/pipeline_creation.rs b/voxygen/src/render/renderer/pipeline_creation.rs
index b7d8a00afe..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,8 +182,9 @@ impl ShaderModules {
             // TODO: Configurable vertex/fragment shader preference.
             "VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT",
             match pipeline_modes.fluid {
-                FluidMode::Cheap => "FLUID_MODE_CHEAP",
-                FluidMode::Shiny => "FLUID_MODE_SHINY",
+                FluidMode::Low => "FLUID_MODE_LOW",
+                FluidMode::Medium => "FLUID_MODE_MEDIUM",
+                FluidMode::High => "FLUID_MODE_HIGH",
             },
             match pipeline_modes.cloud {
                 CloudMode::None => "CLOUD_MODE_NONE",
@@ -192,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",
@@ -298,8 +305,8 @@ impl ShaderModules {
         };
 
         let selected_fluid_shader = ["fluid-frag.", match pipeline_modes.fluid {
-            FluidMode::Cheap => "cheap",
-            FluidMode::Shiny => "shiny",
+            FluidMode::Low => "cheap",
+            _ => "shiny",
         }]
         .concat();
 
diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs
index 26ed0a74a7..0cb276d878 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_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 {
+        if self.tgt_dist < CLIPPING_MODE_RANGE.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_RANGE.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 c26244ff26..bbbb9c1111 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,12 @@ 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