Testing_the_waters

Placeholder info

Shader code
shader_type canvas_item;

uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;

uniform sampler2D disp_large : repeat_enable, filter_nearest;
uniform sampler2D disp_small : repeat_enable, filter_nearest;

uniform float waterline : hint_range(0.0, 1.0) = 0.55;
uniform float fade_depth : hint_range(0.01, 1.0) = 0.30;
uniform float shoreline_softness : hint_range(0.0, 0.1) = 0.01;

uniform vec4 water_tint : source_color = vec4(0.12, 0.20, 0.26, 1.0);
uniform float reflection_strength : hint_range(0.0, 2.0) = 1.0;

uniform vec2 pixel_resolution = vec2(480.0, 270.0);

// Large displacement map
uniform vec2 large_scale = vec2(1.2, 0.35);
uniform vec2 large_scroll = vec2(0.01, 0.0);
uniform vec2 large_strength = vec2(0.008, 0.0015);

// Small displacement map
uniform vec2 small_scale = vec2(4.0, 1.2);
uniform vec2 small_scroll = vec2(-0.025, 0.0);
uniform vec2 small_strength = vec2(0.0025, 0.0008);

// Perspective correction inspired by the Kingdom post.
// Set to 0.0 to disable.
uniform float perspective_amount : hint_range(-3.0, 3.0) = 0.0;

// Higher values make distortion fade out faster with depth.
uniform float distortion_depth_power : hint_range(-3.0, 3.0) = -1.0;

vec2 snap_uv(vec2 uv, vec2 res) {
    return (floor(uv * res) + vec2(0.5)) / res;
}

void fragment() {
    vec2 uv = SCREEN_UV;

    if (uv.y <= waterline) {
        COLOR = vec4(0.0);
    } else {
        // Snap the destination pixel row first for stable pixel-art distortion.
        vec2 snapped_uv = snap_uv(uv, pixel_resolution);

        float dist_below = snapped_uv.y - waterline;
        float fade = 1.0 - smoothstep(0.0, fade_depth, dist_below);
        float shoreline = smoothstep(waterline, waterline + shoreline_softness, snapped_uv.y);

        // Mirror vertically around the waterline.
        vec2 refl_uv = snapped_uv;
        refl_uv.y = 2.0 * waterline - snapped_uv.y;

        // Stylized perspective correction on displacement lookup.
        vec2 perspective_correction = vec2(
            2.0 * (0.5 - snapped_uv.x) * snapped_uv.y * perspective_amount,
            0.0
        );

        vec2 large_uv = refl_uv * large_scale + perspective_correction + TIME * large_scroll;
        vec2 small_uv = refl_uv * small_scale + perspective_correction + TIME * small_scroll;

        // Read displacement from RG channels, centered around 0.
        vec2 disp1 = texture(disp_large, large_uv).rg - vec2(0.5);
        vec2 disp2 = texture(disp_small, small_uv).rg - vec2(0.5);

        float depth_factor = pow(max(fade, 0.0), distortion_depth_power);

        vec2 total_disp =
            disp1 * large_strength +
            disp2 * small_strength;

        total_disp *= depth_factor;

        refl_uv += total_disp;

        // Snap again after distortion to keep the reflected image crisp.
        refl_uv = snap_uv(refl_uv, pixel_resolution);
        refl_uv = clamp(refl_uv, vec2(0.0), vec2(1.0));

        vec4 reflected = textureLod(screen_texture, refl_uv, 0.0);
        vec4 stylized = mix(water_tint, reflected * reflection_strength, fade);

        COLOR = vec4(stylized.rgb, fade * shoreline);
    }
}
Live Preview
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments