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.005; 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() { float snow_progress = clamp(global_snow_amount, 0.0, 1.0); if (global_snow_start_time >= 0.0) { float timed_progress = clamp((TIME - global_snow_start_time) * global_snow_accumulation_speed, 0.0, 1.0); snow_progress = max(snow_progress, timed_progress); } 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 = min(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 ); float flat_accumulation = smoothstep(0.0, 0.35, v_snow_cap_mask) * snow_accumulation; float snow_variation = mix(0.75, 1.15, noise_val); float snow_coverage = clamp(snow_progress * snow_variation + flat_accumulation * 0.25, 0.0, 1.0); float snow_factor = max(snow_edge * snow_coverage, flat_accumulation); float snow_opacity = clamp(snow_factor, 0.0, 1.0); 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; } }