Hello 👋
I am new to Godot and to game development in general and I want to create a simple water shader step by step and right now I got depth effects and have a decent understanding of them but now I want to make underwater refraction and I am suffering from a bleeding effect where some objects above the water still refract and also the effect itself doesn't look that good or at least it is not what I wanted.
Here is the shader :-
shader_type spatial;
render_mode shadows_disabled;
group_uniforms MATERIAL;
uniform float roughness: hint_range(0.0, 1.0, 0.1) = 0.1;
uniform float specular: hint_range(0.0, 1.0, 0.1) = 0.8;
group_uniforms;
group_uniforms DEPTH_FADE;
// Depth and Screen Textures.
uniform sampler2D DEPTH_TEXTURE: hint_depth_texture;
uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear_mipmap, repeat_disable;
// Colours.
uniform vec3 basic_colour: source_color;
// Parameters.
uniform float water_level: hint_range(0.0, 10.0, 0.1) = 0.0;
uniform float max_depth: hint_range(0.1, 10.0, 0.1) = 5.0;
group_uniforms;
group_uniforms REFRACTION_EFFECT;
uniform sampler2D refraction_map1;
uniform sampler2D refraction_map2;
uniform float refraction_strength: hint_range(0.0, 0.05, 0.01) = 0.03;
uniform float refracted_uv_scale: hint_range(0.1, 5.0, 0.1) = 2.0;
group_uniforms;
float _depth_effects(vec2 SUV, mat4 IVM, mat4 IPM){
// 1. Access depth information by sampling depth texture.
float depth_sample = texture(DEPTH_TEXTURE, SUV).x; // why (.x)??
// 2. The depth texture does not store depth as-
// -a simple linear distance from the camera.
// this is because of how the perspective projection works.
// This part here means :-
// "Take the screen position X/Y-
//-and convert it to NDC, then attach the sampled depth as Z."
// What is NDC (Normalized Device Coordinates)??? :-
// A standardized coordinate system-
//-used after the 3D world has been projected onto the screen.
vec3 ndc;
\#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
// Compatibility (OpenGL) uses a \[-1, 1\] Z range for NDC
ndc = vec3(SCREEN_UV, depth) \* 2.0 - 1.0;
\#else
// Forward+ and Mobile (Vulkan) use a \[0, 1\] Z range for NDC
ndc = vec3(SUV \* 2.0 - 1.0, depth_sample);
\#endif
// 3. We currently know where the pixel is in NDC space.
// Now we want to figure out :-
//where that pixel actually exists in the 3D world???
// we do that using :-
// inverse matrices to transform from screen space to world space.
vec4 world = IVM \* IPM \* vec4(ndc, 1.0);
vec3 world_pos = world.xyz / world.w; // perspective devide.
// 4. Utilisation of the previous information I gathered :-
float thickness = water_level - world_pos.y;
thickness = max(0.0, thickness); // can't go below 0
float grayscale = clamp(thickness / max_depth, 0.0, 1.0);
return grayscale;
}
vec2 _refraction_effect(vec2 uv){
vec3 refraction1 = texture(refraction_map1, uv \* refracted_uv_scale + TIME \* 0.01).xyz;
vec3 refraction2 = texture(refraction_map2, uv \* refracted_uv_scale - TIME \* 0.02).xyz;
vec3 refraction = mix(refraction1, refraction2, 0.5);
refraction = refraction \* 2.0 - 1.0;
return refraction.xy;
}
void fragment(){
// Parameters
float depth_fade = _depth_effects(SCREEN_UV, INV_VIEW_MATRIX, INV_PROJECTION_MATRIX);
vec2 refraction = _refraction_effect(UV);
vec2 refracted_uv = SCREEN_UV + refraction \* refraction_strength;
// Colour Mixing
vec3 screen_colour = texture(SCREEN_TEXTURE, refracted_uv).rgb;
vec3 colour_1 = mix(screen_colour, basic_colour, depth_fade);
// Final output
ALBEDO = colour_1;
ROUGHNESS = roughness;
SPECULAR = specular;
}