How to do it...

The vertex shader attributes include per-particle values and mesh values:

// Mesh attributes
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;

// Per-particle attributes
layout (location = 3) in vec3 ParticlePosition;
layout (location = 4) in vec3 ParticleVelocity;
layout (location = 5) in float ParticleAge;
layout (location = 6) in vec2 ParticleRotation;

We include output variables for transform feedback, used during the update pass, and for the fragment shader, used during the render pass:

// To transform feedback
out vec3 Position;
out vec3 Velocity;
out float Age;
out vec2 Rotation;

// To fragment shader
out vec3 fPosition;
out vec3 fNormal;

The update function (vertex shader) is similar to the one used in the previous recipe, however, here we update the particle's rotation as well:

void update() {
if( ParticleAge < 0 || ParticleAge > ParticleLifetime ) {
// The particle is past it's lifetime, recycle.
Position = Emitter;
Velocity = randomInitialVelocity();
Rotation = vec2( 0.0, randomInitialRotationalVelocity() );
if( ParticleAge < 0 ) Age = ParticleAge + DeltaT;
else Age = (ParticleAge - ParticleLifetime) + DeltaT;
} else {
// The particle is alive, update.
Position = ParticlePosition + ParticleVelocity * DeltaT;
Velocity = ParticleVelocity + Accel * DeltaT;
Rotation.x = mod( ParticleRotation.x + ParticleRotation.y
* DeltaT, 2.0 * PI );
Rotation.y = ParticleRotation.y;
Age = ParticleAge + DeltaT;
}
}

The render function (in the vertex shader) applies the rotation and translation using a matrix built from the particle's rotation and position attributes:

void render() {
float cs = cos(ParticleRotation.x);
float sn = sin(ParticleRotation.x);
mat4 rotationAndTranslation = mat4(
1, 0, 0, 0,
0, cs, sn, 0,
0, -sn, cs, 0,
ParticlePosition.x, ParticlePosition.y, ParticlePosition.z, 1
);
    mat4 m = MV * rotationAndTranslation;
fPosition = (m * vec4(VertexPosition, 1)).xyz;
fNormal = (m * vec4(VertexNormal, 0)).xyz;
gl_Position = Proj * vec4(fPosition, 1.0);
}

The fragment shader applies a shading model such as Blinn-Phong. The code is omitted here.

When invoking the transform feedback pass (the update pass), we disable the mesh attributes and set the divisor to zero for the particle attributes. We invoke the vertex shader for each particle using glDrawArrays:

glEnable(GL_RASTERIZER_DISCARD);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, feedback[drawBuf]);
glBeginTransformFeedback(GL_POINTS);
glBindVertexArray(particleArray[1-drawBuf]);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glVertexAttribDivisor(3,0);
glVertexAttribDivisor(4,0);
glVertexAttribDivisor(5,0);
glVertexAttribDivisor(6,0);
glDrawArrays(GL_POINTS, 0, nParticles);
glBindVertexArray(0);
glEndTransformFeedback();
glDisable(GL_RASTERIZER_DISCARD);;

To draw the particles, we re-enable the mesh attributes, set the divisor for the per-particle attributes to one, and draw the torus nParticles times using glDrawElementsInstanced

glBindVertexArray(particleArray[drawBuf]);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribDivisor(3,1);
glVertexAttribDivisor(4,1);
glVertexAttribDivisor(5,1);
glVertexAttribDivisor(6,1);
glDrawElementsInstanced(GL_TRIANGLES, torus.getNumVerts(),
GL_UNSIGNED_INT, 0, nParticles);

..................Content has been hidden....................

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