189 lines
5.3 KiB
GLSL
189 lines
5.3 KiB
GLSL
#version 450
|
|
|
|
#define PI 3.14159265f
|
|
|
|
layout(local_size_x = 128) in;
|
|
|
|
struct EmitterParameters
|
|
{
|
|
vec4 angularRangesAndOffsets;
|
|
float minSpeed;
|
|
float maxSpeed;
|
|
float rotationSpeed;
|
|
float rotationDecay;
|
|
vec4 lifeAndSizeRange; // min life, max life, min size, max size
|
|
float sizeDecay;
|
|
uint textureIndex;
|
|
float padding[2];
|
|
};
|
|
|
|
struct ParticleData
|
|
{
|
|
vec4 position;
|
|
vec4 orientationSpeedDecay;
|
|
vec4 velocity;
|
|
vec4 acceleration;
|
|
vec4 scaleAndDecay;
|
|
float life;
|
|
uint textureIndex;
|
|
};
|
|
|
|
struct GenericData
|
|
{
|
|
//! Delta time
|
|
float dt;
|
|
|
|
//! Elapsed time of the application
|
|
float elapsedTime;
|
|
|
|
//! Viewport width of the scene (excluding imgui, that means smaller than window)
|
|
uint viewportWidth;
|
|
|
|
//! Ditto but for height
|
|
uint viewportHeight;
|
|
};
|
|
|
|
layout (set = 0, binding = 0) uniform GenericDataBuffer
|
|
{
|
|
GenericData data;
|
|
} genericDataBuffer;
|
|
|
|
layout (std430, set = 2, binding = 0) readonly buffer EmitterBuffer
|
|
{
|
|
EmitterParameters data;
|
|
} emitterParams;
|
|
|
|
layout (std430, set = 2, binding = 1) coherent restrict buffer ParticlesInputBuffer
|
|
{
|
|
ParticleData data[];
|
|
} inputParticles;
|
|
|
|
// output buffer not needed
|
|
// layout (std430, set = 2, binding = 2) coherent restrict buffer ParticlesOutputBuffer
|
|
// {
|
|
// ParticleData data[];
|
|
// } outputParticles;
|
|
|
|
layout (std430, set = 2, binding = 3) coherent restrict buffer ParticlesFreelistBuffer
|
|
{
|
|
int freeCount;
|
|
int freeIndices[];
|
|
|
|
} freelist;
|
|
|
|
|
|
// push constants
|
|
layout(std140, push_constant) uniform EmitterPushConstant
|
|
{
|
|
vec4 emitterPosition;
|
|
uint emissionCount;
|
|
|
|
} emitterPushConstant;
|
|
|
|
uint pcg_hash(uint seed)
|
|
{
|
|
uint state = seed * 747796405u + 2891336453u;
|
|
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
|
|
return (word >> 22u) ^ word;
|
|
}
|
|
|
|
// Used to advance the PCG state.
|
|
uint rand_pcg(inout uint rng_state)
|
|
{
|
|
uint state = rng_state;
|
|
rng_state = rng_state * 747796405u + 2891336453u;
|
|
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
|
|
return (word >> 22u) ^ word;
|
|
}
|
|
|
|
// Advances the prng state and returns the corresponding random float.
|
|
float rand(inout uint state)
|
|
{
|
|
uint x = rand_pcg(state);
|
|
state = x;
|
|
return float(x)*uintBitsToFloat(0x2f800004u);
|
|
}
|
|
|
|
float map(float value, float inMin, float inMax, float outMin, float outMax)
|
|
{
|
|
return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
uint emitterInvocationIndex = gl_GlobalInvocationID.x;
|
|
vec4 emitterPosition = emitterPushConstant.emitterPosition;
|
|
vec4 angularRangesAndOffsets = emitterParams.data.angularRangesAndOffsets;
|
|
float minSpeed = emitterParams.data.minSpeed;
|
|
float maxSpeed = emitterParams.data.maxSpeed;
|
|
|
|
if (emitterInvocationIndex >= emitterPushConstant.emissionCount)
|
|
return;
|
|
|
|
// Freecount will start at max particles. Here we subtract every time we emit.
|
|
int freelistIndex = atomicAdd (freelist.freeCount, -1) - 1;
|
|
if (freelistIndex < 0)
|
|
atomicAdd (freelist.freeCount, 1);
|
|
|
|
ParticleData particle;
|
|
|
|
// Get seed for randomization
|
|
uint pixel_index = uint (emitterPosition.x + emitterPosition.y + floatBitsToUint(genericDataBuffer.data.elapsedTime) * (gl_GlobalInvocationID.x + 1));
|
|
uint seed = pcg_hash (pixel_index);
|
|
|
|
int index = freelist.freeIndices[freelistIndex];
|
|
|
|
// emit particle from emitter position
|
|
particle.position = vec4 (emitterPosition.xyz, 1.0f);
|
|
|
|
vec2 eulerAngles = vec2 (rand(seed) * angularRangesAndOffsets.x + angularRangesAndOffsets.z,
|
|
rand(seed) * angularRangesAndOffsets.y + angularRangesAndOffsets.w);
|
|
|
|
// Set its velocity
|
|
// particle.velocity.xyz = vec3 (cos(eulerAngles.x) * cos(eulerAngles.y),
|
|
// sin(eulerAngles.x) * cos(eulerAngles.y),
|
|
// sin(eulerAngles.y));
|
|
|
|
float bank = eulerAngles.y;
|
|
float cb = cos(bank);
|
|
float sb = sin(bank);
|
|
float ch = cos (eulerAngles.x);
|
|
float sh = sin (eulerAngles.x);
|
|
float cp = cos (0.0f);
|
|
float sp = sin (0.0f);
|
|
|
|
particle.velocity.xyz = mat3 (
|
|
(ch * cb + sh * sp * sb), (sb * cp), (-sh * cb + ch * sp * sb),
|
|
(-ch * sb + sh * sp * cb), (cb * cp), ( sb * sh + ch * sp * cb),
|
|
(sh * cp), (-sp), (ch * cp)
|
|
) * vec3 (1.0f, 0.0f, 0.0f);
|
|
|
|
|
|
particle.velocity *= map (rand (seed), 0.0f, 1.0f, minSpeed.x, maxSpeed.x);
|
|
|
|
// randomize life value that ranges from minLife to maxLife
|
|
particle.life = map (rand(seed), 0.0f, 1.0f, emitterParams.data.lifeAndSizeRange.x, emitterParams.data.lifeAndSizeRange.y);
|
|
|
|
float particleSize = map (rand(seed), 0.0f, 1.0f, emitterParams.data.lifeAndSizeRange.z, emitterParams.data.lifeAndSizeRange.w);
|
|
|
|
// Set size of particle
|
|
particle.scaleAndDecay.x = particleSize;
|
|
particle.scaleAndDecay.y = particleSize;
|
|
particle.scaleAndDecay.z = emitterParams.data.sizeDecay;
|
|
particle.scaleAndDecay.w = emitterParams.data.sizeDecay;
|
|
|
|
// Set the texture for the particle
|
|
particle.textureIndex = emitterParams.data.textureIndex;
|
|
|
|
// Set orientation and rotation speed
|
|
if (emitterParams.data.rotationSpeed != 0.0f)
|
|
particle.orientationSpeedDecay = vec4 (rand(seed) * PI, emitterParams.data.rotationSpeed, emitterParams.data.rotationDecay, 0.0f);
|
|
else
|
|
particle.orientationSpeedDecay = vec4 (0.0f);
|
|
|
|
|
|
particle.acceleration = vec4 (0.0f, -0.058f, 0.0f, 0.0f);
|
|
|
|
|
|
inputParticles.data[index] = particle;
|
|
} |