Getting ready

We'll create two buffers (or a single interleaved buffer) to store our input attributes. The first buffer will store the initial velocity for each particle. We'll choose the values randomly from a limited range of possible vectors. To create the cone of particles in the previous image, we'll choose randomly from a set of vectors within the cone. We will tilt the cone toward some direction by applying a rotation matrix (emitterBasis). The following code is one way to do this:

glm::mat3 emitterBasis = ...; // Rotation matrix 
auto nParticles = 10000;
glGenBuffers(1, &initVel);
glBindBuffer(GL_ARRAY_BUFFER, initVel);
glBufferData(GL_ARRAY_BUFFER,
nParticles * sizeof(float) * 3, nullptr, GL_STATIC_DRAW);

glm::vec3 v(0); float velocity, theta, phi; std::vector<GLfloat> data(nParticles * 3); for( uint32_t i = 0; i < nParticles; i++ ) { // Pick the direction of the velocity theta = glm::mix(0.0f, glm::pi<float>() / 20.0f, randFloat()); phi = glm::mix(0.0f, glm::two_pi<float>(), randFloat()); v.x = sinf(theta) * cosf(phi); v.y = cosf(theta); v.z = sinf(theta) * sinf(phi); // Scale to set the magnitude of the velocity (speed) velocity = glm::mix(1.25f,1.5f,randFloat()); v = glm::normalize(emitterBasis * v) * velocity; data[3*i] = v.x; data[3*i+1] = v.y; data[3*i+2] = v.z; } glBindBuffer(GL_ARRAY_BUFFER, initVel); glBufferSubData(GL_ARRAY_BUFFER, 0, nParticles * 3 * sizeof(float), data.data());

In the previous code, the randFloat function returns a random value between zero and one. We pick random numbers within a range of possible values by using the GLM mix function (the GLM mix function works the same as the corresponding GLSL function—it performs a linear interpolation between the values of the first two arguments). Here, we choose a random float between zero and one and use that value to interpolate between the endpoints of our range.

To pick vectors from within our cone, we utilize spherical coordinates. The value of theta determines the angle between the center of the cone and the vector. The value of phi defines the possible directions around the axis for a given value of theta. For more on spherical coordinates, grab your favorite math book.

Once a direction is chosen, the vector is scaled to have a magnitude between 1.25 and 1.5. This is a range that seems to work well for the desired effect. The magnitude of the velocity vector is the overall speed of the particle, and we can tweak this range to get a wider variety of speeds or faster/slower particles.

The last three lines in the loop assign the vector to the appropriate location in the vector data. After the loop, we copy the data into the buffer referred to by initVel. Set up this buffer to provide data for vertex attribute zero.

In the second buffer, we'll store the start time for each particle. This will provide only a single float per vertex (particle). For this example, we'll just create each particle in succession at a fixed rate. The following code will set up a buffer with each particle created a fixed number of seconds after the previous one:

glGenBuffers(1, &startTime);
glBindBuffer(GL_ARRAY_BUFFER, startTime);
glBufferData(GL_ARRAY_BUFFER, nParticles * sizeof(float),
nullptr, GL_STATIC_DRAW);

float rate = particleLifetime / nParticles; for( uint32_t i = 0; i < nParticles; i++ ) { data[i] = rate * i; } glBindBuffer(GL_ARRAY_BUFFER, startTime); glBufferSubData(GL_ARRAY_BUFFER, 0, nParticles * sizeof(float), data.data());

This code simply creates an array of floats that starts at zero and gets incremented by rate. The array is then copied into the buffer referred to by startTime. Set this buffer to be the input for vertex attribute one.

Before continuing, we set the divisor for both attributes to one. This ensures that all vertices of a particle will receive the same value for the attributes:

glVertexAttribDivisor(0,1);
glVertexAttribDivisor(1,1);

The preceding commands should be executed while the vertex array object (VAO) is bound. The divisor information is stored within the VAO. See the example code for details.

The vertex shader has a number of uniform variables that control the simulation. Set the following uniform variables from within the OpenGL program:

  • ParticleTex: The particle's texture
  • Time: The amount of time that has elapsed since the animation began
  • Gravity: The vector representing one half of the acceleration in the previous equation
  • ParticleLifetime: Defines how long a particle survives after it is created
  • ParticleSize:  Size of the particle
  • EmitterPos:  The position of the particle emitter

Since we want our particles to be partially transparent, we enable alpha blending using the following statements:

glEnable(GL_BLEND); 
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
..................Content has been hidden....................

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