From 0be4b503e21268cb7cd0441a3b0d9c7773ba557e Mon Sep 17 00:00:00 2001 From: Overside srl Date: Tue, 31 Mar 2026 23:59:04 +0200 Subject: [PATCH] add rain puddle --- core/daynight/environment_management.tscn | 2 +- core/daynight/weather_controller.gd | 21 +++++- core/daynight/weather_overlay.gdshader | 87 +++++++++++++++++------ core/daynight/weather_overlay.tres | 18 +---- core/environment_config.gd | 2 + project.godot | 4 ++ 6 files changed, 94 insertions(+), 40 deletions(-) diff --git a/core/daynight/environment_management.tscn b/core/daynight/environment_management.tscn index 85d39db..611ed07 100644 --- a/core/daynight/environment_management.tscn +++ b/core/daynight/environment_management.tscn @@ -230,7 +230,7 @@ blur = NodePath("Blur") environment_shadows = NodePath("EnvironmentCloudsShadows") [node name="Particles_Fireflies" type="GPUParticles3D" parent="." unique_id=947538133] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 10, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0) emitting = false amount = 60 lifetime = 4.0 diff --git a/core/daynight/weather_controller.gd b/core/daynight/weather_controller.gd index bc085df..ddb4bdf 100644 --- a/core/daynight/weather_controller.gd +++ b/core/daynight/weather_controller.gd @@ -25,6 +25,8 @@ var timer_next_lightning: float = 0.0 var is_raining: bool = false var rain_intensity: float = 0.0 var rain_tween: Tween +var puddle_tween: Tween +var puddle_amount: float = 0.0 var snow_tween: Tween var snow_particles_tween: Tween @@ -81,6 +83,10 @@ func _ready() -> void: func _process(delta: float) -> void: _follow_camera() + + var is_night = day_time >= 2.5 + if particles_fireflies: + particles_fireflies.emitting = thereare_fireflies and is_night var base_tint: Color var base_sky_top: Color @@ -228,8 +234,9 @@ func _follow_camera() -> void: func toggle_fireflies(value: bool): thereare_fireflies = value + var is_night = day_time >= 2.5 if particles_fireflies: - particles_fireflies.emitting = thereare_fireflies + particles_fireflies.emitting = thereare_fireflies and is_night #disable fireflies and set default values and materials func set_fireflies(): @@ -390,6 +397,11 @@ func set_rain(): if particles_rain: particles_rain.visible = true particles_rain.emitting = false + RenderingServer.global_shader_parameter_set("global_rain_puddle_amount", 0.0) + +func set_puddle_amount(value: float): + puddle_amount = value + RenderingServer.global_shader_parameter_set("global_rain_puddle_amount", value) func toggle_rain(value: bool): is_raining = value @@ -403,17 +415,24 @@ func toggle_rain(value: bool): if rain_tween and rain_tween.is_valid(): rain_tween.kill() + if puddle_tween and puddle_tween.is_valid(): + puddle_tween.kill() + if is_raining: particles_rain.amount_ratio = 0.0 particles_rain.emitting = true rain_tween = create_tween() rain_tween.tween_property(particles_rain, "amount_ratio", 1.0, environment_config.rain_fade_time) + puddle_tween = create_tween() + puddle_tween.tween_method(set_puddle_amount, puddle_amount, 1.0, environment_config.puddle_form_time) else: rain_tween = create_tween() rain_tween.tween_property(particles_rain, "amount_ratio", 0.0, environment_config.rain_fade_time) rain_tween.tween_callback(func(): particles_rain.emitting = false trigger_after_rain()) + puddle_tween = create_tween() + puddle_tween.tween_method(set_puddle_amount, puddle_amount, 0.0, environment_config.puddle_dry_time) func trigger_morning() -> void: spawn_single_godray() diff --git a/core/daynight/weather_overlay.gdshader b/core/daynight/weather_overlay.gdshader index 106809c..d0b2efd 100644 --- a/core/daynight/weather_overlay.gdshader +++ b/core/daynight/weather_overlay.gdshader @@ -1,27 +1,21 @@ shader_type spatial; render_mode blend_mix, depth_draw_never; -// --- Snow globals --- +//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_threshold = 0.5; global uniform vec4 global_snow_color = vec4(0.92, 0.96, 1.0, 1.0); - -// --- Rain globals --- -global uniform float global_rain_intensity; - -// --- Shared --- -uniform sampler2D noise_texture : filter_linear_mipmap, repeat_enable; - -// --- Snow params --- 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 params --- +//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; @@ -30,6 +24,10 @@ 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; float ripple_ring(vec2 uv, float time_offset) { float d = length(uv); @@ -44,7 +42,7 @@ void fragment() { 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 ===================== + //Snow float snow_amount = 0.0; if (global_snow_start_time >= 0.0) { snow_amount = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0); @@ -66,7 +64,7 @@ void fragment() { vec3 snow_albedo = clamp(global_snow_color.rgb + vec3(shade), 0.0, 1.0); float snow_rough = mix(0.15 - snow_roughness_variation, 0.15 + snow_roughness_variation, noise_val); - // ===================== RAIN ===================== + //Rain float rain_int = clamp(global_rain_intensity, 0.0, 1.0); float rain_factor = 0.0; vec3 rain_albedo = vec3(0.0); @@ -77,11 +75,11 @@ void fragment() { 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 + //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 + //Ripples on horizontal surfaces float ripple = 0.0; for (float i = 0.0; i < ripple_layers; i += 1.0) { float phase = i * 2.17; @@ -94,11 +92,11 @@ void fragment() { } ripple /= ripple_layers; - // Flowing water on inclined surfaces (30-45 degrees) + //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 + //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); @@ -106,26 +104,71 @@ void fragment() { 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) + //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) + //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.02, 0.02, 0.03); - rain_metallic = 0.3 * rain_int; - rain_factor = effect * 0.8 + darkness; + 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; } - // ===================== COMBINE ===================== if (snow_factor > rain_factor) { ALBEDO = snow_albedo; ROUGHNESS = snow_rough; diff --git a/core/daynight/weather_overlay.tres b/core/daynight/weather_overlay.tres index 132ef95..8d78446 100644 --- a/core/daynight/weather_overlay.tres +++ b/core/daynight/weather_overlay.tres @@ -1,21 +1,7 @@ -[gd_resource type="ShaderMaterial" format=3] +[gd_resource type="ShaderMaterial" format=3 uid="uid://cn5vw7unbqgve"] -[ext_resource type="Shader" path="res://core/daynight/weather_overlay.gdshader" id="1_weather"] -[ext_resource type="NoiseTexture2D" uid="uid://bpswbjh4jj1ou" path="res://core/daynight/noise.tres" id="2_noise"] +[ext_resource type="Shader" uid="uid://dh3j2jp6defuk" path="res://core/daynight/weather_overlay.gdshader" id="1_weather"] [resource] render_priority = 0 shader = ExtResource("1_weather") -shader_parameter/noise_texture = ExtResource("2_noise") -shader_parameter/snow_noise_scale = 0.15 -shader_parameter/snow_edge_softness = 0.2 -shader_parameter/snow_roughness_variation = 0.15 -shader_parameter/snow_color_variation = 0.05 -shader_parameter/ripple_scale = 1.5 -shader_parameter/ripple_speed = 2.0 -shader_parameter/ripple_layers = 3.0 -shader_parameter/streak_scale = 2.0 -shader_parameter/streak_speed = 0.8 -shader_parameter/wetness_darkening = 0.25 -shader_parameter/wet_roughness = 0.05 -shader_parameter/rain_normal_strength = 0.4 diff --git a/core/environment_config.gd b/core/environment_config.gd index e15c61e..f380067 100644 --- a/core/environment_config.gd +++ b/core/environment_config.gd @@ -73,6 +73,8 @@ extends Resource @export_group("Rain") @export var rain_mode_color: Color = Color(0.5, 0.6, 0.7, 1.0) @export var rain_fade_time: float = 5.0 +@export var puddle_form_time: float = 15.0 +@export var puddle_dry_time: float = 20.0 @export_group("Lightning and Thunders") @export var lightning_min_time: float = 5.0 diff --git a/project.godot b/project.godot index aa3d994..4638021 100644 --- a/project.godot +++ b/project.godot @@ -103,3 +103,7 @@ global_rain_intensity={ "type": "float", "value": 0.0 } +global_rain_puddle_amount={ +"type": "float", +"value": 0.0 +}