DJMaesen here:
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_disabled, unshaded;
uniform vec3 albedo : source_color = vec3(0.0, 0.04, 0.04);
uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap, repeat_disable;
uniform sampler2D screen_depth : hint_depth_texture, repeat_disable, filter_nearest;
uniform sampler2D distortion_texture : source_color, filter_linear_mipmap, repeat_enable;
uniform float refraction : hint_range(-1.0, 1.0) = 0.1;
uniform float bor_cut : hint_range(-1.0, 1.0) = 0.0;
varying vec2 uv_dist;
uniform float speed = 1.0;
uniform float amount = 0.1;
void vertex() {
vec3 normal = NORMAL;
TANGENT = vec3(0.0,0.0,-1.0) * abs(normal.x);
TANGENT+= vec3(1.0,0.0,0.0) * abs(normal.y);
TANGENT+= vec3(1.0,0.0,0.0) * abs(normal.z);
TANGENT = normalize(TANGENT);
BINORMAL = vec3(0.0,1.0,0.0) * abs(normal.x);
BINORMAL+= vec3(0.0,0.0,-1.0) * abs(normal.y);
BINORMAL+= vec3(0.0,1.0,0.0) * abs(normal.z);
BINORMAL = normalize(BINORMAL);
uv_dist = UV + vec2(0.0, TIME * speed);
}
float getDepth(vec2 coords, mat4 invp)
{
float depth = textureLod(screen_depth, coords, 0.0).r;
vec3 ndc = vec3(coords * 2.0 - 1.0, depth);
vec4 view = invp * vec4(ndc, 1.0);
view.xyz /= view.w;
return -view.z;
}
void fragment() {
vec3 distortion = texture(distortion_texture, uv_dist).rgb * amount;
vec2 ref_ofs = SCREEN_UV + distortion.xy;
float linear_depth = (getDepth(ref_ofs, INV_PROJECTION_MATRIX) * 0.01) + 0.5;
ALBEDO = textureLod(screen_texture, ref_ofs, linear_depth * refraction * 8.0).rgb * mix(albedo, vec3(1.0), clamp(linear_depth * 0.2, 0.0, 1.0));
}
but you have to put it on a plane in front of the camera and meshes using screen_texture will stop working. The water can disappear but the effect looks fine.
I used a normal map for water distortion.
I had it combined with:
@onready var screen_effect = $cam_v/Camera3D/MeshInstance3D
@onready var water_detector : Area3D = $cam_v/Camera3D/Area3D
func _process(_delta):
if is_on_water:
screen_effect.visible = true
else:
screen_effect.visible = false
func _physics_process(_delta):
is_on_water = water_detector.has_overlapping_areas()
the detector was in the camera and it would activate when it gets submerged.