• Advertisement
Sign in to follow this  

Particle Stretch

This topic is 4769 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm creating a 3D particle system, and now I want to add the ability for a sprite to stretch in the direction of it's velocity. I've seen lots of games incorporate this ability, but I can't find anything on the web about how you acheive this effect. Anyone know anything?

Share this post


Link to post
Share on other sites
Advertisement
I can think about two ways of making such an effect :

1) Motion blur your particles.
2) Keep track of their 'n' lastest positions and display them with increasing alpha. This will work only if your particles have slow motion. For high motion particles sample positions non-linearly along negative actual velocity vector. Or may be a combination of the two.

The second seems better but can consume a lot of memory if you have a lot of particles. I've never tried to do this but it seems an interesting challenge, I'm intreseted about your results.

ptl

Share this post


Link to post
Share on other sites
There are sveral articles out there on how you do billboarding along an axis. Do this, with the axis being your velocity vector.

Share this post


Link to post
Share on other sites
Snaily, is correct. I have used a particle system with his feature.
we called them "velocity aligned".. The particles rotated around the velocity vector axis.



We then had a stretch factor, this value determined how much
the particle stretches based on its velocity. So fast moving particles
would stretch out further than slow moving.

Obviously if you start making them too long realitive to their speed
they can look a bit weird with large changes of direction.

The next step up from this is more like ribbons, with more polys -
doom3 is a good place to look at an example of this more advanced version.
The built in particle editor allows you to mess with it.

Share this post


Link to post
Share on other sites
I've implemented a 'billboarding' effect I beleive in my particle system. It looks nice and the particle system already was very robust =D

I'll post some pictures for the guy who wanted to see the results.

This is a picture of the particle system with another effect I already created.


This shows how it draws the quads.


This is another texture I made in 2 minutes. I wasn't going for a blood look, but thats what it looks like ;)


All the code for the effect is in Render(), Update() has the collision code.

Share this post


Link to post
Share on other sites
phy:
nice work, how are your editing your particle systems?

also, are you going to try the more advanced ribbon type?

Share this post


Link to post
Share on other sites
The particles are all in a linked list, to create an effect I call a function (there are several) to initialize how many particles the effect needs and thats about it. Render() and Update() treat all particles equally based on their flags, of which I'm starting to run out of at the moment ;) I'm using 14/16 ( 2 bytes ). I'll post the particle structures and how the blending effect is created.

Straight out of 'r_particle.h'.

#define PRT_MF_NONE 0
#define PRT_MF_LINEAR 1
#define PRT_MF_GRAVITY 2
#define PRT_MF_COLLIDE 4
#define PRT_MF_BOUNCE 8
#define PRT_MF_REMOVE 16 // remove on collision
#define PRT_MF_COLLIDED 128 // DoCollision sets this (handled in Update) so don't mess with it!

#define PRT_RF_NONE 0
#define PRT_RF_ROTATE 1
#define PRT_RF_TWINKLE 2 // second sprite at inverse rotation
#define PRT_RF_FRAMES 4 // multiple frames
#define PRT_RF_NOLERP 8 // no color lerp
#define PRT_RF_LERPMODE 16 // 0-InvLinear 1-InvSqr
#define PRT_RF_ADDITIVE 32 // glBlendFunc( GL_SRC_COLOR, GL_ONE )
#define PRT_RF_STRETCH 64 // stretch along velocity
#define PRT_RF_STFIXED 128 // stretch length is fixed (8 * size)

struct lParticle
{
byte rf;
byte mf;
GLuint tex;
Vertex3f pos;
Vertex3f vel;
short nframes;
float fps;
Color4f scol;
Color4f ecol;
float rot;
float rotvel;
float size;
float bounce;
float birth;
float death;

lParticle* nextParticle;
};





This is the entire Render(), It runs pretty fast, I can have thousands of particles on screen with no noticable lag. Usually Update() is eating the CPU with particles that have collision, I'd say about 5-6ms per call with a couple hundred particles using collision (testing about 300 triangles).

//Player position
extern Vertex3f vPosition;

