- Edited
- Best Answerset by Erich_L
Ok. So here's the whole thing, sans the foam. Blending looks best if the final output is used as a normal map.
Flow map and animated procedural gradient tiles:
Displacement noise added to gradients and 4-tile weighted blending. Noise texture is Godot's procedural simplex noise.
Normal map calculated from gradient derivatives using shader's dFdx and dFdy functions:
Final shader:
This is just to demonstrate the whole approach. It can obviously be tweaked and improved.
Here's the shader code:
shader_type spatial;
render_mode async_visible,blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx;
uniform sampler2D texture_flow : hint_albedo;
uniform sampler2D texture_noise : hint_albedo;
uniform float wave_count : hint_range(1.0, 10.0, .5) = 3.0;
uniform float noise_strength : hint_range(0.0, 1.0, .05) = .4;
uniform float blend_zone : hint_range(.5, 1.0, .05) = 1.0;
// flow texture sampling function
vec2 sample_flow(ivec2 tile){
return texelFetch(texture_flow, clamp(tile, ivec2(0,0), textureSize(texture_flow, 0)), 0).rg;
}
// generate gradient pixel at given uv for given flow
// gradient used here is smoothstepped. We can use linear or sine gradients as well.
float wave_amplitude(vec2 flow, vec2 uv, float count){
float vel = length(flow)* 3.0 + 1.0; // velocity
vec2 dir = normalize(flow);
uv = mat2(vec2(dir.y, -dir.x), dir) * uv; // direction - rotate via 2x2 matrix
float grad = fract( TIME * vel + uv.x * count); // translate
return smoothstep(0.0, .5, grad) * smoothstep(1.0, .5, grad); // smoothstep the gradient
}
void fragment() {
ROUGHNESS = 0.4;
METALLIC = .5;
SPECULAR = .5;
vec2 uvtex = UV * vec2(textureSize(texture_flow, 0)); // texture coords in flowmap space
ivec2 tile = ivec2(uvtex); // tile index
vec2 tile_fract = fract(uvtex); // tile fraction
uvtex += texture(texture_noise, UV).rg * noise_strength; // uv noise
// sample 4 nearest tiles and do weighted blending
vec2 baseTile = floor(uvtex - vec2(.5)) + vec2(.5);
float a = 0.0;
float w_total = 0.0;
for(int j = 0; j<=1; ++j){
for(int i = 0; i<=1; ++i){
float d = length(uvtex - (baseTile + vec2(float(i), float(j))));
float w = blend_zone - clamp(d*d, 0.0, blend_zone);
a += wave_amplitude(sample_flow(ivec2(floor(baseTile)) + ivec2(i, j)), uvtex, wave_count) * w;
w_total += w;
}
}
a /= w_total;
// calculate normal
vec3 nmap = vec3(dFdx(a), dFdy(a), .3);
nmap = normalize(nmap);
NORMALMAP = nmap;
// some albedo
ALBEDO = vec3(0.0, 0.2, 1.0);
}