PSX Style Water Surface – Pixelation, Waves, Scrolling Textures

Update – edge glow and opacity

This is a water surface shader for Godot 4 that I am using in my helicopter project. I wanted to make a simple water shader that blended two textures and had vertex displacement, and could also pixelate the texture for a more old-school look. Features include:

  • 2-layer customizable textures (current is a royalty-free placeholder) that can move in different directions and have adjustable blending levels
  • Vertex-based wave displacement based on Godot’s fastnoiselite
  • Control the level of pixelation in each texture
  • Opacity and edge foam is customizable
I have tested this in small maps (100×100) as well as large maps (6000×6000) with no issues.

Remember if you scale up the size, you will need to subdivide the mesh AND raise the UV scaling for all textures.
 
License: MIT, use however you’d like. I hope this helps you on your Godot Journey!
Shader code
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx;

uniform vec3 albedo : source_color;
uniform sampler2D water_texture1;
uniform sampler2D water_texture2;
uniform sampler2D noise_texture;
uniform vec2 scroll_speed1 = vec2(0.05, 0.0);
uniform vec2 scroll_speed2 = vec2(-0.03, 0.0);
uniform float blend_factor = 0.5;
uniform vec2 scale1 = vec2(1.0, 1.0);
uniform vec2 scale2 = vec2(1.0, 1.0);
uniform float wave_strength = 1.0;
uniform float wave_scale = 0.02;
uniform int pixelation_level = 64;
uniform float FoamSize = 0.5;
uniform sampler2D DepthTexture : hint_depth_texture; 
uniform float WaterOpacity = 1.0;
uniform float FoamGlowIntensity = 0.5;

void vertex() {
    vec2 global_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xz;
    float noise_value = texture(noise_texture, global_position * wave_scale).r;
    float wave = sin(global_position.x * 0.2 + global_position.y * 0.2 + TIME + noise_value * 10.0) * wave_strength;
    VERTEX.y += wave;
}

void fragment() {
    vec2 scaledUV1 = UV * scale1;
    vec2 scaledUV2 = UV * scale2;
    vec2 scrolledUV1 = scaledUV1 + scroll_speed1 * TIME;
    vec2 scrolledUV2 = scaledUV2 + scroll_speed2 * TIME;
    scrolledUV1 = mod(scrolledUV1, vec2(1.0, 1.0));
    scrolledUV2 = mod(scrolledUV2, vec2(1.0, 1.0));
    scrolledUV1 = floor(scrolledUV1 * float(pixelation_level)) / float(pixelation_level);
    scrolledUV2 = floor(scrolledUV2 * float(pixelation_level)) / float(pixelation_level);

    vec4 water_color1 = texture(water_texture1, scrolledUV1);
    vec4 water_color2 = texture(water_texture2, scrolledUV2);
    vec4 blended_water_color = mix(water_color1, water_color2, blend_factor);

    float depthValue = texture(DepthTexture, SCREEN_UV).r;
    vec4 worldPosition = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depthValue, 1.0);
    worldPosition.xyz /= worldPosition.w;
    float foamEffect = clamp(1.0 - smoothstep(worldPosition.z + FoamSize, worldPosition.z, VERTEX.z), 0.0, 1.0);
    float foamOpacity = 1.0 - foamEffect;
    float foamEffectRounded = round(foamOpacity);
    float finalOpacity = foamEffectRounded + WaterOpacity;
	
    ALBEDO = blended_water_color.rgb * albedo;
    ALPHA = finalOpacity;
    EMISSION = vec3(foamEffectRounded) * FoamGlowIntensity;
    METALLIC = 0.0;
    ROUGHNESS = 1.0;
}
Tags
ocean, pixelate, water, waves
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from GEG-fairbear8974

PSX Style Camera Shader – Distance Fog, Dithering, Color Limiter, Noise

Related shaders

Toon Style 3D Water Shader – No textures needed

Wind Waker Water – NO Textures needed!

water waves 2d

Subscribe
Notify of
guest

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
vyntrix
5 months ago

Really cool!

ChapC_YT
5 months ago

This is amazing. Been implementing it for my 3d/2d hybrid game, and helped me out a lot. I am trying to figure out how I can add some ‘sparkling’ elements, compared to this 2d example: https://godotshaders.com/shader/pixel-art-water/

I am also looking for a way to bring out the whites a bit more. I already added some edge detection logic to it. Looks pretty cool 😀

wip screenshot: https://www.dropbox.com/transfer/AAAAAMCZIcd3y3ElNPNJr8GVeaI94b2ucRVLqYwzX2cAjYXO9h0Kup0

Again, thanks a lot, made my day!

KingGD
KingGD
5 months ago

Great!