[Not A Tutorial] FPS-Independent Particles
Moderator: InsideQC Admins
2 posts
• Page 1 of 1
[Not A Tutorial] FPS-Independent Particles
These code snippets are intended for those who already know what they're doing, and will give you framerate-independent particles. No more craziness when running too fast or too slow. They're also suitable for moving particle position/velocity updates to the GPU, and can serve as a first step towards a more generic particle system that's not constrained to the builtin types.
Then neworg becomes the origin that is further modified to produce your particle. All modifications of p->org and p->vel get removed.
There are a few new things here.
p->spawntime is just the value of cl.time when the particle is spawned.
p->dvel is normally 0 but can have values depending on the emitter (explosions, generally) - pull these from the old type handling (which can otherwise be mostly removed).
grav is sv_gravity.value * 0.05
p->grav is 1 for particles that go up (rocket/grenade trails), -1 for particles that go down (most types) or 0 for particles that are unaffected by gravity.
Full drawing code (GL 3.x+ needed):
Shaders:
- Code: Select all
ptime = cl.time - p->spawntime;
neworg[0] = p->org[0] + (p->vel[0] + (p->dvel[0] * ptime)) * ptime;
neworg[1] = p->org[1] + (p->vel[1] + (p->dvel[1] * ptime)) * ptime;
neworg[2] = p->org[2] + (p->vel[2] + (p->dvel[2] * ptime) + (ptime * grav * p->grav)) * ptime;
Then neworg becomes the origin that is further modified to produce your particle. All modifications of p->org and p->vel get removed.
There are a few new things here.
p->spawntime is just the value of cl.time when the particle is spawned.
p->dvel is normally 0 but can have values depending on the emitter (explosions, generally) - pull these from the old type handling (which can otherwise be mostly removed).
grav is sv_gravity.value * 0.05
p->grav is 1 for particles that go up (rocket/grenade trails), -1 for particles that go down (most types) or 0 for particles that are unaffected by gravity.
Full drawing code (GL 3.x+ needed):
- Code: Select all
extern cvar_t sv_gravity;
typedef struct partinst_s
{
float org[3];
union
{
unsigned color;
byte rgba[4];
};
} partinst_t;
typedef struct partvert_s
{
float st[2];
} partvert_t;
#define MAX_PART_INST 16384
partinst_t partinsts[MAX_PART_INST];
partvert_t partverts[4] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
void R_DrawParticles (void)
{
particle_t *p, *kill;
float grav = sv_gravity.value * 0.05;
float ptime;
int numinst = 0;
// kill any particles at the head of the list
for (;;)
{
kill = active_particles;
if (kill && kill->die < cl.time)
{
active_particles = kill->next;
kill->next = free_particles;
free_particles = kill;
continue;
}
break;
}
if (!active_particles) return;
GL_BindProgram (gl_particleprog);
glUniform3fv (u_partrorigin, 1, r_origin);
glUniform3fv (u_partvpn, 1, vpn);
glUniform3fv (u_partvright, 1, vright);
glUniform3fv (u_partvup, 1, vup);
glEnable (GL_BLEND);
glDepthMask (GL_FALSE);
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, sizeof (partinst_t), partinsts[0].org);
glVertexAttribPointer (1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof (partinst_t), partinsts[0].rgba);
glVertexAttribPointer (2, 2, GL_FLOAT, GL_FALSE, sizeof (partvert_t), partverts->st);
glVertexAttribDivisor (0, 1);
glVertexAttribDivisor (1, 1);
for (numinst = 0, p = active_particles; p; p = p->next, numinst++)
{
// kill any particles in the middle of the list
for (;;)
{
kill = p->next;
if (kill && kill->die < cl.time)
{
p->next = kill->next;
kill->next = free_particles;
free_particles = kill;
continue;
}
break;
}
if (numinst == MAX_PART_INST)
{
glDrawArraysInstanced (GL_TRIANGLE_STRIP, 0, 4, numinst);
c_draw_calls++;
numinst = 0;
}
ptime = cl.time - p->spawntime;
partinsts[numinst].org[0] = p->org[0] + (p->vel[0] + (p->dvel[0] * ptime)) * ptime;
partinsts[numinst].org[1] = p->org[1] + (p->vel[1] + (p->dvel[1] * ptime)) * ptime;
partinsts[numinst].org[2] = p->org[2] + (p->vel[2] + (p->dvel[2] * ptime) + (ptime * grav * p->grav)) * ptime;
partinsts[numinst].color = d_8to24table[(int) p->color];
if (p->baseramp)
{
float ramptime = p->ramp + ptime * p->ramptime;
int ramp = (int) ramptime;
if (ramp > 10 || p->baseramp[ramp] == 0xff)
p->die = -1;
else p->color = p->baseramp[ramp];
}
}
if (numinst)
{
glDrawArraysInstanced (GL_TRIANGLE_STRIP, 0, 4, numinst);
c_draw_calls++;
}
glVertexAttribDivisor (0, 0);
glVertexAttribDivisor (1, 0);
glDepthMask (GL_TRUE);
glDisable (GL_BLEND);
}
Shaders:
- Code: Select all
uniform vec3 r_origin;
uniform vec3 vpn;
uniform vec3 vright;
uniform vec3 vup;
layout(location = 0) in vec4 position;
layout(location = 1) in vec4 color;
layout(location = 2) in vec4 texcoord;
void main ()
{
float scale = (1.0 + dot (position.xyz - r_origin, vpn) * 0.004) * 0.666;
gl_Position = gl_ModelViewProjectionMatrix * vec4 (position.xyz + (vright * scale * texcoord.t) + (vup * scale * texcoord.s), 1.0);
gl_FrontColor = color;
gl_TexCoord[0] = texcoord;
}
void main ()
{
vec4 color = gl_Color;
color.a = (1.0 - dot (gl_TexCoord[0].st, gl_TexCoord[0].st)) * 1.5;
gl_FragColor = color * gl_Color.a;
}
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
We knew the words, we knew the score, we knew what we were fighting for
-

mh - Posts: 2292
- Joined: Sat Jan 12, 2008 1:38 am
Re: [Not A Tutorial] FPS-Independent Particles
/me learns how instancing works in gl. :s
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
2 posts
• Page 1 of 1
Return to Programming Tutorials
Who is online
Users browsing this forum: No registered users and 1 guest