gpu based particle system

Started by
15 comments, last by ekba89 12 years, 8 months ago
I'm trying to create particle system based on gpu and i'm half way done. Currently i'm sending position and velocity to gpu and i do position += totalGameTime * velocity. My problem is i should have a reset or delete particle mechanism but i'm not sure how to make it. For example for snow particle should start from y position 50 when its y value is below 0. Here my problem begins. If i loop and calculate position of every particle to delete or reset in cpu there is no point calculating them in gpu. Any ideas how can i do it? Thanks.
Advertisement
What I did is using a lifetime value in the vertex data, and reserving a maximum amount of particles in the VBO. If the spawner can have up to 1.000 particles, then reserve vertex data for 1.000 particles.

Asides managing position and such, you can also store a particle lifetime. While it's higher than 0, the particle is "active". If it's <= 0, the particle will become invisible or "recycled" for a new one. In order to know if a particle be be used again or not, I numbered each particle with an ID (starting at 0,1,2, and so on).

<vertex shader>
if (particle.lifeTime <= 0.f)
{
// Reset?
if ( particle.id < currentMaxParticles )
{
// Recycle
particle.lifeTime = ...something higher than 0 again
particle // reset position, velocity, color, size, whatever
} else
{
particle.size = 0; // Keep it invisible by scaling it to 0, or place it somewhere far outside the world
}
}

With some extra intelligence in the timer, you can also make a spawn delay to prevent all particles getting spawned right after the start.

Rick
Thanks for reply. But as far as i know in vertex shader we can't change data in the buffer. So how can i change its lifetime in shader.

Here is my code. I have age already because i though it would be useful but as i said i don't know how can i change its real value from shader so i don't use it here. And currently i'm using particle system for snow so particles start from y position 50 and goes down with time. I should reset them after their y position is below 0.


BBVertexToPixel ParticleVS(float4 inPos : POSITION, float2 inTex : TEXCOORD0, float4 velocityAndAge : TEXCOORD1)
{
BBVertexToPixel Output = (BBVertexToPixel)0;

float3 position = inPos.xyz;

position += velocityAndAge.xyz * xTotalGameTime;

//other stuff for billboarding...
Two ways to fix that:
1- Store vertex results in a texture (reserve 1 pixel for each particle(vertex) on 2 or 3 textures (or packed together eventually). RGB for position, another RGB for velocity, A for lifetime, another A for...
2.-Use OpenGL Transform Feedback (not sure how its called in DX.... Vertex streaming?)

Option uses textures(FBO's / Rendertargets) to store the results. So read the positions/other data per vertex from a texture, add velocity / time, and write it back to the texture.

I would go for option 2 though, no textures needed. Here you write back data in the VBO, so, yes you can :)
What I do is making 1 vertex per particle, and update them all with a vertex shader (no fragment shader needed). Then next, a shader will render that VBO again, with a geometry shader. The geometry shaders makes billboard-sprites out of the single points. This allows to render big amounts of particles & no CPU needed at all.

Rick
Thanks again. I'm using directx so i'll look for vertex streaming and currently i'm using 4 points to create billboards so i'll also look for geometry shader solution too. If you know any good tutorial to start it would be very helpful.
You can make completely stateless particles on the GPU by allowing particles to respawn on the frame they die by treating their live-time as a looping value too. Of course, this trick only works if their spawn-rate is 1:1 with their lifetime and the particles are indeed purely stateless, but for many localised effects its pretty valid.

By purely stateless, I mean the state of every particle is purely a function of time and constant values. At startup you just set a buffer to have a single 'spawns at' time, and all other "Initial" parameters. This means nothing can change at runtime though, not even the emitter location. A particles existance is true as long as the effects instance age >= particle spawn-at time. Each particles' age is ('instance age' - 'spawn at') % 'particle lifetime'.

Works well for localised looping effects. I've used this mostly for fire embers and ash. In these cases i typically had a single float4 per sprite, 1 spawn-at time and 3 random variables. InitialPosition, InitialDirection, DirectionSpread, Initial Velocity, Gravity, VortexLine were shader parameters. All math was done in the vertex shader.

Where possible to use, these sorts of effects require zero CPU time beyond initialisation and issuing the draw call of the resulting effect (oh, and culling, but thats a given anyway) And the GPU doesnt need to feed back into VRAM either as its stateless.

Most annoying downside to this is, because there is no feedback you have to work out what the maximum boundingbox size can be manually.
"Of course, this trick only works if their spawn-rate is 1:1 with their lifetime"

It's perfectly reasonable to include some downtime as a ratio inside the particle data. You just draw a degenerate quad/tri if the particle is currently not running. By tweaking the origin times/respawn ratio times you can then generate effects like a fire which flares up and dies down.

"Of course, this trick only works if their spawn-rate is 1:1 with their lifetime"

It's perfectly reasonable to include some downtime as a ratio inside the particle data. You just draw a degenerate quad/tri if the particle is currently not running. By tweaking the origin times/respawn ratio times you can then generate effects like a fire which flares up and dies down.


This is true, and works because the hidden time is considered to be part of a single particles lifetime. Terminating the effect so all of the particles die off without needing to edit the data beyond a shader parameter isnt too hard either, its just the inverse of starting it up.
instead of an ID to controll how many Particles of hte maximum count should be active at a given time.
One can just include a propability in the respawn code.

e.g.

u have max 1000 particles but only want 200 active, in your update code u have something like


life_time -= time;
if(life_time < 0)
{
if( rnd() < 0.2)
life_time = start-life;
}

Thanks for advices but i think we are getting away from my real problem :D. I mean every method you said needs to update some values and here my problem begins. Where should i write them? For example i send initial positions with my buffer and as i mentioned above i find current position of particle by multiplying it with total elapsed game time. For example if i do position += velocity *elapsedTime this won't work because i always sent initial position with buffer. So i need to update and keep some values whether it be age or position. As far as i found out i need shader 4.0 and dx 10 to change buffer from hlsl and i'm using dx 9. As spek said i can use a texture for saving data but i don't like the idea having a texture for particle system. Also a side question :D do i need directx 10 to use geometry shaders and it is hard to find good tutorials for starting glsl so if you know some beginner tutorials for geometry shader i really appreciate. Thanks.

This topic is closed to new replies.

Advertisement