How to do it...

In the vertex shader, we have code that supports two passes: the update pass where the particles' position, age, and velocity are updated, and the render pass where the particles are drawn:

const float PI = 3.14159265359;
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexVelocity;
layout (location = 2) in float VertexAge;

// Render pass
uniform int Pass;

// Output to transform feedback buffers (pass 1)
out vec3 Position;
out vec3 Velocity;
out float Age;

// Out to fragment shader (pass 2)
out float Transp; // Transparency
out vec2 TexCoord; // Texture coordinate

// Uniform variables here... (omitted)

vec3 randomInitialVelocity() {
// Access the texture containing random velocities using gl_VertexID...
}

void update() {
if( VertexAge < 0 || VertexAge > ParticleLifetime ) {
// Recycle particle (or particle isn't born yet)
Position = Emitter;
Velocity = randomInitialVelocity();
if( VertexAge < 0 ) Age = VertexAge + DeltaT;
else Age = (VertexAge - ParticleLifetime) + DeltaT;
} else {
// The particle is alive, update.
Position = VertexPosition + VertexVelocity * DeltaT;
Velocity = VertexVelocity + Accel * DeltaT;
Age = VertexAge + DeltaT;
}
}

void render() {
Transp = 0.0;
vec3 posCam = vec3(0.0);
if(VertexAge >= 0.0) {
posCam = (MV * vec4(VertexPosition,1)).xyz + offsets[gl_VertexID] *
ParticleSize;
Transp = clamp(1.0 - VertexAge / ParticleLifetime, 0, 1);
}
TexCoord = texCoords[gl_VertexID];
gl_Position = Proj * vec4(posCam,1);
}

void main() {
if( Pass == 1 ) update();
else render();
}

The fragment shader code is simple and identical to that of the previous recipe.

After compiling the shader program, but before linking, use the following code to set up the connection between vertex shader output variables and output buffers:

const char * outputNames[] = { "Position", "Velocity", "Age" };
glTransformFeedbackVaryings(progHandle, 3, outputNames, GL_SEPARATE_ATTRIBS);

In the OpenGL render function, we'll use two passes. The first pass sends the particle positions to the vertex shader for updating, and capture the results using transform feedback. The input to the vertex shader will come from buffer A, and the output will be stored in buffer B. During this pass, we enable GL_RASTERIZER_DISCARD so that nothing is actually rendered to the framebuffer:

// Update pass
prog.setUniform("Pass", 1);

glEnable(GL_RASTERIZER_DISCARD);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, feedback[drawBuf]);
glBeginTransformFeedback(GL_POINTS);

glBindVertexArray(particleArray[1-drawBuf]);
glVertexAttribDivisor(0,0);
glVertexAttribDivisor(1,0);
glVertexAttribDivisor(2,0);
glDrawArrays(GL_POINTS, 0, nParticles);
glBindVertexArray(0);

glEndTransformFeedback();
glDisable(GL_RASTERIZER_DISCARD);

Note that we set the divisor to zero for all particle buffers and use glDrawArrays here. There's no need to use instancing here because we're not actually rendering the particles.

In the second pass, we use the output gathered from the first pass to render the particles using glDrawArraysInstanced:

// Render pass
prog.setUniform("Pass", 2);

glDepthMask(GL_FALSE);
glBindVertexArray(particleArray[drawBuf]);
glVertexAttribDivisor(0,1);
glVertexAttribDivisor(1,1);
glVertexAttribDivisor(2,1);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, nParticles);
glBindVertexArray(0);
glDepthMask(GL_TRUE);

Finally, we swap the buffers:

drawBuf = 1 - drawBuf; 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.94.171