godot/servers/rendering/rasterizer_rd/shaders/particles.glsl

395 lines
9.3 KiB
GLSL

#[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
#define SAMPLER_NEAREST_CLAMP 0
#define SAMPLER_LINEAR_CLAMP 1
#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
#define SAMPLER_NEAREST_REPEAT 6
#define SAMPLER_LINEAR_REPEAT 7
#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
/* SET 0: GLOBAL DATA */
layout(set = 0, binding = 1) uniform sampler material_samplers[12];
layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalVariableData {
vec4 data[];
}
global_variables;
/* Set 1: FRAME AND PARTICLE DATA */
// a frame history is kept for trail deterministic behavior
struct FrameParams {
bool emitting;
float system_phase;
float prev_system_phase;
uint cycle;
float explosiveness;
float randomness;
float time;
float delta;
uint random_seed;
uint pad[3];
mat4 emission_transform;
};
layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
FrameParams data[];
}
frame_history;
struct ParticleData {
mat4 xform;
vec3 velocity;
bool is_active;
vec4 color;
vec4 custom;
};
layout(set = 1, binding = 1, std430) restrict buffer Particles {
ParticleData data[];
}
particles;
#define EMISSION_FLAG_HAS_POSITION 1
#define EMISSION_FLAG_HAS_ROTATION_SCALE 2
#define EMISSION_FLAG_HAS_VELOCITY 4
#define EMISSION_FLAG_HAS_COLOR 8
#define EMISSION_FLAG_HAS_CUSTOM 16
struct ParticleEmission {
mat4 xform;
vec3 velocity;
uint flags;
vec4 color;
vec4 custom;
};
layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmission {
int particle_count;
uint pad0;
uint pad1;
uint pad2;
ParticleEmission data[];
}
src_particles;
layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmission {
int particle_count;
int particle_max;
uint pad1;
uint pad2;
ParticleEmission data[];
}
dst_particles;
/* SET 2: MATERIAL */
#ifdef USE_MATERIAL_UNIFORMS
layout(set = 2, binding = 0, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
} material;
#endif
layout(push_constant, binding = 0, std430) uniform Params {
float lifetime;
bool clear;
uint total_particles;
uint trail_size;
bool use_fractional_delta;
bool sub_emitter_mode;
bool can_emit;
uint pad;
}
params;
uint hash(uint x) {
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
x = (x >> uint(16)) ^ x;
return x;
}
bool emit_particle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, uint p_flags) {
if (!params.can_emit) {
return false;
}
bool valid = false;
int dst_index = atomicAdd(dst_particles.particle_count, 1);
if (dst_index >= dst_particles.particle_max) {
atomicAdd(dst_particles.particle_count, -1);
return false;
}
/*
valid = true;
int attempts = 256; // never trust compute
while(attempts-- > 0) {
dst_index = dst_particles.particle_count;
if (dst_index == dst_particles.particle_max) {
return false; //can't emit anymore
}
if (atomicCompSwap(dst_particles.particle_count, dst_index, dst_index +1 ) != dst_index) {
continue;
}
valid=true;
break;
}
barrier();
if (!valid) {
return false; //gave up (attempts exhausted)
}
*/
dst_particles.data[dst_index].xform = p_xform;
dst_particles.data[dst_index].velocity = p_velocity;
dst_particles.data[dst_index].color = p_color;
dst_particles.data[dst_index].custom = p_custom;
dst_particles.data[dst_index].flags = p_flags;
return true;
}
/* clang-format off */
COMPUTE_SHADER_GLOBALS
/* clang-format on */
void main() {
uint particle = gl_GlobalInvocationID.x;
if (particle >= params.total_particles * params.trail_size) {
return; //discard
}
uint index = particle / params.trail_size;
uint frame = (particle % params.trail_size);
#define FRAME frame_history.data[frame]
#define PARTICLE particles.data[particle]
bool apply_forces = true;
bool apply_velocity = true;
float local_delta = FRAME.delta;
float mass = 1.0;
bool restart = false;
bool restart_position = false;
bool restart_rotation_scale = false;
bool restart_velocity = false;
bool restart_color = false;
bool restart_custom = false;
if (params.clear) {
PARTICLE.color = vec4(1.0);
PARTICLE.custom = vec4(0.0);
PARTICLE.velocity = vec3(0.0);
PARTICLE.is_active = false;
PARTICLE.xform = mat4(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(0.0, 0.0, 0.0, 1.0));
}
if (params.sub_emitter_mode) {
if (!PARTICLE.is_active) {
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
if (src_index >= 0) {
PARTICLE.is_active = true;
restart = true;
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_POSITION)) {
PARTICLE.xform[3] = src_particles.data[src_index].xform[3];
} else {
PARTICLE.xform[3] = vec4(0, 0, 0, 1);
restart_position = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_ROTATION_SCALE)) {
PARTICLE.xform[0] = src_particles.data[src_index].xform[0];
PARTICLE.xform[1] = src_particles.data[src_index].xform[1];
PARTICLE.xform[2] = src_particles.data[src_index].xform[2];
} else {
PARTICLE.xform[0] = vec4(1, 0, 0, 0);
PARTICLE.xform[1] = vec4(0, 1, 0, 0);
PARTICLE.xform[2] = vec4(0, 0, 1, 0);
restart_rotation_scale = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_VELOCITY)) {
PARTICLE.velocity = src_particles.data[src_index].velocity;
} else {
PARTICLE.velocity = vec3(0);
restart_velocity = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_COLOR)) {
PARTICLE.color = src_particles.data[src_index].color;
} else {
PARTICLE.color = vec4(1);
restart_color = true;
}
if (bool(src_particles.data[src_index].flags & EMISSION_FLAG_HAS_CUSTOM)) {
PARTICLE.custom = src_particles.data[src_index].custom;
} else {
PARTICLE.custom = vec4(0);
restart_custom = true;
}
}
}
} else if (FRAME.emitting) {
float restart_phase = float(index) / float(params.total_particles);
if (FRAME.randomness > 0.0) {
uint seed = FRAME.cycle;
if (restart_phase >= FRAME.system_phase) {
seed -= uint(1);
}
seed *= uint(params.total_particles);
seed += uint(index);
float random = float(hash(seed) % uint(65536)) / 65536.0;
restart_phase += FRAME.randomness * random * 1.0 / float(params.total_particles);
}
restart_phase *= (1.0 - FRAME.explosiveness);
if (FRAME.system_phase > FRAME.prev_system_phase) {
// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
if (restart_phase >= FRAME.prev_system_phase && restart_phase < FRAME.system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
}
}
} else if (FRAME.delta > 0.0) {
if (restart_phase >= FRAME.prev_system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (1.0 - restart_phase + FRAME.system_phase) * params.lifetime;
}
} else if (restart_phase < FRAME.system_phase) {
restart = true;
if (params.use_fractional_delta) {
local_delta = (FRAME.system_phase - restart_phase) * params.lifetime;
}
}
}
uint current_cycle = FRAME.cycle;
if (FRAME.system_phase < restart_phase) {
current_cycle -= uint(1);
}
uint particle_number = current_cycle * uint(params.total_particles) + particle;
if (restart) {
PARTICLE.is_active = FRAME.emitting;
restart_position = true;
restart_rotation_scale = true;
restart_velocity = true;
restart_color = true;
restart_custom = true;
}
}
if (PARTICLE.is_active) {
/* clang-format off */
COMPUTE_SHADER_CODE
/* clang-format on */
}
#if !defined(DISABLE_VELOCITY)
if (PARTICLE.is_active) {
PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
}
#endif
#if 0
if (PARTICLE.is_active) {
//execute shader
//!defined(DISABLE_FORCE)
if (false) {
vec3 force = vec3(0.0);
for (int i = 0; i < attractor_count; i++) {
vec3 rel_vec = xform[3].xyz - attractors[i].pos;
float dist = length(rel_vec);
if (attractors[i].radius < dist)
continue;
if (attractors[i].eat_radius > 0.0 && attractors[i].eat_radius > dist) {
out_velocity_active.a = 0.0;
}
rel_vec = normalize(rel_vec);
float attenuation = pow(dist / attractors[i].radius, attractors[i].attenuation);
if (attractors[i].dir == vec3(0.0)) {
//towards center
force += attractors[i].strength * rel_vec * attenuation * mass;
} else {
force += attractors[i].strength * attractors[i].dir * attenuation * mass;
}
}
out_velocity_active.xyz += force * local_delta;
}
#if !defined(DISABLE_VELOCITY)
if (true) {
xform[3].xyz += out_velocity_active.xyz * local_delta;
}
#endif
} else {
xform = mat4(0.0);
}
xform = transpose(xform);
out_velocity_active.a = mix(0.0, 1.0, shader_active);
out_xform_1 = xform[0];
out_xform_2 = xform[1];
out_xform_3 = xform[2];
#endif
}