pub const KERNEL_PARTICLE_EMIT: &str = r#"
// Particle emission kernel
// Uses an atomic counter to allocate slots in the particle buffer.
layout(local_size_x = 64) in;
struct Particle {
vec4 pos_age;
vec4 vel_life;
};
layout(std430, binding = 1) writeonly buffer ParticlesOut {
Particle particles[];
};
layout(binding = 0, offset = 0) uniform atomic_uint u_alive_count;
uniform uint u_emit_count;
uniform uint u_max_particles;
uniform vec3 u_emitter_pos;
uniform float u_emitter_radius;
uniform float u_speed_min;
uniform float u_speed_max;
uniform vec3 u_direction;
uniform float u_spread;
uniform float u_life_min;
uniform float u_life_max;
uniform float u_time;
uniform uint u_seed;
uniform vec4 u_color_start;
uniform vec4 u_color_end;
// PCG random number generator
uint pcg(uint state) {
uint s = state * 747796405u + 2891336453u;
uint word = ((s >> ((s >> 28u) + 4u)) ^ s) * 277803737u;
return (word >> 22u) ^ word;
}
float rand01(inout uint seed) {
seed = pcg(seed);
return float(seed) / 4294967295.0;
}
vec3 random_direction(inout uint seed, vec3 dir, float spread) {
float phi = rand01(seed) * 6.283185307;
float cos_theta = 1.0 - rand01(seed) * spread;
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
vec3 random_dir = vec3(
sin_theta * cos(phi),
sin_theta * sin(phi),
cos_theta
);
// Rotate random_dir to align with dir
vec3 up = abs(dir.y) < 0.999 ? vec3(0, 1, 0) : vec3(1, 0, 0);
vec3 right = normalize(cross(up, dir));
up = cross(dir, right);
return right * random_dir.x + up * random_dir.y + dir * random_dir.z;
}
vec3 random_sphere(inout uint seed, float radius) {
float phi = rand01(seed) * 6.283185307;
float cos_theta = rand01(seed) * 2.0 - 1.0;
float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
float r = pow(rand01(seed), 1.0 / 3.0) * radius;
return r * vec3(sin_theta * cos(phi), sin_theta * sin(phi), cos_theta);
}
void main() {
uint idx = gl_GlobalInvocationID.x;
if (idx >= u_emit_count) return;
// Allocate a slot
uint slot = atomicCounterIncrement(u_alive_count);
if (slot >= u_max_particles) return;
// Seed RNG
uint seed = u_seed + idx * 1973u + uint(u_time * 1000.0) * 7919u;
// Random position within emitter sphere
vec3 pos = u_emitter_pos + random_sphere(seed, u_emitter_radius);
// Random direction with spread
vec3 dir = random_direction(seed, normalize(u_direction), u_spread);
// Random speed
float speed = mix(u_speed_min, u_speed_max, rand01(seed));
// Random lifetime
float life = mix(u_life_min, u_life_max, rand01(seed));
particles[slot].pos_age = vec4(pos, 0.0);
particles[slot].vel_life = vec4(dir * speed, life);
}
"#;Expand description
Particle emission kernel: spawn new particles using atomic counter.