diff --git a/addons/post_processing/node/children/directional_drift.tscn b/addons/post_processing/node/children/directional_drift.tscn new file mode 100644 index 0000000..d0683db --- /dev/null +++ b/addons/post_processing/node/children/directional_drift.tscn @@ -0,0 +1,24 @@ +[gd_scene load_steps=3 format=3 uid="uid://xtejdfho465q"] + +[ext_resource type="Shader" path="res://addons/post_processing/shaders/directional_drift_shader.gdshader" id="1_5lpdk"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_directional_drift"] +shader = ExtResource("1_5lpdk") +shader_parameter/particle_color = Color(0.87, 0.78, 0.6, 1) +shader_parameter/particle_density = 0.3 +shader_parameter/flow_speed = 1.0 +shader_parameter/pattern_scale = 30.0 +shader_parameter/flow_direction = Vector2(1, 0.5) +shader_parameter/layer_velocity_ratio = 1.2 + +[node name="directional_drift" type="CanvasLayer"] +visible = false + +[node name="data" type="ColorRect" parent="."] +material = SubResource("ShaderMaterial_directional_drift") +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 diff --git a/addons/post_processing/node/post_process.gd b/addons/post_processing/node/post_process.gd index 988abf4..2e5b05e 100644 --- a/addons/post_processing/node/post_process.gd +++ b/addons/post_processing/node/post_process.gd @@ -10,6 +10,7 @@ enum EffectType { CIRCULAR_WAVES, COLOR_CORRECTION, CRT, + DIRECTIONAL_DRIFT, FISHEYE, GLITCH, GRAIN, @@ -121,6 +122,13 @@ func _update_shader_parameters( _type : EffectType, _material : Material) -> voi _material.set_shader_parameter("chaos", configuration.particle_storm_chaos) _material.set_shader_parameter("scale", configuration.particle_storm_scale) _material.set_shader_parameter("density", configuration.particle_storm_density) + EffectType.DIRECTIONAL_DRIFT: + _material.set_shader_parameter("particle_color", configuration.directional_drift_color) + _material.set_shader_parameter("particle_density", configuration.directional_drift_density) + _material.set_shader_parameter("flow_speed", configuration.directional_drift_speed) + _material.set_shader_parameter("pattern_scale", configuration.directional_drift_scale) + _material.set_shader_parameter("flow_direction", configuration.directional_drift_direction) + _material.set_shader_parameter("layer_velocity_ratio", configuration.directional_drift_layer_ratio) _: push_error("Unhandled/Invalid EffectType: " + str(_type)) @@ -161,6 +169,8 @@ func _check_shader_visibility(_type: EffectType) -> bool: return configuration.Vignette EffectType.PARTICLE_STORM: return configuration.particle_storm + EffectType.DIRECTIONAL_DRIFT: + return configuration.directional_drift _: push_error("Unhandled/Invalid EffectType: " + str(_type)) return false @@ -184,6 +194,7 @@ func _ready(): _add_canvas_layer_children("res://addons/post_processing/node/children/pixelate.tscn", EffectType.PIXELATE) _add_canvas_layer_children("res://addons/post_processing/node/children/palette.tscn", EffectType.PALETTE) _add_canvas_layer_children("res://addons/post_processing/node/children/particle_storm.tscn", EffectType.PARTICLE_STORM) + _add_canvas_layer_children("res://addons/post_processing/node/children/directional_drift.tscn", EffectType.DIRECTIONAL_DRIFT) var null_effects: PackedStringArray = [] for e in effects: diff --git a/addons/post_processing/resource/post_processing_configuration.gd b/addons/post_processing/resource/post_processing_configuration.gd index 1e23bb4..7d8aa73 100644 --- a/addons/post_processing/resource/post_processing_configuration.gd +++ b/addons/post_processing/resource/post_processing_configuration.gd @@ -199,7 +199,35 @@ class_name PostProcessingConfiguration extends Resource set(value): particle_storm_wind_speed = value reload = true - +@export_subgroup("Directional Drift") +@export var directional_drift: bool: + set(value): + directional_drift = value + reload = true +@export var directional_drift_color: Color = Color(0.76, 0.69, 0.5, 0.3): + set(value): + directional_drift_color = value + reload = true +@export_range(0.0, 3.0) var directional_drift_density = 0.3: + set(value): + directional_drift_density = value + reload = true +@export_range(0.0, 5.0) var directional_drift_speed = 1.0: + set(value): + directional_drift_speed = value + reload = true +@export var directional_drift_scale = 30.0: + set(value): + directional_drift_scale = value + reload = true +@export var directional_drift_direction = Vector2(1.0, 0.5): + set(value): + directional_drift_direction = value + reload = true +@export_range(0.1, 5.0) var directional_drift_layer_ratio = 1.2: + set(value): + directional_drift_layer_ratio = value + reload = true @export_group("Display") diff --git a/addons/post_processing/shaders/directional_drift_shader.gdshader b/addons/post_processing/shaders/directional_drift_shader.gdshader new file mode 100644 index 0000000..94fb553 --- /dev/null +++ b/addons/post_processing/shaders/directional_drift_shader.gdshader @@ -0,0 +1,67 @@ +// DirectionalDriftShader +// Copyright (c) 2025 Lou Bernardi (https://github.com/Loufe) +// A versatile shader for creating directional particle effects like sandstorms, snow, or mist +// using layered noise patterns for natural movement. + +shader_type canvas_item; + +uniform vec4 particle_color: source_color = vec4(0.76, 0.69, 0.5, 0.3); // Base color with alpha +uniform float particle_density: hint_range(0.0, 3.0) = 0.3; // Controls the amount of visible particles +uniform float flow_speed: hint_range(0.0, 5.0) = 1.0; // Base movement speed +uniform float pattern_scale = 30.0; // Controls size and distribution of particles +uniform vec2 flow_direction = vec2(1.0, 0.5); // Direction of particle movement +uniform float layer_velocity_ratio = 1.2; // Speed multiplier for second noise layer +const float SPEED_SCALE = 0.3; // Internal speed scaling factor (30%) + +// Enhanced noise generation for natural-looking distribution +vec2 generate_noise_vector(vec2 position) { + return fract(sin(vec2( + dot(position, vec2(127.1, 311.7)), + dot(position, vec2(269.5, 183.3)) + )) * 43758.5453123); +} + +// Smooth noise function for organic particle movement +float create_smooth_noise(vec2 position) { + vec2 integer_pos = floor(position); + vec2 fractional_pos = fract(position); + + // Smooth interpolation curve + fractional_pos = fractional_pos * fractional_pos * (3.0 - 2.0 * fractional_pos); + + // Sample points for bilinear interpolation + float point_a = length(generate_noise_vector(integer_pos)); + float point_b = length(generate_noise_vector(integer_pos + vec2(1.0, 0.0))); + float point_c = length(generate_noise_vector(integer_pos + vec2(0.0, 1.0))); + float point_d = length(generate_noise_vector(integer_pos + vec2(1.0, 1.0))); + + // Bilinear interpolation for smooth noise + return mix( + mix(point_a, point_b, fractional_pos.x), + mix(point_c, point_d, fractional_pos.x), + fractional_pos.y + ); +} + +void fragment() { + // Ensure consistent movement speed regardless of direction magnitude + vec2 normalized_direction = normalize(flow_direction); + + // Create two offset layers moving at different speeds for parallax effect + // Negate the normalized direction to match expected coordinate system + vec2 primary_offset = UV - (normalized_direction * TIME * flow_speed * SPEED_SCALE); + vec2 secondary_offset = UV - (normalized_direction * TIME * flow_speed * layer_velocity_ratio * SPEED_SCALE); + + // Generate two noise layers with different scales + float primary_noise = create_smooth_noise(primary_offset * pattern_scale); + float secondary_noise = create_smooth_noise(secondary_offset * (pattern_scale * 0.8)); + + // Blend layers with weighted contribution + float composite_noise = (primary_noise * 0.6 + secondary_noise * 0.4); + + // Create stepped appearance for distinct particles + float quantized_noise = floor(composite_noise * 4.0) / 4.0; + + // Apply final color and transparency + COLOR = vec4(particle_color.rgb, quantized_noise * particle_density * particle_color.a); +} \ No newline at end of file diff --git a/addons/post_processing/shaders/particle_storm.gdshader b/addons/post_processing/shaders/particle_storm.gdshader index 30fa243..30c015d 100644 --- a/addons/post_processing/shaders/particle_storm.gdshader +++ b/addons/post_processing/shaders/particle_storm.gdshader @@ -1,3 +1,9 @@ +// ParticleStormShader +// Copyright (c) 2025 Lou Bernardi (https://github.com/Loufe) +// A dynamic particle system shader designed to simulate natural phenomena like dust storms, +// snow flurries, or ash clouds with controllable wind direction and turbulence patterns. +// Features customizable particle density, color, and chaotic movement behaviors. + shader_type canvas_item; // Visual appearance parameters @@ -20,11 +26,11 @@ vec2 hash22(vec2 seed_position) { vec2 get_particle_pos(vec2 cell_position, float particle_index, float current_time) { // Generate initial position using cell and particle index vec2 initial_position = hash22(cell_position + particle_index * 123.456); - + // Generate unique movement characteristics for this particle vec2 particle_characteristics = hash22(initial_position * 123.456); float velocity_variation = 0.7 + particle_characteristics.x * 0.6; - + // Calculate wind-driven movement vec2 wind_offset = wind_direction * current_time * velocity_variation; @@ -35,7 +41,7 @@ vec2 get_particle_pos(vec2 cell_position, float particle_index, float current_ti sin(swirl_phase + initial_position.y * 6.28), cos(swirl_phase + initial_position.x * 6.28) ) * swirl_amplitude * chaos; - + // Combine movements and wrap around unit space return fract(initial_position + wind_offset + swirl_offset); } @@ -43,10 +49,10 @@ vec2 get_particle_pos(vec2 cell_position, float particle_index, float current_ti // Renders a single square particle with soft edges float get_pixel(vec2 current_uv, vec2 particle_center) { vec2 distance_to_center = abs(current_uv - particle_center); - + // Base particle size with scale adjustment float particle_size = 0.04 / scale; - + // Create soft-edged square shape float square_distance = max(distance_to_center.x, distance_to_center.y); return 1.0 - smoothstep(particle_size * 0.8, particle_size, square_distance); @@ -57,36 +63,35 @@ void fragment() { vec2 scaled_uv = UV * scale; vec2 current_cell = floor(scaled_uv); vec2 cell_local_uv = fract(scaled_uv); - + // Calculate time-based movement float current_time = TIME * wind_speed; - + float total_sand_density = 0.0; - + // Calculate particle count based on density parameter float particles_per_cell = floor(4.0 + density * 12.0); - + // Render particles from current and neighboring cells for (float x = -1.0; x <= 1.0; x++) { for (float y = -1.0; y <= 1.0; y++) { vec2 neighbor_offset = vec2(x, y); vec2 neighbor_cell = current_cell + neighbor_offset; - + // Generate and render all particles in this cell for (float particle_idx = 0.0; particle_idx < particles_per_cell; particle_idx++) { vec2 particle_pos = get_particle_pos(neighbor_cell, particle_idx, current_time); vec2 world_pos = (particle_pos + neighbor_offset); - + // Accumulate particle density total_sand_density += get_pixel(cell_local_uv, world_pos) * 0.25; } } } - // Apply intensity and clamp to valid range total_sand_density *= intensity; total_sand_density = clamp(total_sand_density, 0.0, 1.0); - + // Quantize density for sharper particle appearance total_sand_density = floor(total_sand_density * 6.0) / 6.0;