shader_type spatial; uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_nearest; uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_nearest; uniform vec3 albedo : source_color = vec3(0.02, 0.45, 0.8); uniform float transparency : hint_range(0.0, 1.0, 0.01) = 0.4; uniform float metallic : hint_range(0.0, 1.0, 0.01) = 0; uniform float roughness : hint_range(0.0, 1.0, 0.01) = 0.5; uniform vec2 surface_normals_move_direction_a = vec2(-1.0, 0.0); uniform vec2 surface_normals_move_direction_b = vec2(0.0, 1.0); uniform float surface_texture_roughness : hint_range(0.0, 1.0, 0.01) = 0.6; uniform float surface_texture_scale : hint_range(0.001, 2.0, 0.001) = 0.3; uniform float surface_texture_time_scale : hint_range(0.001, 2.0, 0.001) = 0.06; uniform float ssr_resolution : hint_range(0.0, 10.0, 0.1) = 2.0; uniform float ssr_max_travel : hint_range(0.0, 200.0, 0.1) = 30.0; uniform float ssr_max_diff : hint_range(0.1, 10.0, 0.1) = 4.0; uniform float ssr_mix_strength : hint_range(0.0, 1.0, 0.01) = 0.7; uniform float wave_noise_scale = 15.0; uniform float wave_height_scale = 0.25; uniform float wave_time_scale = 0.025; uniform float wave_normal_flatness : hint_range(0.1, 100.0, 0.1) = 30.0; uniform vec3 border_color : source_color = vec3(1.0); uniform float border_scale : hint_range(0.0, 5.0, 0.01) = 2.0; uniform float border_near = 0.5; uniform float border_far = 300.0; uniform float refraction_intensity : hint_range(0.0, 1.0, 0.01) = 0.4; uniform float max_visible_depth : hint_range(0.0, 100.0, 0.1) = 10.0; uniform vec3 color_deep : source_color = vec3(0.0, 0.1, 0.4); uniform sampler2D wave_a; uniform sampler2D wave_b; uniform sampler2D surface_normals_a; uniform sampler2D surface_normals_b; varying vec2 vertex_uv; float get_wave_height(vec2 uv) { float height1 = texture(wave_a, uv).r; float height2 = texture(wave_b, -uv * vec2(-1.0, -1.0)).r; return (height1 + height2 * 2.0) / 2.0; } void vertex() { vertex_uv = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xz; vec2 uv = vertex_uv / wave_noise_scale + TIME * wave_time_scale; VERTEX.y += get_wave_height(uv) * wave_height_scale; float normal_height_scale = wave_height_scale / wave_normal_flatness; vec2 e = vec2(0.01, 0.0); vec3 normal = normalize(vec3( get_wave_height(uv - e) * normal_height_scale - get_wave_height(uv + e) * normal_height_scale, 2.0 * e.x, get_wave_height(uv - e.yx) * normal_height_scale - get_wave_height(uv + e.yx) * normal_height_scale )); NORMAL = normal; } bool is_within_screen_boundaries(vec2 position) { return position.x > 0.0 && position.x < 1.0 && position.y > 0.0 && position.y < 1.0; } vec2 get_uv_from_view_position(vec3 position_view_space, mat4 proj_m) { vec4 position_clip_space = proj_m * vec4(position_view_space.xyz, 1.0); vec2 position_ndc = position_clip_space.xy / position_clip_space.w; return position_ndc.xy * 0.5 + 0.5; } vec3 get_view_position_from_uv(vec2 uv, float depth, mat4 inv_proj_m) { vec4 position_ndc = vec4((uv * 2.0) - 1.0, depth, 1.0); vec4 view_position = inv_proj_m * position_ndc; return view_position.xyz /= view_position.w; } vec3 get_ssr_color(vec3 surface_view_position, vec3 normal_view_space, vec3 view_view_space, mat4 proj_m, mat4 inv_proj_m) { vec3 current_position_view_space = surface_view_position; vec3 view_direction_view_space = view_view_space * -1.0; vec3 reflect_vector_view_space = normalize(reflect(view_direction_view_space.xyz, normal_view_space.xyz)); vec2 current_screen_position = vec2(0.0); vec3 resulting_color = vec3(-1.0); for(float travel=0.0; resulting_color.x < 0.0 && travel < ssr_max_travel; travel = travel + ssr_resolution) { current_position_view_space += reflect_vector_view_space * ssr_resolution; current_screen_position = get_uv_from_view_position(current_position_view_space, proj_m); float depth_texture_probe_raw = texture(DEPTH_TEXTURE, current_screen_position).x; vec3 depth_texture_probe_view_position = get_view_position_from_uv(current_screen_position, depth_texture_probe_raw, inv_proj_m); float depth_diff = depth_texture_probe_view_position.z - current_position_view_space.z; resulting_color = (is_within_screen_boundaries(current_screen_position) && depth_diff >= 0.0 && depth_diff < ssr_max_diff) ? texture(SCREEN_TEXTURE, current_screen_position.xy).rgb : vec3(-1.0); } return resulting_color; } float border(float cur_depth) { return border_near * border_far / (border_far + (2.0 * cur_depth - 1.0) * (border_near - border_far)); } float normalize_float(float min_v, float max_v, float value) { float clamped_value = clamp(value, min_v, max_v); return (clamped_value - min_v) / (max_v - min_v); } vec2 get_refracted_uv(vec2 raw_screen_uv, float screen_depth_raw, vec3 view, vec3 normal, mat4 proj_m, mat4 inv_proj_m) { vec3 screen_view_position_original = get_view_position_from_uv(raw_screen_uv, screen_depth_raw, inv_proj_m); float screen_center_distance = clamp(abs(length(raw_screen_uv - vec2(0.5, 0.5))) * 2.0, 0.0, 1.0); float refraction_intensity_deglitched = mix(1.0 - refraction_intensity, 1.0, screen_center_distance); vec3 refraction_position_view_space = screen_view_position_original + normalize(refract(view, -normal, refraction_intensity_deglitched)); vec2 refraction_uv = get_uv_from_view_position(refraction_position_view_space, proj_m); return refraction_uv; } void fragment() { float screen_depth_raw = texture(DEPTH_TEXTURE, SCREEN_UV).x; vec2 refraction_uv = refraction_intensity > 0.0 ? get_refracted_uv(SCREEN_UV, screen_depth_raw, VIEW, NORMAL, PROJECTION_MATRIX, INV_PROJECTION_MATRIX) : SCREEN_UV; float screen_depth = texture(DEPTH_TEXTURE, refraction_uv).x; float surface_depth = FRAGCOORD.z; float border_diff = border(screen_depth_raw) - border(surface_depth); vec2 time_vector = (TIME * surface_normals_move_direction_a) * surface_texture_time_scale; vec2 time_vector2 = (TIME * surface_normals_move_direction_b) * surface_texture_time_scale; vec3 normal_texture_blend = mix(texture(surface_normals_a, vertex_uv * surface_texture_scale + time_vector).xyz, texture(surface_normals_b, vertex_uv * surface_texture_scale + time_vector2).xyz, 0.5); vec3 normal_blend = mix(NORMAL, normal_texture_blend, surface_texture_roughness); vec3 screen_view_position = get_view_position_from_uv(refraction_uv, screen_depth, INV_PROJECTION_MATRIX); vec3 surface_view_position = get_view_position_from_uv(refraction_uv, surface_depth, INV_PROJECTION_MATRIX); float depth_opacity = 1.0 - normalize_float(0.0, max_visible_depth, length(surface_view_position - screen_view_position)); vec3 screen_color = texture(SCREEN_TEXTURE, refraction_uv).rgb; vec3 surface_color = albedo; vec3 ssr_color = get_ssr_color(surface_view_position, NORMAL, VIEW, PROJECTION_MATRIX, INV_PROJECTION_MATRIX); vec3 color_with_transparency = mix(surface_color, screen_color, transparency); vec3 depth_color = mix(color_deep.rgb, color_with_transparency, depth_opacity); vec3 water_color = (ssr_color.x > 0.0) ? mix(depth_color, ssr_color, ssr_mix_strength) : depth_color; vec3 final_color = mix(border_color, water_color, step(border_scale, border_diff)); ALBEDO.rgb = final_color; METALLIC = metallic; ROUGHNESS = roughness; NORMAL_MAP = normal_blend; }