Particle System

introduction

Particle Systems are well suited to using shaders. To the right is a supernova explosion simulation using shaders.

Usually a particle system is composed of many dynamic particles. The particles change with time in some way. In the system we will develop this section the particles change in a well defined way with time. Each particle has a number of parameters which define how they change. In this section we shall use the shader attributes to define these parameters and the shader shall calculate the position of each particle for a given uniform time.


particle attributes

In this section we shall create a particle system which appears as a fountain of particles falling under the influence of gravity

The attributes we shall define in our vertex shader will have a data type of vec4. The attribute name we shall called velocity. The components will be initialize to values as listed below .

we shall use the following code to initialize an array of floats which shall define the attributes for our shader.

        float[] velocity = new float[100000];

        int index = 0;

        for(int i = 0; i < velocity.length/4;i++)
        {
            velocity[index++] = (float) (10+2*Math.random());
            velocity[index++] = (float) (10+2*Math.random());
            velocity[index++] = (float) ( Math.random());
            velocity[index++] =  (float) (cycleTime*Math.random());
        }
            

We shall also define some uniform variables. They are listed here.

Below is the java code and shaders for this particle system

package particlesystem;

import core.Program;
import core.Projection;
import core.VBO;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

import static org.lwjgl.opengl.GL11.*;

public class ParticleSystem {

    public static void main(String arg[]) throws LWJGLException {

        DisplayMode mode = Display.getDesktopDisplayMode();
        float inverseAspectRatio = 1.0f * mode.getHeight() / mode.getWidth();
        Display.setDisplayMode(mode);

        Display.create();

        glViewport(0, 0, mode.getWidth(), mode.getHeight());


// **************************************************************
// construct the Program object

        Program program = new Program();

// load the shaders
        program.addShader("/particlesystem/glsl/particle.vert");
        program.addShader("/particlesystem/glsl/particle.frag");

// bind the two in variables of of the vertex shader
        program.bindAttribute("velocity");


// compile and link the shaders
        program.linkProgram();

// bind the program to make it current
        program.bind();

// define as using the uniform variable projectionMatrix within the vertex shader
        program.defineUniform("projectionMatrix");
        program.defineUniform("modelviewMatrix");
        program.defineUniform("time");
        program.defineUniform("cycleTime");
        program.defineUniform("acceleration");
         program.setUniform("acceleration",-9.8f);

        float left = -1.0f;
        float right = 1.0f;
        float bottom = -1.0f;
        float top = 1.0f;
        float near = 5;
        float far = 200;

        Matrix4f pMatrix = Projection.perspective(left, right, bottom * inverseAspectRatio, top * inverseAspectRatio, near, far);
        program.setUniform("projectionMatrix", false, pMatrix);

        float cycleTime = 3.0f;
         program.setUniform("cycleTime", cycleTime);

// **************************************************************
// construct the Vertex Buffer Object

        VBO vbo = new VBO();

// define and initialize attributes to be added to vbo
        float[] velocity = new float[100000];

        int index = 0;

        for(int i = 0; i < velocity.length/4;i++)
        {
            velocity[index++] = (float) (10+2*Math.random());
            velocity[index++] = (float) (10+2*Math.random());
            velocity[index++] = (float) ( Math.random());
            velocity[index++] =  (float) (cycleTime*Math.random());
        }

// set the bound attributes of the program defined above

        vbo.setAttributes("velocity", 4, velocity);

// construct vbo and set the type of geometric primitive to use.

        vbo.construct(GL_POINTS);

//
        Display.sync(60);
        Display.setVSyncEnabled(true);

        Matrix4f modelviewMatrix = new Matrix4f();

        float time = 0.0f;
        float startTime = System.nanoTime();


        while (!Display.isCloseRequested()) {

            time =  (float) ((System.nanoTime() - startTime)*1E-9);

            glClearColor(0.0f, 0.2f, 0.5f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);


            program.bind();

            modelviewMatrix.setIdentity();
            modelviewMatrix.translate(new Vector3f(-30,0,-180));
             program.setUniform("modelviewMatrix",false,modelviewMatrix);

             program.setUniform("time",time);

            // Bind the program
            // This is not really necessary here but may be if the application is using many shader
            // porgrams that each need to be bound in turn before rendering.

            // Render the vbo.
            vbo.render();
// For thoroughness unbind the program.
            program.unbind();
            Display.update();

        }
        vbo.delete();
        program.delete();

        Display.destroy();
        System.exit(0);
    }
}

            

The vertex shader.

#version 120

uniform mat4 projectionMatrix;
uniform mat4 modelviewMatrix;
uniform float acceleration;
uniform float time;
uniform float cycleTime;

attribute vec4 velocity;

vec4 position;
float t;


void main(void)
{

   if(time > velocity.w)
{
   t = time - velocity.w;
   t = mod(t,cycleTime);
   position = vec4(velocity.x*t,velocity.y*t + 0.5*acceleration*t*t,velocity.z*t ,1.0);
}

   gl_Position = projectionMatrix*modelviewMatrix*position;

}
            

The fragment shader.

                varying vec4 fragColor; 

void main(void)
{
   gl_FragColor = vec4(1.0,1.0,1.0,1.0);
}
            
Exercise :Make another attribute that defines the color of the particle. Initialize the colors to some slightly random values.