Archived

This topic is now archived and is closed to further replies.

carb

Vertex Arrays for Particles?

Recommended Posts

Right now in my particle system, I''ve got 4 vertices denoting a quad, a few thousand particles, and each particle fools around with the transformation matrix in order to render a sprite from those vertices in the appropriate location. Performance is a little sluggish. I''ve got a few options. Point sprites has been suggested in earlier forum posts, but I''m told it''s still only supported by NV cards (correct me on this). The other is using vertex arrays ... But how to you use vertex arrays with only 4 vertices? How can I speed this bad boy up? What about a dynamic vertex array? Would it be beneficial to declare vertices for a few thousand particles, alter that data each frame (moving the vertices to a new position), and render? I''m just a little lost on the subject. I appreciate any advice/thoughts you can spare. - Ben

Share this post


Link to post
Share on other sites
if you have like 100 particles, then you get 400 vertices if you calculate their position yourself and put in all vertices for all particles in one big array then you only need to use one matrix for the position of everything and all vertices are alreadey calculated so then you only need to draw them all at once..

I have not tried this so I don't know if it would work.. it was just a thought I had


EDIT: maybe it will get slow to calculate the positions of the quads yourself?!

[edited by - McZ on March 18, 2004 3:43:31 AM]

Share this post


Link to post
Share on other sites
I don''t know about OpenGL, but on Direct3D I implemented a particle system with a simple dynamic vertex buffer that I push the pre-transformed particles in (ordered by texture), and it worked very fast. Calling a drawing function for every particle is probably much worse than doing one transform per particle (and you probably don''t need even a full 4x4 transform, just a translation).

Share this post


Link to post
Share on other sites
you can write a billboard shader in 9 instructions.

Basically, you can keep your VB on the graphics card, and just run that same static geometry through the shader. It''s very fast!

Here''s the code for the billboard shader


//Written by Navreet Gill
struct VSIN
{
float4 position: POSITION;
float4 uvIndexSize: TEXCOORD0;
float4 color: COLOR0;
};

struct VSOUT
{
float4 position: POSITION;
float4 color: COLOR0;
float2 tc: TEXCOORD0;
};

/*
z index: texCoords:

0------3 0,0------1,0
| | | |
| | | |
| | | |
1------2 0,1------1,1

*/

VSOUT main(VSIN IN,
uniform float4 offsets[4],
uniform float4x4 modelViewProj)
{
VSOUT OUT;

IN.position+=offsets[IN.uvIndexSize.z]*IN.uvIndexSize.w;

OUT.tc=IN.uvIndexSize.xy;
OUT.color = IN.color;
OUT.position = mul(modelViewProj, IN.position);

return OUT;
}


ofcourse, you have to provide the right offsets, and it seems you already know how to do that, but here it is for other people that might want to do it:

glGetFloatv(GL_MODELVIEW_MATRIX,modelViewMatrix);

//for index 0
offsetArray[0]=-modelViewMatrix[0]+modelViewMatrix[1];
offsetArray[1]=-modelViewMatrix[4]+modelViewMatrix[5];
offsetArray[2]=-modelViewMatrix[8]+modelViewMatrix[9];
offsetArray[3]=0;

offsetArray[4]=-modelViewMatrix[0]-modelViewMatrix[1];
offsetArray[5]=-modelViewMatrix[4]-modelViewMatrix[5];
offsetArray[6]=-modelViewMatrix[8]-modelViewMatrix[9];
offsetArray[7]=0;

offsetArray[8]=modelViewMatrix[0]-modelViewMatrix[1];
offsetArray[9]=modelViewMatrix[4]-modelViewMatrix[5];
offsetArray[10]=modelViewMatrix[8]-modelViewMatrix[9];
offsetArray[11]=0;

offsetArray[12]=modelViewMatrix[0]+modelViewMatrix[1];
offsetArray[13]=modelViewMatrix[4]+modelViewMatrix[5];
offsetArray[14]=modelViewMatrix[8]+modelViewMatrix[9];
offsetArray[15]=0;

Share this post


Link to post
Share on other sites
and then theres always the one big question: why even bother billboarding it in world space when you could just as well do it AFTER projection.


!!ARBvp1.0

PARAM ModProj[4] = {state.matrix.mvp};

TEMP pos, res;
MOV pos, vertex.normal;
MOV pos.w, 1;
DP4 res.x, ModProj[0], pos;
DP4 res.y, ModProj[1], pos;
DP4 res.z, ModProj[2], pos;
DP4 res.w, ModProj[3], pos;

