Water Hotspring Exercise

The shader for a little water shader I made and posted on twitter.

I made a written article for the full breakdown of this effect here: Polar Water Breakdown

Keep in mind that this is meant more as a personal shader exercise and a way to present some shader topics, you will very likely need to adapt this to suit your own game.

Shader code
shader_type spatial;

uniform sampler2D caustics_texture : hint_black;
uniform sampler2D color_gradient : hint_albedo;
uniform sampler2D distort_noise : hint_black;

uniform float flow_speed = 0.3;
uniform float vignette_size = 0.3;
uniform float vignette_blend = 0.1;
uniform float distort_strength = 0.1;
uniform float disc_speed = 0.5;

const float TAU = 6.283185307;

vec2 polar_coordinates(vec2 uv, vec2 center, float zoom, float repeat)
{
	vec2 dir = uv - center;
	highp float radius = length(dir) * 2.0;
	highp float angle = atan(dir.y, dir.x) / TAU;
	return mod(vec2(radius * zoom, angle * repeat), 1.0);
}

void vertex(){
	float dn = texture(distort_noise, UV + TIME * 0.1).r;
	VERTEX.y += dn * 0.1;
}

void fragment(){
	// Polar UVs + Noise for caustics
	vec2 base_uv = UV;
	float dn = texture(distort_noise, UV + TIME * 0.1).r;
	base_uv += dn * distort_strength;
	base_uv -= distort_strength / 2.0;
	highp vec2 polar_uv = polar_coordinates(base_uv, vec2(0.5), 1.0, 1.0);
	polar_uv.x -= TIME * flow_speed;
	float caus = texture(caustics_texture, polar_uv).r;
	
	// Fade out caustics
	float cd = distance(UV, vec2(0.5));
	float vign = 1.0 - smoothstep(vignette_size, vignette_size + vignette_blend, cd);
	
	// Color the caustics
	float grad_uv = caus * vign;
	vec3 color = texture(color_gradient, vec2(grad_uv)).rgb;
	
	// Center discs
	float global_disc = 0.0;
	for (int i = 0; i < 20; i++){
		float offset = float(i) * 0.2;
		float radius_disc = 0.8;
		float loop = fract((TIME + offset) * disc_speed) * radius_disc;
		float disc = smoothstep(cd, cd + 0.01, loop);
		float fade = abs(loop - radius_disc);
		fade = pow(fade, 5.0);
		disc *= fade;
		disc = clamp(disc, 0.0, 1.0);
		global_disc += disc;
	}
	global_disc *= 0.6;
	
	ALBEDO = color + global_disc;
}
Tags
hot spring, polar, pool, puddle, water
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.

More from NekotoArts

Omen’s Smoke Bomb from Valorant

Basic Vector Sprite Upscaling

HQ4X Shader (like in Emulators)

Related shaders

Pixel Art Water

Sprite Water Reflection Pixel Art Pokémon Style

Water Shader

Subscribe
Notify of
guest

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
morningking
morningking
2 years ago

this is great.

baiduwen3
1 month ago

godot4.3

shader_type spatial;

uniform sampler2D caustics_texture : hint_default_black;
uniform sampler2D color_gradient : source_color;
uniform sampler2D distort_noise : hint_default_black;

uniform float flow_speed = 0.3;
uniform float vignette_size = 0.3;
uniform float vignette_blend = 0.1;
uniform float distort_strength = 0.1;
uniform float disc_speed = 0.5;

vec2 polar_coordinates(vec2 uv, vec2 center, float zoom, float repeat)
{
vec2 dir = uv – center;
highp float radius = length(dir) * 2.0;
highp float angle = atan(dir.y, dir.x) / TAU;
return mod(vec2(radius * zoom, angle * repeat), 1.0);
}

void vertex(){
float dn = texture(distort_noise, UV + TIME * 0.1).r;
VERTEX.y += dn * 0.1;
}

void fragment(){
// Polar UVs + Noise for caustics
vec2 base_uv = UV;
float dn = texture(distort_noise, UV + TIME * 0.1).r;
base_uv += dn * distort_strength;
base_uv -= distort_strength / 2.0;
highp vec2 polar_uv = polar_coordinates(base_uv, vec2(0.5), 1.0, 1.0);
polar_uv.x -= TIME * flow_speed;
float caus = texture(caustics_texture, polar_uv).r;

// Fade out caustics
float cd = distance(UV, vec2(0.5));
float vign = 1.0 – smoothstep(vignette_size, vignette_size + vignette_blend, cd);

// Color the caustics
float grad_uv = caus * vign;
vec3 color = texture(color_gradient, vec2(grad_uv)).rgb;

// Center discs
float global_disc = 0.0;
for (int i = 0; i < 20; i++){
float offset = float(i) * 0.2;
float radius_disc = 0.8;
float loop = fract((TIME + offset) * disc_speed) * radius_disc;
float disc = smoothstep(cd, cd + 0.01, loop);
float fade = abs(loop – radius_disc);
fade = pow(fade, 5.0);
disc *= fade;
disc = clamp(disc, 0.0, 1.0);
global_disc += disc;
}
global_disc *= 0.6;

ALBEDO = color + global_disc;
}

jacobibanez
jacobibanez
15 days ago
Reply to  baiduwen3

thanks a lot!

jacobibanez
jacobibanez
15 days ago

How do you achieve that effect? I’m unable to do it 🙁