gpu particle system problems

Started by
4 comments, last by ic0de 11 years, 5 months ago
So I tried simulating my particles in the vertex shader. I wrote a simple shader that moves point sprite particles based on a velocity vector passed via the colour buffer. Also passed through the alpha component of the colour buffer is the time of the particle's creation. I passed a simple gravity vector as velocity (0, -1.0, 0) and I ran into a problem. The particles fall alright but they start off far lower than they are supposed to. I'm thinking this might have to do with the fact that I'm passing data through the color buffer.

Vertex Shader:

uniform float Time;
uniform float scr_h;


void main()
{
vec4 newpos = gl_Vertex;

newpos.xyz += gl_Color.xyz*(Time - gl_Color.w); // move the sprite based on velocity

vec3 particle = vec3(gl_ModelViewMatrix * newpos); // scale point sprite for perspective
gl_PointSize = scr_h / sqrt(dot(particle, particle));

gl_Position = gl_ModelViewProjectionMatrix * newpos;
}


C++ part:

glUniform1f(ptime, float(SDL_GetTicks()/1000.0));
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glUniform1f(pr_scrh, float(screen->h));
glBindBuffer(GL_ARRAY_BUFFER, Vbuffer);
glBufferData(GL_ARRAY_BUFFER, particle::getNumParticles() * 3 * sizeof(float), particle::get(), GL_STATIC_DRAW);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, Tbuffer);
glBufferData(GL_ARRAY_BUFFER, particle::getNumParticles() * 4 * sizeof(float), particle::getVelocityTime(), GL_STATIC_DRAW);
glColorPointer(3, GL_FLOAT, 0, 0);
glDrawArrays(GL_POINTS, 0, particle::getNumParticles() * 3);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);


Anything obvious wrong with my code or even my approach?

P.S. don't bother telling me if anything I'm using is deprecated because this is targeted for OpenGL 2.1
Advertisement
On older hardware the color array may be interpolated - or just generally stored - with lower precision, as these values generally only need a 0-255 range. You're targetting GL2.1 so you can and should junk glVertexPointer/glColorPointer and use generic attrib arrays instead (glEnableVertexAttribArray/glVertexAttribPointer) - these are guaranteed available with GL2.0 and may even be available on lower versions that have the GL_ARB_vertex_program extension (beware of attrib index 3 on this older hardware as GL_ARB_vertex_program maps that to the color array - I saw the same happen once when storing texcoords at that index; won't be a problem with GL2.0 though).

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Your problem could be related to calling glBufferData every frame. Is there a reason you're doing this? The spawn time and original position of your particles does not ever change, and your vertex shader appears to assume that the velocity remains constant as well. If nothing else, calling the function only once when you first create the particle system should help your program in terms of efficiency.

Since you're simulating your particle's position using a single velocity value and its' point of origin. I think you'll find that particle effects will be rather boring when simulated that way. Allow me to suggest some alternative options:

Plain old CPU simulation! You can do something similar to what you're doing now (although I recommend glBufferSubData) to pass a 4 component (position.xyz, time) vector for each particle to your shader, along with uniforms for initial and final (or multiple over time) values for color and scale, and use your time value to interpolate between those in the shader. This will let you handle more complex particle physics such as acceleration while not changing your implementation too much.

If you really want to simulate particles using the vertex shader, I'd suggest using GL_ARB_TRANSFORM_FEEDBACK2 (or GL_EXT_TRANSFORM_FEEDBACK if the ARB version isn't available to you). This will allow you to write the output of a vertex shader back into a buffer, which means you can account for accelerations, changing velocity, and any other properties you may wish to update over time. Note that Transform Feedback probably won't let you write to more than one buffer at a time on GL 2, so you'd probably need to do multiple draw calls to update a system with changing velocity and position.

Another option for GL 2 is to use a pixel shader to do your particle physics, and store properties that change over time in floating-point textures. You can then use vertex texture fetch to read position, age, etc. from said textures in your vertex shader. This will require GL_ARB_texture_float, while you can look up textures in your vertex shader using texture2DLod.
Transform feedback isn't available at all on GL2.

A simple time/gravity/velocity equation on the vertex shader is a good option; something like this:
vec3 NewPosition = StartPosition + (Velocity + (DeltaVee * Time) + Gravity) * Time;

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

[font=georgia,serif]For particle systems, my strong recommendation is to use OpenCL. It's easier to write, more robust, and faster. And, IIRC, requires only a similar level of support as TF.[/font]

[size="1"]And a Unix user said rm -rf *.* and all was null and void...|There's no place like 127.0.0.1|The Application "Programmer" has unexpectedly quit. An error of type A.M. has occurred.
[size="2"]

Problem solved it was a stupid mistake.

This:


glColorPointer(3, GL_FLOAT, 0, 0);


Should be This:


glColorPointer(4, GL_FLOAT, 0, 0);


I'll probably switch to attribute arrays later just cause some hardware may not like me re-purposing the colour buffer.

also the reason I'm calling glBufferData every frame is cause this is just test code to try to make it work. For performance reasons it won't appear in the final code.

This topic is closed to new replies.

Advertisement