WebGL particle system

WebGL particle system.

This page's try to WebGL particle system.

Tags: webgl

<!DOCTYPE html>
<html>
  <head>
    <title>WebGL particle system</title>
    <link rel="stylesheet" href="http://www.3daysofprogramming.com/playground/pg.css" type="text/css">
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL,Map,Set,Promise"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.7.1/gl-matrix-min.js"></script>
    <script src="http://www.3daysofprogramming.com/playground/examples/resources/js/stats.js"></script>
    <style>
      #container {
        position: relative;
        overflow: hidden;
      }

      #canvas {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <div id="container" class="main">
      <canvas id="canvas"></canvas>
    </div>

    <div>
      <input type="range" id="volume" min="1000" max="1000000" value="1000" step="1000">
      <label for="volume" id="volume_label">Volume</label>
    </div>
    <script>
      import { Particle } from './utils/graphics/webgl/object-utils';

      var gl = null;
      var canvas = null;
      var container = null;
      var glProgram = null;
      var fragmentShader = null;
      var vertexShader = null;

      var vertexPositionAttribute = null;
      var vertexColorAttribute = null;
      var particles = [];
      var positions = [];
      var colors = [];

      var pointLocationBuffer = null;
      var pointColorBuffer = null;

      var pMatrix = mat4.create();
      var mvMatrix = mat4.create();
      var normalMatrix = mat3.create();

      var paused = false;

      var NUM_PARTICLES = 10000;

      var volume = document.querySelector('#volume');
      var volumeLabel = document.querySelector('#volume_label');

      volume.value = NUM_PARTICLES;
      changeParticleSize(NUM_PARTICLES);

      volume.addEventListener('change', function (e) {
        changeParticleSize(e.target.value);
      });

      function changeParticleSize(value) {
        NUM_PARTICLES = value;
        volumeLabel.innerHTML = value;

        setupParticles(NUM_PARTICLES);
      }

      window.addEventListener('load', initWebGL);
      document.addEventListener('keyup', function (evt) {
        switch (evt.keyCode) {
          case 80: // 'p'
            paused = !paused;
            break;
        }
      });

      function initWebGL() {
        canvas = document.querySelector('#canvas');
        container = document.querySelector('#container');
        canvas.width = container.clientWidth;
        canvas.height = container.clientHeight;

        try {
          gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
        } catch (e) {
          console.error(e);
        }

        if (gl) {
          initShaders();
          setupParticles(NUM_PARTICLES);
          getMatrixUniforms();

          vertexPositionAttribute = gl.getAttribLocation(glProgram, 'aVertexPosition');
          vertexColorAttribute = gl.getAttribLocation(glProgram, 'aVertexColor');
          gl.enableVertexAttribArray(vertexPositionAttribute);
          gl.enableVertexAttribArray(vertexColorAttribute);

          pointLocationBuffer = gl.createBuffer();
          pointColorBuffer = gl.createBuffer();

          (function animLoop() {

            if (!paused) {
              setupWebGL();

              adjustParticles();
              setBufferData();
              setMatrixUniforms();
              drawScene();
            }

            requestAnimationFrame(animLoop);
          })();
        }
      }

      function setupWebGL() {
        // set the clear color to a shade of green
        gl.clearColor(0.1, 0.1, 0.1, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.enable(gl.DEPTH_TEST);
        gl.enable(gl.BLEND);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

        gl.viewport(0, 0, canvas.width, canvas.height);
        mat4.perspective(pMatrix, 45, canvas.width / canvas.height, 0.1, 100.0);
        mat4.identity(mvMatrix);
        mat4.translate(mvMatrix, mvMatrix, [0.0, 0.0, -4.0]);

        var invertedMatrix = mat3.create();
        mat3.fromMat4(invertedMatrix, mvMatrix);
        mat3.invert(normalMatrix, invertedMatrix);
        mat3.transpose(normalMatrix, normalMatrix);
      }

      function initShaders() {
        // get shader source
        var vsSource = vertexShaderSource();
        var fsSource = fragmentShaderSource();

        // compile shaders
        vertexShader = makeShader(vsSource, gl.VERTEX_SHADER);
        fragmentShader = makeShader(fsSource, gl.FRAGMENT_SHADER);

        // create program
        glProgram = gl.createProgram();

        // attach and link shaders to the program
        gl.attachShader(glProgram, vertexShader);
        gl.attachShader(glProgram, fragmentShader);
        gl.linkProgram(glProgram);

        if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) {
          alert('Unable to initialize the shader program.');
        }

        // use program
        gl.useProgram(glProgram);
      }

      function makeShader(source, type) {
        // compile the shader
        var shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
          alert(("Error compiling shader: " + (gl.getShaderInfoLog(shader))));
        }

        return shader;
      }

      function vertexShaderSource() {
        return "\n    attribute vec3 aVertexPosition;\n    attribute vec4 aVertexColor;\n\n    uniform mat4 uPMatrix;\n    uniform mat4 uMVMatrix;\n\n    varying vec4 color;\n\n    void main(void) {\n      color = aVertexColor;\n      gl_PointSize = 3.0;\n      gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition.xyz, 1.0);\n    }\n  ";
      }

      function fragmentShaderSource() {
        return "\n    varying highp vec4 color;\n\n    void main(void) {   \n      gl_FragColor = color;\n    }\n  ";
      }

      function drawScene() {
        gl.bindBuffer(gl.ARRAY_BUFFER, pointLocationBuffer);
        gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, pointColorBuffer);
        gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
        gl.drawArrays(gl.POINTS, 0, positions.length / 3);
      }

      function getMatrixUniforms() {
        glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, 'uPMatrix');
        glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, 'uMVMatrix');
      }

      function setMatrixUniforms() {
        gl.uniformMatrix4fv(glProgram.pMatrixUniform, false, pMatrix);
        gl.uniformMatrix4fv(glProgram.mvMatrixUniform, false, mvMatrix);
      }

      function setupParticles(num) {
        particles = [];
        for (var n = 0; n < num; ++n) {
          particles[n] = new Particle();
        }

        positions = [];
        for (var n$1 = 0; n$1 < particles.length; ++n$1) {
          positions.push(particles[n$1].position[0]);
          positions.push(particles[n$1].position[1]);
          positions.push(particles[n$1].position[2]);
        }

        colors = [];
        for (var n$2 = 0; n$2 < particles.length; ++n$2) {
          colors.push(particles[n$2].color[0]);
          colors.push(particles[n$2].color[1]);
          colors.push(particles[n$2].color[2]);
          colors.push(particles[n$2].color[3]);
        }
      }

      function adjustParticles() {
        for (var n = 0; n < particles.length; ++n) {
          particles[n].update();
        }

        positions = [];
        for (var n$1 = 0; n$1 < particles.length; ++n$1) {
          positions.push(particles[n$1].position[0]);
          positions.push(particles[n$1].position[1]);
          positions.push(particles[n$1].position[2]);
        }

        colors = [];
        for (var n$2 = 0; n$2 < particles.length; ++n$2) {
          colors.push(particles[n$2].color[0]);
          colors.push(particles[n$2].color[1]);
          colors.push(particles[n$2].color[2]);
          colors.push(particles[n$2].color[3]);
        }
      }

      function setBufferData() {
        gl.bindBuffer(gl.ARRAY_BUFFER, pointLocationBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
        pointLocationBuffer.itemSize = 3;
        pointLocationBuffer.numItems = positions.length / 3;
        gl.bindBuffer(gl.ARRAY_BUFFER, pointColorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
        pointColorBuffer.itemSize = 4;
        pointColorBuffer.numItems = colors.length / 4;
      }
    </script>
  </body>
</html>