Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions addons/post_processing/node/children/directional_drift.tscn
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions addons/post_processing/node/post_process.gd
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ enum EffectType {
CIRCULAR_WAVES,
COLOR_CORRECTION,
CRT,
DIRECTIONAL_DRIFT,
FISHEYE,
GLITCH,
GRAIN,
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
30 changes: 29 additions & 1 deletion addons/post_processing/resource/post_processing_configuration.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
67 changes: 67 additions & 0 deletions addons/post_processing/shaders/directional_drift_shader.gdshader
Original file line number Diff line number Diff line change
@@ -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);
}
31 changes: 18 additions & 13 deletions addons/post_processing/shaders/particle_storm.gdshader
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;

Expand All @@ -35,18 +41,18 @@ 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);
}

// 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);
Expand All @@ -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;

Expand Down