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
32 changes: 19 additions & 13 deletions addons/post_processing/shaders/directional_drift_shader.gdshader
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down
157 changes: 96 additions & 61 deletions addons/post_processing/shaders/particle_storm.gdshader
Original file line number Diff line number Diff line change
Expand Up @@ -4,97 +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 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 wind-driven movement
vec2 wind_offset = wind_direction * current_time * velocity_variation;
// 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;

// 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;
// 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);
}