Orthogonal Camera View Water Shader

I’m going to be honest – I truly don’t understand shaders.  When I was looking for shaders for my Godot 3.5 game, I found a lot of shaders for perspective view but not a lot for orthogonal view.  I was able to cobble this together using a combination of shaders.

I have found a glitch that I will absolutely not be able to fix because I truly don’t understand how this works.  When the camera is zoomed out, the water looks odd.  I know it’s from the code I added for the foam/intersect portion of it, but I can’t figure out why.  Otherwise, it works well!  I have this applied to a MeshInstance Plane object.

Shader code
//credit:  https://www.reddit.com/r/godot/comments/11j1sh8/need_help_with_orthographic_water_shaders/
//credit:  https://godotshaders.com/shader/cheap-water-shader/
//credit:  https://godotshaders.com/shader/water-shader-for-realistic-look/
//cobbled together by dangerz for SpecFreq, https://www.specfreq.com

shader_type spatial;
render_mode specular_schlick_ggx, unshaded, cull_back, async_visible; // don't know what any of these do

uniform vec4 tint_color : hint_color = vec4( 0.3, 0.4, 0.45, 1.0);
uniform sampler2D caustics : hint_white;
uniform float slowdown : hint_range( 0.1, 10.0, 0.1 ) = 5.0;
uniform float time_caust : hint_range( 0.01, 1.0, 0.01 ) = 0.13;
uniform float caust_form : hint_range( 0.01, 1.0, 0.01 ) = 0.15;
uniform float tile: hint_range( 0.1, 16.0, 0.1 ) = 4.0;
uniform float height_scale = 0.5;
uniform sampler2D noise;
uniform sampler2D normalmap;
varying vec2 tex_position;
const float wave_pow = 0.65; //yes, waves power
const float wave_pow2 = 4.0; //so much wave power that aquaman is jealous
const float wave_scale = 0.4; //maybe related to fish scales?
const float wave_scale2 = 0.3;
const float wave_scale3 = 0.5;
const float wave_scale4 = 0.6;

uniform vec4 intersection_color : hint_color;
uniform vec4 main_color : hint_color;
uniform float intersection_max_threshold = 0.5;

float getDepth(vec2 screen_uv, float raw_depth, mat4 inv_projection_matrix){
//	Credit: https://godotshaders.com/shader/depth-modulated-pixel-outline-in-screen-space/
	vec3 normalized_device_coordinates = vec3(screen_uv * 2.0 - 1.0, raw_depth);
    vec4 view_space = inv_projection_matrix * vec4(normalized_device_coordinates, 1.0);	
	view_space.xyz /= view_space.w;	 //wut
	return -view_space.z;
}

float wave(vec2 position){ //hello *wave back*
  position += texture(noise, position / 64.0).x * 2.0 - 1.0;
  vec2 wv = 1.0 - abs(sin(position));
  return pow(1.0 - pow(wv.x * wv.y, wave_pow), wave_pow2);
}

float height(vec2 position, float time) { //time, yeah, definitely that
  float d = wave((position + time) * wave_scale) * 0.3;
  d += wave((position - time) * wave_scale2) * 0.3; //so friendly with all the waving.. is it too much tho??
  d += wave((position + time) * wave_scale3) * 0.2;
  d += wave((position - time) * wave_scale4) * 0.2;
  return d;
}

void fragment() { //fragment means its just a small part of something.  *science*
	float caustics_form = texture(caustics , UV * (tile + sin(TIME / slowdown) * time_caust )).r;//
	vec2 caustics_uv =  UV * (tile + cos(TIME / slowdown) * time_caust ) + caustics_form * caust_form ;
	float caustics_final = texture(caustics, caustics_uv).r; //whats a caustic lol no idea
	
	float base_depth = getDepth(SCREEN_UV, FRAGCOORD.z, INV_PROJECTION_MATRIX);
	float surface_depth = getDepth(SCREEN_UV, texture(DEPTH_TEXTURE, SCREEN_UV).x, INV_PROJECTION_MATRIX);
	float diff = surface_depth - base_depth; //probably at least 4?  3?  honestly no idea

	vec4 foam = mix(intersection_color, main_color, step(intersection_max_threshold, diff)); //i renamed this from col to foam.  I am smart.

	ALBEDO = (1.0-SCREEN_UV.y) * caustics_final * tint_color.rgb + foam.rgb; //i didn't know what to do with foam, just tried multiplying, dividing, then adding worked so I kept that
	ALPHA = min(SCREEN_UV.y + 0.5, 1.0 ) * tint_color.a + foam.r; //this shader is the alpha shader
}
void vertex() { //so many good comments in here, none are mine
  // Get the current vertex position
  vec3 pos = VERTEX;

  // Get the height of the terrain at this position
  float k = height(pos.xz, TIME);

  // Set the Y position of the vertex to the terrain height
  VERTEX.y = k * height_scale;

  // Calculate the normal vector using nearby terrain heights
  NORMAL = normalize(vec3(
    k - height(pos.xz + vec2(0.1, 0.0), TIME),
    0.1,
    k - height(pos.xz + vec2(0.0, 0.1), TIME)
  ));

  // Apply the normal map to the normal vector
  NORMAL = normalize(texture(normalmap, tex_position).rgb * 2.0 - 1.0 + NORMAL);
} // how does any of this work.  just like the tide, no one truly knows
Tags
godot 3.5 water shader orthogonal foam waves
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.

Related shaders

sine wave camera view shader

Pixelate into view (Custom Resolution)

Pixelate into view (Texture Resolution)

Subscribe
Notify of
guest

6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
ColorauGuiyino
11 months ago

maybe the camera stops rendering certain things when it’s too far away?

IsukyPohlo
6 months ago
Reply to  dangerz

Change INV_PROJECTION_MATRIX to inverse(PROJECTION_MATRIX) and it works fine.

Looks like the first one is broken

Keith
Keith
9 months ago

This is a masterclass on the importance of commenting your code.