├── .gitignore ├── LICENSE.md ├── README.md ├── demo.js ├── index.html ├── package.json └── shaders ├── index.js ├── logic.frag ├── logic.vert ├── render.frag └── render.vert /.gitignore: -------------------------------------------------------------------------------- 1 | bundle.js 2 | node_modules 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## The MIT License (MIT) ## 2 | 3 | Copyright (c) 2013 Hugh Kennedy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # particle-excess-demo [![Flattr this!](https://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=hughskennedy&url=http://github.com/hughsk/particle-excess-demo&title=particle-excess-demo&description=hughsk/particle-excess-demo%20on%20GitHub&language=en_GB&tags=flattr,github,javascript&category=software) # 2 | 3 | Simulating and rendering 262,144 particles with GLSL. 4 | 5 | ![screenshot](http://i.imgur.com/yuUucK9.jpg) 6 | 7 | **[view demo](http://hughsk.github.io/particle-excess-demo)** 8 | 9 | ## License ## 10 | 11 | MIT. See [LICENSE.md](http://github.com/hughsk/particle-excess-demo/blob/master/LICENSE.md) for details. 12 | -------------------------------------------------------------------------------- /demo.js: -------------------------------------------------------------------------------- 1 | var createBuffer = require('gl-buffer') 2 | var createShell = require('gl-now') 3 | var createFBO = require('gl-fbo') 4 | var createVAO = require('gl-vao') 5 | 6 | var ndarray = require('ndarray') 7 | var fill = require('ndarray-fill') 8 | 9 | var particleVertices 10 | var screenVertices 11 | var nextState 12 | var prevState 13 | var shaders 14 | 15 | var t = 0 16 | var shell = createShell({ 17 | clearColor: [0,0,0,1] 18 | }) 19 | 20 | shell.on('gl-render', render) 21 | shell.on('gl-init', init) 22 | 23 | function init() { 24 | var gl = shell.gl 25 | 26 | shaders = require('./shaders')(gl) 27 | 28 | nextState = createFBO(gl, 512, 512, { 'float': true }) 29 | prevState = createFBO(gl, 512, 512, { 'float': true }) 30 | 31 | var initialState = ndarray(new Float32Array(512 * 512 * 4), [512, 512, 4]) 32 | fill(initialState, function(x, y, ch) { 33 | if (ch > 2) return 1 34 | return (Math.random() - 0.5) * 800.6125 35 | }) 36 | 37 | nextState.color.setPixels(initialState) 38 | prevState.color.setPixels(initialState) 39 | 40 | screenVertices = createVAO(gl, null, [{ 41 | type: gl.FLOAT 42 | , size: 2 43 | , buffer: createBuffer(gl, new Float32Array([ 44 | -1, -1, +1, -1, -1, +1, 45 | -1, +1, +1, -1, +1, +1, 46 | ])) 47 | }]) 48 | 49 | var index = new Float32Array(512 * 512 * 2) 50 | var i = 0 51 | for (var x = 0; x < 512; x++) 52 | for (var y = 0; y < 512; y++) { 53 | index[i++] = x / 512 54 | index[i++] = y / 512 55 | } 56 | 57 | particleVertices = createVAO(gl, null, [{ 58 | type: gl.FLOAT 59 | , size: 2 60 | , buffer: createBuffer(gl, index) 61 | }]) 62 | } 63 | 64 | var cleared = false 65 | 66 | function render() { 67 | var gl = shell.gl 68 | 69 | // Switch to clean FBO for GPGPU 70 | // particle motion 71 | nextState.bind() 72 | gl.disable(gl.DEPTH_TEST) 73 | gl.viewport(0, 0, 512, 512) 74 | 75 | var shader = shaders.logic 76 | shader.bind() 77 | shader.uniforms.uState = prevState.color.bind(0) 78 | shader.uniforms.uTime = t++ 79 | screenVertices.bind() 80 | gl.drawArrays(gl.TRIANGLES, 0, 6) 81 | 82 | // Reset, draw to screen 83 | gl.bindFramebuffer(gl.FRAMEBUFFER, null) 84 | gl.disable(gl.DEPTH_TEST) 85 | gl.viewport(0, 0, shell.width, shell.height) 86 | 87 | var shader = shaders.render 88 | shader.bind() 89 | shader.uniforms.uState = nextState.color.bind(0) 90 | shader.uniforms.uScreen = [shell.width, shell.height] 91 | 92 | particleVertices.bind() 93 | 94 | // Additive blending! 95 | gl.enable(gl.BLEND) 96 | gl.blendFunc(gl.ONE, gl.ONE) 97 | gl.drawArrays(gl.POINTS, 0, 512 * 512) 98 | gl.disable(gl.BLEND) 99 | 100 | // Switch 101 | var tmp = prevState 102 | prevState = nextState 103 | nextState = tmp 104 | } 105 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | particle-excess-demo 5 | 6 | 7 | Fork me on GitHub 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "particle-excess-demo", 3 | "description": "Simulating and rendering 262,144 particles with GLSL", 4 | "version": "0.0.0", 5 | "main": "demo.js", 6 | "browser": "demo.js", 7 | "scripts": { 8 | "start": "beefy demo.js:bundle.js --live -- -t glslifyify", 9 | "prepublish": "browserify demo.js -t glslifyify > bundle.js" 10 | }, 11 | "dependencies": { 12 | "gl-vao": "0.0.3", 13 | "gl-shader": "0.0.6", 14 | "gl-now": "0.0.4", 15 | "gl-fbo": "~0.1.1", 16 | "gl-buffer": "~0.1.1", 17 | "gl-texture2d": "~0.1.5", 18 | "glsl-noise": "0.0.0", 19 | "zeros": "0.0.0", 20 | "ndarray-fill": "~0.1.0", 21 | "ndarray": "~1.0.9" 22 | }, 23 | "devDependencies": { 24 | "glslifyify": "~0.1.1", 25 | "beefy": "~0.7.1", 26 | "browserify": "~3.14.1" 27 | }, 28 | "author": "Hugh Kennedy (http://hughsk.io/)", 29 | "license": "MIT", 30 | "repository": { 31 | "type": "git", 32 | "url": "git://github.com/hughsk/particle-excess-demo" 33 | }, 34 | "bugs": { 35 | "url": "https://github.com/hughsk/particle-excess-demo/issues" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /shaders/index.js: -------------------------------------------------------------------------------- 1 | var createShader = require('gl-shader') 2 | 3 | module.exports = init 4 | 5 | function init(gl) { 6 | var shaders = {} 7 | 8 | shaders.logic = createShader(gl 9 | , require('./logic.vert') 10 | , require('./logic.frag') 11 | ) 12 | 13 | shaders.render = createShader(gl 14 | , require('./render.vert') 15 | , require('./render.frag') 16 | ) 17 | 18 | return shaders 19 | } 20 | -------------------------------------------------------------------------------- /shaders/logic.frag: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision mediump float; 3 | #endif 4 | 5 | #pragma glslify: simplex = require(../node_modules/glsl-noise/simplex/3d) 6 | 7 | uniform sampler2D uState; 8 | uniform float uTime; 9 | 10 | const vec3 OFFSET = vec3(2399.24849823098, 3299.9028381, 389.09338327); 11 | const float SPEED = 2.0; 12 | 13 | void main() { 14 | vec3 sampled = texture2D(uState, gl_FragCoord.xy / vec2(512.0)).rgb; 15 | vec2 nextPosition = sampled.xy; 16 | 17 | float t = uTime * 0.013849829389; 18 | 19 | nextPosition += vec2( 20 | simplex(vec3(nextPosition * 0.005, 9280.03992092039 + t + gl_FragCoord.x / 110.0) + OFFSET) 21 | , simplex(vec3(nextPosition * 0.005, 3870.73392092039 - t - gl_FragCoord.y / 110.0) + OFFSET) 22 | ) * SPEED; 23 | 24 | float radius = length(nextPosition); 25 | float rad = 0.00002 * radius; 26 | nextPosition = vec2( 27 | nextPosition.x * cos(rad) - nextPosition.y * sin(rad) 28 | , nextPosition.y * cos(rad) + nextPosition.x * sin(rad) 29 | ); 30 | 31 | nextPosition *= 0.9999; 32 | 33 | gl_FragColor = vec4(vec3(nextPosition, 1.0), 1.0); 34 | } 35 | -------------------------------------------------------------------------------- /shaders/logic.vert: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision mediump float; 3 | #endif 4 | 5 | attribute vec2 aPosition; 6 | 7 | void main() { 8 | gl_Position = vec4(aPosition.xy, 1.0, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /shaders/render.frag: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision mediump float; 3 | #endif 4 | 5 | uniform sampler2D uTexture; 6 | varying vec2 vIndex; 7 | 8 | void main() { 9 | gl_FragColor = vec4( 10 | sin(vIndex.x * 10.0) 11 | , 0.5 12 | , 1.0 - vIndex.y 13 | , 1.0 14 | ) * 0.165; 15 | } 16 | -------------------------------------------------------------------------------- /shaders/render.vert: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision mediump float; 3 | #endif 4 | 5 | attribute vec2 aIndex; 6 | 7 | uniform vec2 uScreen; 8 | uniform sampler2D uState; 9 | 10 | varying vec2 vIndex; 11 | 12 | void main() { 13 | vIndex = aIndex; 14 | gl_PointSize = 1.0; 15 | gl_Position = vec4(texture2D(uState, aIndex).xy / uScreen, 1.0, 1.0); 16 | } 17 | --------------------------------------------------------------------------------