shader_type canvas_item; // --- Uniforms --- uniform vec4 target_color : source_color = vec4(0.0, 0.0, 1.0, 1.0); uniform float tolerance : hint_range(0.0, 1.0) = 0.1; uniform vec4 water_deep_color : source_color = vec4(0.0, 0.4, 0.8, 1.0); uniform vec4 foam_color : source_color = vec4(1.0, 1.0, 1.0, 1.0); // --- Functions --- vec2 random(vec2 uv) { return vec2(fract(sin(dot(uv.xy, vec2(12.9898,78.233))) * 43758.5453123)); } float worley(vec2 uv, float columns, float rows) { vec2 index_uv = floor(vec2(uv.x * columns, uv.y * rows)); vec2 fract_uv = fract(vec2(uv.x * columns, uv.y * rows)); float minimum_dist = 1.0; for (int y= -1; y <= 1; y++) { for (int x= -1; x <= 1; x++) { vec2 neighbor = vec2(float(x),float(y)); vec2 point = random(index_uv + neighbor); vec2 diff = neighbor + point - fract_uv; float dist = length(diff); minimum_dist = min(minimum_dist, dist); } } return minimum_dist; } float noise(vec2 uv) { vec2 uv_index = floor(uv); vec2 uv_fract = fract(uv); vec2 blur = smoothstep(0.0, 1.0, uv_fract); return mix( mix( dot( random(uv_index + vec2(0.0,0.0) ), uv_fract - vec2(0.0,0.0) ), dot( random(uv_index + vec2(1.0,0.0) ), uv_fract - vec2(1.0,0.0) ), blur.x), mix( dot( random(uv_index + vec2(0.0,1.0) ), uv_fract - vec2(0.0,1.0) ), dot( random(uv_index + vec2(1.0,1.0) ), uv_fract - vec2(1.0,1.0) ), blur.x), blur.y) + 0.5; } varying vec2 world_pos; void vertex() { world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0, 1.0)).xy; } void fragment() { vec4 tex_color = texture(TEXTURE, UV); float color_diff = distance(tex_color.rgb, target_color.rgb); if (color_diff < tolerance) { vec2 w_uv = world_pos * 0.01; float noise_value = noise(world_pos * 0.1 - TIME * 0.1) * 0.1; w_uv.x += noise_value; float worley_val = worley(w_uv + TIME * 0.1, 6.0, 10.0); vec4 final_water = mix(water_deep_color, foam_color, step(0.7, worley_val)); COLOR = vec4(final_water.rgb, tex_color.a); } else { COLOR = tex_color; } }