264 lines
11 KiB
Plaintext
264 lines
11 KiB
Plaintext
shader_type spatial;
|
|
render_mode blend_mix, depth_draw_never;
|
|
|
|
//Snow globals
|
|
global uniform float global_snow_start_time = -1.0;
|
|
global uniform float global_snow_accumulation_speed = 0.1;
|
|
global uniform float global_snow_melt_time = -1.0;
|
|
global uniform float global_snow_melt_speed = 0.1;
|
|
global uniform float global_snow_amount = 0.0;
|
|
global uniform float global_snow_threshold = 0.5;
|
|
global uniform vec4 global_snow_color = vec4(0.92, 0.96, 1.0, 1.0);
|
|
global uniform float global_snow_max_accumulation = 0.72;
|
|
global uniform bool global_snow_cap_enabled = true;
|
|
global uniform float global_snow_cap_height = 0.06;
|
|
global uniform float global_snow_cap_flatness_start = 0.72;
|
|
global uniform float global_snow_cap_flatness_end = 0.96;
|
|
global uniform float global_snow_cap_noise_scale = 0.22;
|
|
global uniform float global_snow_cap_noise_strength = 0.18;
|
|
uniform float snow_noise_scale : hint_range(0.01, 1.0) = 0.15;
|
|
uniform float snow_edge_softness : hint_range(0.01, 0.5) = 0.2;
|
|
uniform float snow_roughness_variation : hint_range(0.0, 0.3) = 0.15;
|
|
uniform float snow_color_variation : hint_range(0.0, 0.15) = 0.05;
|
|
|
|
//Rain globals
|
|
global uniform float global_rain_intensity;
|
|
global uniform float global_rain_puddle_amount;
|
|
uniform float ripple_scale : hint_range(0.1, 5.0) = 1.5;
|
|
uniform float ripple_speed : hint_range(0.5, 5.0) = 2.0;
|
|
uniform float ripple_layers : hint_range(1.0, 4.0) = 3.0;
|
|
uniform float streak_scale : hint_range(0.1, 5.0) = 2.0;
|
|
uniform float streak_speed : hint_range(0.1, 3.0) = 0.8;
|
|
uniform float wetness_darkening : hint_range(0.0, 0.5) = 0.25;
|
|
uniform float wet_roughness : hint_range(0.0, 0.3) = 0.05;
|
|
uniform float rain_normal_strength : hint_range(0.0, 1.0) = 0.4;
|
|
uniform float puddle_noise_scale : hint_range(0.01, 0.5) = 0.08;
|
|
uniform float puddle_threshold : hint_range(0.0, 0.8) = 0.45;
|
|
|
|
uniform sampler2D noise_texture : filter_linear_mipmap, repeat_enable;
|
|
|
|
varying float v_snow_amount;
|
|
varying float v_snow_cap_mask;
|
|
|
|
float hash(vec2 p) {
|
|
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
}
|
|
|
|
float value_noise(vec2 p) {
|
|
vec2 i = floor(p);
|
|
vec2 f = fract(p);
|
|
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
|
|
float a = hash(i);
|
|
float b = hash(i + vec2(1.0, 0.0));
|
|
float c = hash(i + vec2(0.0, 1.0));
|
|
float d = hash(i + vec2(1.0, 1.0));
|
|
|
|
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
|
}
|
|
|
|
float fbm(vec2 p) {
|
|
float value = 0.0;
|
|
float amplitude = 0.5;
|
|
float frequency = 1.0;
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
value += value_noise(p * frequency) * amplitude;
|
|
frequency *= 2.0;
|
|
amplitude *= 0.5;
|
|
}
|
|
|
|
return clamp(value, 0.0, 1.0);
|
|
}
|
|
|
|
float get_snow_progress() {
|
|
bool has_snow_timeline = global_snow_start_time >= 0.0 || global_snow_melt_time >= 0.0;
|
|
float snow_progress = clamp(global_snow_amount, 0.0, 1.0);
|
|
|
|
if (has_snow_timeline) {
|
|
snow_progress = 0.0;
|
|
if (global_snow_start_time >= 0.0) {
|
|
snow_progress = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0);
|
|
}
|
|
if (global_snow_melt_time >= 0.0) {
|
|
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
|
|
snow_progress *= (1.0 - melt);
|
|
}
|
|
}
|
|
|
|
return snow_progress;
|
|
}
|
|
|
|
float ripple_ring(vec2 uv, float time_offset) {
|
|
float d = length(uv);
|
|
float ring = sin(d * 30.0 - TIME * ripple_speed + time_offset) * 0.5 + 0.5;
|
|
float fade = smoothstep(0.5, 0.0, d);
|
|
return ring * fade;
|
|
}
|
|
|
|
void vertex() {
|
|
float snow_progress = get_snow_progress();
|
|
float snow_accumulation = snow_progress * global_snow_max_accumulation;
|
|
vec3 world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
|
vec3 world_normal = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
|
|
|
float flat_surface = smoothstep(
|
|
max(global_snow_threshold, global_snow_cap_flatness_start),
|
|
global_snow_cap_flatness_end,
|
|
world_normal.y
|
|
);
|
|
float accumulation_growth = smoothstep(0.08, 0.65, snow_progress);
|
|
float cap_noise = fbm(world_pos.xz * global_snow_cap_noise_scale + vec2(11.3, 4.7));
|
|
float cap_variation = mix(1.0 - global_snow_cap_noise_strength, 1.0 + global_snow_cap_noise_strength, cap_noise);
|
|
|
|
v_snow_amount = snow_progress;
|
|
v_snow_cap_mask = flat_surface * accumulation_growth * cap_variation * (global_snow_cap_enabled ? 1.0 : 0.0);
|
|
|
|
VERTEX += NORMAL * (global_snow_cap_height * snow_accumulation * v_snow_cap_mask);
|
|
}
|
|
|
|
void fragment() {
|
|
vec3 world_pos = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
|
vec3 world_normal = normalize((INV_VIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
|
|
float facing_up = clamp(world_normal.y, 0.0, 1.0);
|
|
float noise_val = texture(noise_texture, world_pos.xz * snow_noise_scale).r;
|
|
|
|
//Snow
|
|
float snow_progress = v_snow_amount;
|
|
float snow_accumulation = snow_progress * global_snow_max_accumulation;
|
|
|
|
float snow_edge = smoothstep(
|
|
global_snow_threshold - snow_edge_softness,
|
|
global_snow_threshold + snow_edge_softness,
|
|
facing_up + (noise_val - 0.5) * 0.4
|
|
);
|
|
float flat_accumulation = smoothstep(0.0, 0.35, v_snow_cap_mask) * snow_accumulation;
|
|
float snow_coverage = smoothstep(0.0, 0.6, noise_val + snow_progress - 0.4 + flat_accumulation * 0.25);
|
|
float snow_factor = max(snow_edge * snow_coverage * snow_progress, flat_accumulation);
|
|
float snow_opacity = smoothstep(0.04, 0.28, snow_factor);
|
|
snow_opacity = max(snow_opacity, flat_accumulation * 0.9);
|
|
|
|
float shade = mix(-snow_color_variation, snow_color_variation, noise_val);
|
|
vec3 snow_albedo = clamp(global_snow_color.rgb + vec3(shade), 0.0, 1.0);
|
|
snow_albedo = mix(snow_albedo, vec3(1.0), 0.18 + snow_opacity * 0.12);
|
|
float snow_rough = mix(0.15 - snow_roughness_variation, 0.15 + snow_roughness_variation, noise_val);
|
|
|
|
//Rain
|
|
float rain_int = clamp(global_rain_intensity, 0.0, 1.0);
|
|
float rain_factor = 0.0;
|
|
vec3 rain_albedo = vec3(0.0);
|
|
float rain_rough = wet_roughness;
|
|
float rain_metallic = 0.0;
|
|
vec3 rain_normal = vec3(0.5, 0.5, 1.0);
|
|
|
|
if (rain_int > 0.01 && facing_up > 0.3) {
|
|
float surface_mask = smoothstep(0.3, 0.6, facing_up);
|
|
|
|
//Slope detection: 0 = flat, 1 = peak at ~30-45 degrees
|
|
float slope_angle = 1.0 - facing_up;
|
|
float slope_mask = smoothstep(0.1, 0.25, slope_angle) * smoothstep(0.75, 0.5, slope_angle);
|
|
|
|
//Ripples on horizontal surfaces
|
|
float ripple = 0.0;
|
|
for (float i = 0.0; i < ripple_layers; i += 1.0) {
|
|
float phase = i * 2.17;
|
|
vec2 cell_offset = vec2(
|
|
texture(noise_texture, world_pos.xz * ripple_scale * 0.3 + vec2(phase)).r,
|
|
texture(noise_texture, world_pos.xz * ripple_scale * 0.3 + vec2(0.0, phase)).r
|
|
);
|
|
vec2 ripple_uv = fract(world_pos.xz * ripple_scale + cell_offset) - 0.5;
|
|
ripple += ripple_ring(ripple_uv, phase * 3.7);
|
|
}
|
|
ripple /= ripple_layers;
|
|
|
|
//Flowing water on inclined surfaces (30-45 degrees)
|
|
vec3 down_dir = normalize(world_normal - vec3(0.0, 1.0, 0.0) * facing_up);
|
|
float flow_speed = streak_speed * (1.0 + slope_angle * 2.0);
|
|
|
|
//Main flow rivulets
|
|
float flow_coord = dot(world_pos, down_dir);
|
|
float lateral_coord = dot(world_pos.xz, vec2(-down_dir.z, down_dir.x)) * streak_scale;
|
|
vec2 flow_uv = vec2(lateral_coord, flow_coord * streak_scale - TIME * flow_speed);
|
|
float flow_noise = texture(noise_texture, flow_uv * 0.4).r;
|
|
float flow2 = texture(noise_texture, flow_uv * 0.7 + vec2(0.3, TIME * flow_speed * 0.3)).r;
|
|
float rivulets = smoothstep(0.3, 0.7, flow_noise) * smoothstep(0.25, 0.6, flow2);
|
|
|
|
//Accumulation at bottom (lower world_pos.y gets more water)
|
|
float height_factor = texture(noise_texture, world_pos.xz * 0.05).r;
|
|
float accumulation = smoothstep(0.6, 0.2, fract(world_pos.y * 0.3 + height_factor * 0.5));
|
|
rivulets = mix(rivulets, rivulets + accumulation * 0.3, slope_mask);
|
|
|
|
float streak = rivulets * slope_mask;
|
|
|
|
//Blend ripples (flat) and streaks (inclined)
|
|
float flat_weight = smoothstep(0.3, 0.15, slope_angle);
|
|
float effect = mix(streak, ripple, flat_weight) * rain_int * surface_mask;
|
|
float darkness = wetness_darkening * rain_int * surface_mask;
|
|
|
|
vec2 normal_offset = vec2(dFdx(effect), dFdy(effect)) * rain_normal_strength;
|
|
rain_normal = vec3(normal_offset.x + 0.5, normal_offset.y + 0.5, 1.0);
|
|
rain_albedo = vec3(0.05, 0.05, 0.06);
|
|
rain_metallic = 0.15 * rain_int;
|
|
rain_factor = darkness + effect * 0.15;
|
|
}
|
|
|
|
//Puddles
|
|
float puddle_factor = 0.0;
|
|
float puddle_amt = clamp(global_rain_puddle_amount, 0.0, 1.0);
|
|
|
|
if (puddle_amt > 0.01 && facing_up > 0.85) {
|
|
float puddle_mask = smoothstep(0.85, 0.95, facing_up);
|
|
|
|
// Large-scale distribution: sparse zones where puddles can form
|
|
float distribution = texture(noise_texture, world_pos.xz * puddle_noise_scale * 0.15 + vec2(13.7, 7.3)).r;
|
|
float sparse_mask = smoothstep(0.55 - puddle_amt * 0.3, 0.35 - puddle_amt * 0.2, distribution);
|
|
|
|
// Multi-scale noise for organic puddle shapes
|
|
float n1 = texture(noise_texture, world_pos.xz * puddle_noise_scale).r;
|
|
float n2 = texture(noise_texture, world_pos.xz * puddle_noise_scale * 2.7 + vec2(5.7, 3.1)).r;
|
|
float n3 = texture(noise_texture, world_pos.xz * puddle_noise_scale * 5.3 + vec2(11.2, 8.9)).r;
|
|
float puddle_noise = n1 * 0.5 + n2 * 0.3 + n3 * 0.2;
|
|
|
|
// Puddles expand as puddle_amount increases
|
|
float threshold = puddle_threshold + (1.0 - puddle_amt) * 0.35;
|
|
float puddle_shape = smoothstep(threshold, threshold - 0.08, puddle_noise);
|
|
puddle_factor = puddle_shape * sparse_mask * puddle_mask * puddle_amt;
|
|
|
|
// Ripples inside puddles
|
|
if (puddle_factor > 0.1 && rain_int > 0.01) {
|
|
float puddle_ripple = 0.0;
|
|
for (float i = 0.0; i < 2.0; i += 1.0) {
|
|
float phase = i * 3.14;
|
|
vec2 r_uv = fract(world_pos.xz * ripple_scale * 0.8 + vec2(phase * 0.7)) - 0.5;
|
|
puddle_ripple += ripple_ring(r_uv, phase * 2.3);
|
|
}
|
|
puddle_ripple *= 0.5 * rain_int;
|
|
|
|
vec2 p_normal_offset = vec2(dFdx(puddle_ripple), dFdy(puddle_ripple)) * 0.6;
|
|
rain_normal = vec3(p_normal_offset.x + 0.5, p_normal_offset.y + 0.5, 1.0);
|
|
}
|
|
}
|
|
|
|
//Combine
|
|
//Puddles override rain when stronger
|
|
if (puddle_factor > rain_factor) {
|
|
rain_albedo = vec3(0.01, 0.01, 0.02);
|
|
rain_rough = 0.02;
|
|
rain_metallic = 0.5 * puddle_amt;
|
|
rain_factor = puddle_factor;
|
|
}
|
|
|
|
if (snow_factor > rain_factor) {
|
|
ALBEDO = snow_albedo;
|
|
ROUGHNESS = snow_rough;
|
|
METALLIC = 0.0;
|
|
ALPHA = snow_opacity;
|
|
} else {
|
|
ALBEDO = rain_albedo;
|
|
ROUGHNESS = rain_rough;
|
|
METALLIC = rain_metallic;
|
|
NORMAL_MAP = rain_normal;
|
|
ALPHA = rain_factor;
|
|
}
|
|
}
|