void prtParticleManager::Render( void )
{
int viewport[4];
glGetIntegerv( GL_VIEWPORT, viewport );
float mv[16];
glGetFloatv( GL_MODELVIEW_MATRIX, mv );

Matrix_3x3 mModelView( mv[0], mv[1], mv[2], mv[4], mv[5], mv[6], mv[8], mv[9], mv[10] );

float fTime = g_GetCurrentTime();

glEnable(GL_BLEND);
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
bool bAlphaFunc = true;

glDisable( GL_LIGHTING );
glEnable( GL_TEXTURE_2D );
glEnable( GL_DEPTH_TEST );

// Incorrect, but quick and easy
glDepthMask( GL_FALSE ); // Disable depth writing

lParticle *ptcl = BaseParticle;
GLuint ctex = 0;

while ( ptcl )
{
int frame = 0;
if ( ptcl->rf & PRT_RF_FRAMES )
{
float delta = fTime - ptcl->birth;
delta = (float)fmod( delta, ptcl->fps );
delta = delta / ptcl->fps * ptcl->nframes;
frame = (int)delta;
if ( frame == ptcl->nframes )
frame = 0;
}

if ( ctex != ptcl->tex + frame )
{
ctex = ptcl->tex + frame;
glBindTexture( GL_TEXTURE_2D, ctex );
}

if ( ptcl->rf & PRT_RF_ADDITIVE )
{
if ( bAlphaFunc )
{
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
bAlphaFunc = false;
}
}
else
{
if ( !bAlphaFunc )
{
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
bAlphaFunc = true;
}
}



// Removed this because I'm not using it and it's to slow,
// I also needed another space in the render flags ;)

// FIXME: This is WAAAYYY too slow and it doesn't work half the time.
// I'd bet that I could use traces and it would run faster and work beter
/* if ( ptcl->rf & PRT_RF_GLOW )
{
if ( bIsDepthTest )
{
glDisable( GL_DEPTH_TEST );
bIsDepthTest = false;
}
int screenpos[2];
float tpos[4];
float ppos[4];
float mtx[16];

glGetFloatv( GL_MODELVIEW_MATRIX, mtx );
tpos[0] = mtx[0]*ptcl->pos[0] + mtx[4]*ptcl->pos[1] + mtx[8]*ptcl->pos[2] + mtx[12];
tpos[1] = mtx[1]*ptcl->pos[0] + mtx[5]*ptcl->pos[1] + mtx[9]*ptcl->pos[2] + mtx[13];
tpos[2] = mtx[2]*ptcl->pos[0] + mtx[6]*ptcl->pos[1] + mtx[10]*ptcl->pos[2] + mtx[14];
tpos[3] = mtx[3]*ptcl->pos[0] + mtx[7]*ptcl->pos[1] + mtx[11]*ptcl->pos[2] + mtx[15];

glGetFloatv( GL_PROJECTION_MATRIX, mtx );
ppos[0] = mtx[0]*tpos[0] + mtx[4]*tpos[1] + mtx[8]*tpos[2] + mtx[12]*tpos[3];
ppos[1] = mtx[1]*tpos[0] + mtx[5]*tpos[1] + mtx[9]*tpos[2] + mtx[13]*tpos[3];
ppos[2] = mtx[2]*tpos[0] + mtx[6]*tpos[1] + mtx[10]*tpos[2] + mtx[14]*tpos[3];
ppos[3] = mtx[3]*tpos[0] + mtx[7]*tpos[1] + mtx[11]*tpos[2] + mtx[15]*tpos[3];

ppos[0] /= ppos[3];
ppos[1] /= ppos[3];
ppos[2] /= ppos[3];
ppos[3] = 1.0f;

screenpos[0] = viewport[0] + (int)((1.0f + ppos[0]) * (float)viewport[2] / 2.0f);
screenpos[1] = viewport[1] + (int)((1.0f + ppos[1]) * (float)viewport[3] / 2.0f);

float fBuffValue;
glReadPixels( screenpos[0], screenpos[1], 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &fBuffValue );

if ( fBuffValue < ppos[2] )
{
ptcl = ptcl->nextParticle;

if ( ptcl )
goto Render_while;
else
goto Render_wend;
}
}
else
{
if ( !bIsDepthTest )
{
glEnable( GL_DEPTH_TEST );
bIsDepthTest = true;
}
}*/


Vertex3f points[4];
if ( ptcl->rf & PRT_RF_STRETCH )
{

Vertex3f forward;
float fVelSqrd = ptcl->vel.Sqr();
if ( !(ptcl->rf & PRT_RF_STFIXED) && (fVelSqrd * 0.1 >= ptcl->size*ptcl->size) )
{
forward = ptcl->vel * 0.1f;
}
else
{
float fInvSqrt = FastInvSqrt( fVelSqrd );
if ( ptcl->rf & PRT_RF_STFIXED )
{
forward = ptcl->vel * ((8 * ptcl->size) * fInvSqrt);
}
else
{
Vertex3f newvel = ptcl->vel;
newvel *= fInvSqrt;
forward = newvel;
}
}
Vertex3f right = vPosition - ptcl->pos;
Vertex3f up;
CrossProduct( &up, &forward, &right );
up *= FastInvSqrt( up.Sqr() ) * ptcl->size;
Vertex3f offset = forward * FastInvSqrt( forward.Sqr() ) * ptcl->size;

points[0] = ptcl->pos + (forward + offset) + up;
points[1] = ptcl->pos + (forward + offset) - up;
points[2] = ptcl->pos - (forward - offset) - up;
points[3] = ptcl->pos - (forward - offset) + up;
}
else
{
points[0].Set( -ptcl->size, ptcl->size, 0.0f );
points[1].Set( -ptcl->size, -ptcl->size, 0.0f );
points[2].Set( ptcl->size, -ptcl->size, 0.0f );
points[3].Set( ptcl->size, ptcl->size, 0.0f );

Matrix_3x3 mRotation;
Angle3f aSpriteAngle( 0.0f, ptcl->rot, 0.0f );
AngleMatrix( aSpriteAngle, mRotation );
mRotation *= mModelView;

// Rotate Points and add Position
points[0] *= mRotation;
points[1] *= mRotation;
points[2] *= mRotation;
points[3] *= mRotation;

points[0] += ptcl->pos;
points[1] += ptcl->pos;
points[2] += ptcl->pos;
points[3] += ptcl->pos;
}

// Caclulate colors
Color4f col;
if ( ptcl->rf & PRT_RF_NOLERP )
{
col = ptcl->scol;
}
else
{
float delta = (fTime - ptcl->birth) / (ptcl->death - ptcl->birth);

if ( ptcl->rf & PRT_RF_LERPMODE )
delta = delta * delta;

LerpColor4f( &col, &ptcl->scol, &ptcl->ecol, delta );
}

//FIXME? This might benefit from multitexture, but the way it works right now wont
glColor4fv( (float*)&col );
glBegin( GL_QUADS );
glTexCoord2f( 1.0f, 0.0f );
glVertex3fv( &points[0][0] );

glTexCoord2f( 0.0f, 0.0f );
glVertex3fv( &points[1][0] );

glTexCoord2f( 0.0f, 1.0f );
glVertex3fv( &points[2][0] );

glTexCoord2f( 1.0f, 1.0f );
glVertex3fv( &points[3][0] );

if ( ptcl->rf & PRT_RF_TWINKLE && var->IGetBool( var_particles_twinkle ) )
{
points[0].Set( -ptcl->size, ptcl->size, 0.0f );
points[1].Set( -ptcl->size, -ptcl->size, 0.0f );
points[2].Set( ptcl->size, -ptcl->size, 0.0f );
points[3].Set( ptcl->size, ptcl->size, 0.0f );

Matrix_3x3 mRotation;
Angle3f aSpriteAngle( 0.0f, /*360.0f*/ -ptcl->rot, 0.0f );
AngleMatrix( aSpriteAngle, mRotation );
mRotation *= mModelView;

points[0] *= mRotation;
points[1] *= mRotation;
points[2] *= mRotation;
points[3] *= mRotation;

points[0] += ptcl->pos;
points[1] += ptcl->pos;
points[2] += ptcl->pos;
points[3] += ptcl->pos;

//glBegin( GL_QUADS );
glTexCoord2f( 1.0f, 0.0f );
glVertex3fv( &points[0][0] );

glTexCoord2f( 0.0f, 0.0f );
glVertex3fv( &points[1][0] );

glTexCoord2f( 0.0f, 1.0f );
glVertex3fv( &points[2][0] );

glTexCoord2f( 1.0f, 1.0f );
glVertex3fv( &points[3][0] );
//glEnd();
}

glEnd();

ptcl = ptcl->nextParticle;
}

// Reset state machine
glDepthMask( GL_TRUE );
glDisable( GL_BLEND );
glEnable( GL_DEPTH_TEST );
}




Ribbons can come next, but I'm not sure how I will do them. I have to edit the particle struct to add data for them, and combining the beams... I might be able to add the offsets on each point and renormalize them, but that might be too slow.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement