From 2eb757a39b69941de902d09e9ede5fac905823de Mon Sep 17 00:00:00 2001 From: Lou Bernardi Date: Tue, 21 Jan 2025 11:36:22 -0500 Subject: [PATCH 1/4] Added particle storm effect For sandstorm, snow, etc. --- .../node/children/particle_storm.tscn | 25 +++++ addons/post_processing/node/post_process.gd | 12 +++ .../resource/no_effects_example.tres | 43 ++++++++- .../resource/post_processing_configuration.gd | 35 +++++++ .../shaders/particle_storm.gdshader | 95 +++++++++++++++++++ 5 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 addons/post_processing/node/children/particle_storm.tscn create mode 100644 addons/post_processing/shaders/particle_storm.gdshader diff --git a/addons/post_processing/node/children/particle_storm.tscn b/addons/post_processing/node/children/particle_storm.tscn new file mode 100644 index 0000000..958c807 --- /dev/null +++ b/addons/post_processing/node/children/particle_storm.tscn @@ -0,0 +1,25 @@ +[gd_scene load_steps=3 format=3 uid="uid://c0d4h1un0350r"] + +[ext_resource type="Shader" path="res://addons/post_processing/shaders/particle_storm.gdshader" id="1_wnruh"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_yuki3"] +shader = ExtResource("1_wnruh") +shader_parameter/particle_color = Color(0.87, 0.78, 0.6, 1) +shader_parameter/wind_direction = Vector2(1, 0.2) +shader_parameter/wind_speed = 1.0 +shader_parameter/intensity = 0.5 +shader_parameter/chaos = 1.075 +shader_parameter/scale = 1.0 +shader_parameter/density = 1.0 + +[node name="particle_storm" type="CanvasLayer"] +visible = false + +[node name="data" type="ColorRect" parent="."] +material = SubResource("ShaderMaterial_yuki3") +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 31428c6..988abf4 100644 --- a/addons/post_processing/node/post_process.gd +++ b/addons/post_processing/node/post_process.gd @@ -15,6 +15,7 @@ enum EffectType { GRAIN, OUTLINE, PALETTE, + PARTICLE_STORM, PIXELATE, SCREEN_SHAKE, SPEED_LINES, @@ -112,6 +113,14 @@ func _update_shader_parameters( _type : EffectType, _material : Material) -> voi _material.set_shader_parameter("clip_warp", configuration.clip_warp) _material.set_shader_parameter("vignette_intensity", configuration.vignette_intensity) _material.set_shader_parameter("vignette_opacity", configuration.vignette_opacity) + EffectType.PARTICLE_STORM: # Add new case + _material.set_shader_parameter("particle_color", configuration.particle_storm_color) + _material.set_shader_parameter("wind_direction", configuration.particle_storm_wind_direction) + _material.set_shader_parameter("wind_speed", configuration.particle_storm_wind_speed) + _material.set_shader_parameter("intensity", configuration.particle_storm_intensity) + _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) _: push_error("Unhandled/Invalid EffectType: " + str(_type)) @@ -150,6 +159,8 @@ func _check_shader_visibility(_type: EffectType) -> bool: return configuration.SpeedLines EffectType.VIGNETTE: return configuration.Vignette + EffectType.PARTICLE_STORM: + return configuration.particle_storm _: push_error("Unhandled/Invalid EffectType: " + str(_type)) return false @@ -172,6 +183,7 @@ func _ready(): _add_canvas_layer_children("res://addons/post_processing/node/children/color_correction.tscn", EffectType.COLOR_CORRECTION) _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) var null_effects: PackedStringArray = [] for e in effects: diff --git a/addons/post_processing/resource/no_effects_example.tres b/addons/post_processing/resource/no_effects_example.tres index 3348330..ea4ce00 100644 --- a/addons/post_processing/resource/no_effects_example.tres +++ b/addons/post_processing/resource/no_effects_example.tres @@ -32,10 +32,6 @@ Outline = false OutlineColor = Color(0, 0, 0, 1) OutlineThreshold = 0.0 OutlineBlend = 0.01 -ScreenShake = false -ScreenShakePower = 0.1 -AnalogMonitor = false -AnalogMonitorResolution = Vector2(256, 256) Grain = false GrainPower = 75 CircularWaves = false @@ -47,3 +43,42 @@ SpeedLinesColor = Color(1, 1, 1, 1) SpeedLinesCount = 2 SpeedLineDensity = 0.072 SpeedLineSpeed = 20 +particle_storm = false +particle_storm_color = Color(0.87, 0.78, 0.6, 1) +particle_storm_intensity = 0.5 +particle_storm_chaos = 1.0 +particle_storm_scale = 1.0 +particle_storm_density = 1.0 +particle_storm_wind_direction = Vector2(1, 0.2) +particle_storm_wind_speed = 1.0 +ColorCorrection = false +ColorCorrectionTint = Color(0, 0, 0, 1) +ColorCorrectionBrightness = 0.0 +ColorCorrectionSaturation = 0.0 +Palette = false +Pixelate = false +PixelatePixelSize = 8 +CRT = false +overlay = false +scanlines_opacity = 0.4 +scanlines_width = 0.25 +grille_opacity = 0.3 +pixelate = true +roll_speed = 8.0 +roll_size = 15.0 +roll_variation = 1.8 +distort_intensity = 0.05 +noise_opacity = 0.4 +noise_speed = 5.0 +static_noise_intensity = 0.06 +aberration = 0.03 +brightness = 1.4 +discolor = true +warp_amount = 1.0 +clip_warp = false +vignette_intensity = 0.4 +vignette_opacity = 0.5 +AnalogMonitor = false +AnalogMonitorResolution = Vector2(256, 256) +ScreenShake = false +ScreenShakePower = 0.1 diff --git a/addons/post_processing/resource/post_processing_configuration.gd b/addons/post_processing/resource/post_processing_configuration.gd index 184e7fb..1e23bb4 100644 --- a/addons/post_processing/resource/post_processing_configuration.gd +++ b/addons/post_processing/resource/post_processing_configuration.gd @@ -166,6 +166,41 @@ class_name PostProcessingConfiguration extends Resource set(value): SpeedLineSpeed = value reload = true +@export_subgroup("Particle Storm") +@export var particle_storm: bool: + set(value): + particle_storm = value + reload = true +@export var particle_storm_color: Color = Color(0.87, 0.78, 0.60, 1.0): + set(value): + particle_storm_color = value + reload = true +@export_range(0.0, 10.0) var particle_storm_intensity = 0.5: + set(value): + particle_storm_intensity = value + reload = true +@export_range(0.0, 10.0) var particle_storm_chaos = 1.0: + set(value): + particle_storm_chaos = value + reload = true +@export_range(0.5, 10.0) var particle_storm_scale = 1.0: + set(value): + particle_storm_scale = value + reload = true +@export_range(0.0, 10.0) var particle_storm_density = 1.0: + set(value): + particle_storm_density = value + reload = true +@export var particle_storm_wind_direction = Vector2(1.0, 0.2): + set(value): + particle_storm_wind_direction = value + reload = true +@export_range(0.1, 5.0) var particle_storm_wind_speed = 1.0: + set(value): + particle_storm_wind_speed = value + reload = true + + @export_group("Display") @export_subgroup("Color Correction") diff --git a/addons/post_processing/shaders/particle_storm.gdshader b/addons/post_processing/shaders/particle_storm.gdshader new file mode 100644 index 0000000..30fa243 --- /dev/null +++ b/addons/post_processing/shaders/particle_storm.gdshader @@ -0,0 +1,95 @@ +shader_type canvas_item; + +// Visual appearance parameters +uniform vec4 particle_color : source_color = vec4(0.87, 0.78, 0.60, 1.0); +uniform vec2 wind_direction = vec2(1.0, 0.2); // Normalized direction vector +uniform float wind_speed = 1.0; // Movement speed multiplier +uniform float intensity = 0.5; // Overall particle visibility +uniform float chaos = 1.0; // Swirl movement intensity +uniform float scale = 1.0; // UV coordinate scaling factor +uniform float density = 1.0; // Particles per cell multiplier + +// Generates a stable pseudo-random 2D vector from a 2D input +vec2 hash22(vec2 seed_position) { + vec3 hash_input = fract(vec3(seed_position.xyx) * vec3(443.897, 441.423, 437.195)); + hash_input += dot(hash_input, hash_input.yzx + 19.19); + return fract((hash_input.xx + hash_input.yz) * hash_input.zy); +} + +// Calculates particle position based on time and initial conditions +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; + + // Calculate swirling motion parameters + float swirl_phase = current_time * (0.5 + particle_characteristics.y * 0.5); + float swirl_amplitude = 0.1 + particle_characteristics.x * 0.1; + vec2 swirl_offset = vec2( + 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); +} + +void fragment() { + // Transform UV coordinates to scaled space + 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; + + // Output final color with transparency + COLOR = vec4(particle_color.rgb, total_sand_density * particle_color.a); +} \ No newline at end of file From 644a23dc7f9fcff26dfbdfcfe2273bc7455e756d Mon Sep 17 00:00:00 2001 From: Lou Bernardi Date: Tue, 21 Jan 2025 11:42:52 -0500 Subject: [PATCH 2/4] Fixed ID used --- addons/post_processing/node/children/particle_storm.tscn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/post_processing/node/children/particle_storm.tscn b/addons/post_processing/node/children/particle_storm.tscn index 958c807..54d8fbe 100644 --- a/addons/post_processing/node/children/particle_storm.tscn +++ b/addons/post_processing/node/children/particle_storm.tscn @@ -2,7 +2,7 @@ [ext_resource type="Shader" path="res://addons/post_processing/shaders/particle_storm.gdshader" id="1_wnruh"] -[sub_resource type="ShaderMaterial" id="ShaderMaterial_yuki3"] +[sub_resource type="ShaderMaterial" id="ShaderMaterial_particle_storm"] shader = ExtResource("1_wnruh") shader_parameter/particle_color = Color(0.87, 0.78, 0.6, 1) shader_parameter/wind_direction = Vector2(1, 0.2) @@ -16,7 +16,7 @@ shader_parameter/density = 1.0 visible = false [node name="data" type="ColorRect" parent="."] -material = SubResource("ShaderMaterial_yuki3") +material = SubResource("ShaderMaterial_particle_storm") anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 From 37488815fac2f5ec6227110b169e0b0f35255e5b Mon Sep 17 00:00:00 2001 From: Lou Bernardi Date: Thu, 23 Jan 2025 18:30:19 -0500 Subject: [PATCH 3/4] Added directional drift shader + improved particle storm shader --- .../node/children/directional_drift.tscn | 24 +++++++ addons/post_processing/node/post_process.gd | 11 +++ .../resource/post_processing_configuration.gd | 30 ++++++++- .../shaders/directional_drift_shader.gdshader | 67 +++++++++++++++++++ .../shaders/particle_storm.gdshader | 36 +++++----- 5 files changed, 152 insertions(+), 16 deletions(-) create mode 100644 addons/post_processing/node/children/directional_drift.tscn create mode 100644 addons/post_processing/shaders/directional_drift_shader.gdshader 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..d69b430 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,14 +26,14 @@ 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; - + // Calculate swirling motion parameters float swirl_phase = current_time * (0.5 + particle_characteristics.y * 0.5); float swirl_amplitude = 0.1 + particle_characteristics.x * 0.1; @@ -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,39 +63,39 @@ 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; - + // Output final color with transparency COLOR = vec4(particle_color.rgb, total_sand_density * particle_color.a); } \ No newline at end of file From 2e3a550f5f8709f6f271d53a18436bdd82c23036 Mon Sep 17 00:00:00 2001 From: Lou Bernardi Date: Mon, 27 Jan 2025 12:17:11 -0500 Subject: [PATCH 4/4] Performance improvements directional drift and particle storm shaders --- .../shaders/directional_drift_shader.gdshader | 32 ++-- .../shaders/particle_storm.gdshader | 164 +++++++++++------- 2 files changed, 118 insertions(+), 78 deletions(-) diff --git a/addons/post_processing/shaders/directional_drift_shader.gdshader b/addons/post_processing/shaders/directional_drift_shader.gdshader index 94fb553..d6fda98 100644 --- a/addons/post_processing/shaders/directional_drift_shader.gdshader +++ b/addons/post_processing/shaders/directional_drift_shader.gdshader @@ -9,16 +9,24 @@ uniform vec4 particle_color: source_color = vec4(0.76, 0.69, 0.5, 0.3); // Base 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 vec2 normalized_flow_direction = vec2(0.89442719, 0.4472136); // Normalized direction vector (default 1.0, 0.5 normalized) 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 +// Optimized noise generation using combined cellular and gradient noise +// Provides good distribution while maintaining efficiency 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); + vec2 i = floor(position); + vec2 f = fract(position); + + // Generate vectors using simple but effective hash + vec2 hash = fract(sin(vec2( + dot(i, vec2(12.9898, 78.233)), + dot(i, vec2(43.5453, 28.179)) + )) * 43758.5453); + + // Mix in cellular noise characteristics for better particle distribution + return hash + 0.5 * sin(6.2831 * f); } // Smooth noise function for organic particle movement @@ -44,13 +52,11 @@ float create_smooth_noise(vec2 position) { } 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); + // Use pre-normalized direction vector to avoid per-fragment normalization + // Negate the direction to match expected coordinate system + vec2 time_offset = normalized_flow_direction * TIME * flow_speed * SPEED_SCALE; + vec2 primary_offset = UV - time_offset; + vec2 secondary_offset = UV - (time_offset * layer_velocity_ratio); // Generate two noise layers with different scales float primary_noise = create_smooth_noise(primary_offset * pattern_scale); diff --git a/addons/post_processing/shaders/particle_storm.gdshader b/addons/post_processing/shaders/particle_storm.gdshader index d69b430..c18d2b4 100644 --- a/addons/post_processing/shaders/particle_storm.gdshader +++ b/addons/post_processing/shaders/particle_storm.gdshader @@ -4,98 +4,132 @@ // snow flurries, or ash clouds with controllable wind direction and turbulence patterns. // Features customizable particle density, color, and chaotic movement behaviors. +/* +Parameters: +- particle_color: Base color of the particles (includes alpha) +- wind_direction: 2D vector for flow direction +- wind_speed: Overall movement speed +- intensity: Global particle visibility +- chaos: Amount of turbulent motion +- scale: Size of the particle system +- density: Base particle count multiplier +*/ + shader_type canvas_item; -// Visual appearance parameters -uniform vec4 particle_color : source_color = vec4(0.87, 0.78, 0.60, 1.0); -uniform vec2 wind_direction = vec2(1.0, 0.2); // Normalized direction vector -uniform float wind_speed = 1.0; // Movement speed multiplier -uniform float intensity = 0.5; // Overall particle visibility -uniform float chaos = 1.0; // Swirl movement intensity -uniform float scale = 1.0; // UV coordinate scaling factor -uniform float density = 1.0; // Particles per cell multiplier +// Uniform parameters that can be modified from outside the shader +uniform vec4 particle_color : source_color = vec4(0.87, 0.78, 0.60, 1.0); // Defaults to a sandy color +uniform vec2 wind_direction = vec2(1.0, 0.2); // Default wind blows right and slightly up +uniform float wind_speed = 1.0; // Base movement speed +uniform float intensity = 0.5; // Overall particle visibility +uniform float chaos = 1.0; // Amount of swirl/turbulence +uniform float scale = 1.0; // Overall size of the effect +uniform float density = 1.0; // Number of particles to render + +// Core constants for the particle system +const float TWO_PI = 6.28318530718; // Optimization: pre-calculated 2*PI +const float INVERSE_QUANTIZE = 0.166666667; // Optimization: pre-calculated 1.0/6.0 +const vec3 HASH_SCALE = vec3(443.897, 441.423, 437.195); // Prime numbers for pseudo-random generation +const float MIN_PARTICLE_RATIO = 0.25; // Minimum particles at edge of effect +const float BASE_PARTICLE_SIZE = 0.04; // Base size of each particle +const float PARTICLE_INDEX_OFFSET = 123.456; // Offset for particle position variation +const float HASH_OFFSET = 123.456; // Offset for hash calculation +const float SWIRL_SCALE = 0.1; // Base scale of swirling motion + +// Varying variables passed from vertex to fragment shader +varying float particle_size; // Calculated size of particles based on scale +varying float base_particles; // Number of particles to generate +varying float velocity_base; // Base velocity calculation +varying float velocity_range; // Range of velocity variation +varying float swirl_factor; // Optimization: pre-calculated swirl scale * chaos + +// Vertex shader: pre-calculate values that are constant for all fragments +void vertex() { + particle_size = BASE_PARTICLE_SIZE / scale; + base_particles = floor(4.0 + density * 12.0); + velocity_base = wind_speed * 0.7; // Base speed component + velocity_range = wind_speed * 0.6; // Variable speed component + swirl_factor = SWIRL_SCALE * chaos; // Pre-calculate swirl intensity +} -// Generates a stable pseudo-random 2D vector from a 2D input +// Generate a pseudo-random 2D vector from a 2D position +// Uses a combination of dot products and hash functions for stable randomization vec2 hash22(vec2 seed_position) { - vec3 hash_input = fract(vec3(seed_position.xyx) * vec3(443.897, 441.423, 437.195)); + vec3 hash_input = fract(vec3(seed_position.xyx) * HASH_SCALE); hash_input += dot(hash_input, hash_input.yzx + 19.19); return fract((hash_input.xx + hash_input.yz) * hash_input.zy); } -// Calculates particle position based on time and initial conditions -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; +// Calculate how many particles to render for a given grid cell +// Reduces particle count for cells further from center +float get_cell_particle_count(vec2 offset) { + float dist_squared = dot(offset, offset); + float falloff = 1.0 - smoothstep(1.0, 2.0, dist_squared); + return base_particles * max(MIN_PARTICLE_RATIO, falloff); +} - // Calculate swirling motion parameters - float swirl_phase = current_time * (0.5 + particle_characteristics.y * 0.5); - float swirl_amplitude = 0.1 + particle_characteristics.x * 0.1; +// Calculate the position of a specific particle +vec2 get_particle_pos(vec2 cell_position, float particle_index, float time_factor) { + // Generate unique seed for this particle + vec2 seed_offset = cell_position + particle_index * PARTICLE_INDEX_OFFSET; + vec2 initial_position = hash22(seed_offset); + vec2 particle_characteristics = hash22(initial_position * HASH_OFFSET); + + // Calculate particle-specific properties + float velocity = velocity_base + particle_characteristics.x * velocity_range; + float time_phase = time_factor * (0.5 + particle_characteristics.y * 0.5); + float pos_phase_x = initial_position.x * TWO_PI; + float pos_phase_y = initial_position.y * TWO_PI; + + // Combine wind and swirl movements + vec2 wind_offset = wind_direction * time_factor * velocity; vec2 swirl_offset = vec2( - 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 + sin(time_phase + pos_phase_y), + cos(time_phase + pos_phase_x) + ) * swirl_factor; + 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 +// Calculate the intensity of a pixel based on distance from particle center +float get_pixel(vec2 distance_to_center) { float square_distance = max(distance_to_center.x, distance_to_center.y); - return 1.0 - smoothstep(particle_size * 0.8, particle_size, square_distance); + float size_factor = particle_size * 0.8; + return 1.0 - smoothstep(size_factor, particle_size, square_distance); } +// Fragment shader: calculate the color of each pixel void fragment() { - // Transform UV coordinates to scaled space + // Calculate basic UV coordinates and time 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 + + // Process a 3x3 grid of cells around current position 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 offset = vec2(x, y); + vec2 neighbor_cell = current_cell + offset; + + // Get number of particles for this cell + float local_particle_count = get_cell_particle_count(offset); + + // Process each particle in the cell + for (float particle_idx = 0.0; particle_idx < local_particle_count; 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; + vec2 distance_to_center = abs(cell_local_uv - (particle_pos + offset)); + total_sand_density += get_pixel(distance_to_center) * 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; - - // Output final color with transparency + + // Apply intensity and quantization for final output + total_sand_density = clamp(total_sand_density * intensity, 0.0, 1.0); + total_sand_density = (floor(total_sand_density * 6.0 + 0.5)) * INVERSE_QUANTIZE; + + // Set final color with calculated alpha COLOR = vec4(particle_color.rgb, total_sand_density * particle_color.a); } \ No newline at end of file