WebGL particle system.
This page's try to WebGL particle system.
<!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>