MOV result.position, res;
MAD result.position.xy, vertex.position, vertex.texcoord, res;
ADD result.texcoord[1], vertex.position, {.5,.5,0,0};
MOV result.color, vertex.color;

END


you shouldnt go above 100 000 particles with that (50fps on a 2100+ amd and radeon 9800), maybe far less, depending on how complex your particle updates are. oh, and its only useful in immediate mode, because vertex arrays require tons of redundant updates for every "corner".


glBegin(GL_QUADS);
for (unsigned i=0; i //Update Particle (distance test, adjusting direction, moving)
glTexCoord2fv(size=Particles.Size);
glColor4fv(Particles[i].Col)
glNormal3fv(Particles[i].Pos);
glVertex2f(-.5f,-.5f);
glVertex2f(.5f,-.5f);
glVertex2f(.5f,.5f);
glVertex2f(-.5f,.5f);
}
glEnd();

Share this post


Link to post
Share on other sites
quote:
Original post by Trienco
and then theres always the one big question: why even bother billboarding it in world space when you could just as well do it AFTER projection.



Note: I can''t exactly figure out what your program is doing.

I couldn''t get the further particles to look smaller than the nearer particles. Does your technique fix this? If so, can you please go into more detail?

But, that was the reason why I did stuff the way I did.

Share this post


Link to post
Share on other sites
BTW - I''m kind of ignoring any kind of solution that involves shaders, because this game''ll probably be running on "legacy" hardware. People still have GeForce 2''s y''know

- Ben

Share this post


Link to post
Share on other sites
quote:
Original post by ngill
Note: I can''t exactly figure out what your program is doing.

I couldn''t get the further particles to look smaller than the nearer particles. Does your technique fix this? If so, can you please go into more detail?

But, that was the reason why I did stuff the way I did.


the position is transformed as usual, then:
MOV result.position, res;

could as well be limited to .zw.. those values dont change anymore

MAD result.position.xy, vertex.position, vertex.texcoord, res;
vertex.position actually contains the offset to the center as +-.5
this offset is multiplied with the particle size (x,y of texcoord) and added to the transformed vertex position.

ADD result.texcoord[1], vertex.position, {.5,.5,0,0};
tex coords are just offsets shifted into place

the confusing part: i expected the transformed vertex to already have x and y divided by w, but at this point it doesnt seem to be the case, so adding the fixed size works well and the "distance resize" is happening automatically.

if thats not the case you can either first multiply with the modelview alone, add the offsets and then multiply with projection or add offsets/res.w instead of offsets.

detail: the version above requires you to consider the aspect ration in the particle size (ie, its width should be height*aspect for quadratic billboards). thats not true anymore if you do modelview and projection in two steps (though then the particle size should be bigger).

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Just for the record, the fastest way to rotate a large number of quads to face the camera is to use a vertex shader (see O''Dell Hicks'' article in ShaderX^2 for details of one method). Without a vertex shader, you have to transform each quad individually.

Ben -

I''m really not sure if using a vertex array is going to be faster than changing the transformation matrix and calling a drawing function for each particle. It''s fewer drawing calls, but you''re pushing more data onto the graphics chip, and the CPU is doing work that the GPU does faster (and in parallel). It might not sound helpful, but I would suggest you try it and see. But I would also also look for ways to improve your current method. You say each particle "fools around with the transformation matrix". Be sure this bit of code is streamlined. The rotation part of your transformation matrix should be constant. It''s only the translation part that''s different. [Painless said as much already]

- rab

Share this post


Link to post
Share on other sites
quote:
Original post by carb
Performance is a little sluggish. I''ve got a few options. Point sprites has been suggested in earlier forum posts, but I''m told it''s still only supported by NV cards (correct me on this). The other is using vertex arrays ...


There is an ARB extension for point sprites now.
ARB_point_sprite
DirectX has been supporting point sprites for a long time now on ATI''s cards, so I''d assume that the ARB extension would work on ati and other cards aswell.

And, I''m pritty certain that point sprites are even faster than writing shaders.

Share this post


Link to post
Share on other sites
quote:
Original post by aboeing
And, I''m pritty certain that point sprites are even faster than writing shaders.


they are (about twice as fast as my shader approach, as it doesnt require tons of glxxx calls). unfortunately they''re also completely broken on ati and results range from screwed texturing to freezing your machine (which i guess simply translates to "crash the vpu").

Share this post


Link to post
Share on other sites