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 30c015d..65fa899 100644 --- a/addons/post_processing/shaders/particle_storm.gdshader +++ b/addons/post_processing/shaders/particle_storm.gdshader @@ -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); } \ No newline at end of file