Click anywhere to generate a new particle flow.
Created
August 4, 2018 14:14
-
-
Save haokaibo/1030b9ac1399e790deeeeaad7f23c826 to your computer and use it in GitHub Desktop.
leaving out particularities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
{ | |
// http://mrl.nyu.edu/~perlin/noise/ | |
const perlin = { | |
init () { | |
this.p = new Uint8Array(512); | |
this.reset(); | |
}, | |
reset() { | |
const p = new Uint8Array(256); | |
for (let i = 0; i < 256; i++) p[i] = i; | |
for (let i = 255; i > 0; i--) { | |
const n = Math.floor((i + 1) * Math.random()); | |
[p[i], p[n]] = [p[n], p[i]]; | |
} | |
for (let i = 0; i < 512; i++) this.p[i] = p[i & 255]; | |
}, | |
lerp(t, a, b) { | |
return a + t * (b - a); | |
}, | |
grad2d(i, x, y) { | |
const v = (i & 1) === 0 ? x : y; | |
return (i & 2) === 0 ? -v : v; | |
}, | |
noise2d(x2d, y2d) { | |
const X = Math.floor(x2d) & 255; | |
const Y = Math.floor(y2d) & 255; | |
const x = x2d - Math.floor(x2d); | |
const y = y2d - Math.floor(y2d); | |
const fx = (3 - 2 * x) * x * x; | |
const fy = (3 - 2 * y) * y * y; | |
const p0 = this.p[X] + Y; | |
const p1 = this.p[X + 1] + Y; | |
return this.lerp( | |
fy, | |
this.lerp( | |
fx, | |
this.grad2d(this.p[p0], x, y), | |
this.grad2d(this.p[p1], x - 1, y) | |
), | |
this.lerp( | |
fx, | |
this.grad2d(this.p[p0 + 1], x, y - 1), | |
this.grad2d(this.p[p1 + 1], x - 1, y - 1) | |
) | |
); | |
} | |
}; | |
///////////////////////////////////////////////////////////////// | |
const canvas = { | |
init() { | |
this.elem = document.createElement("canvas"); | |
document.body.appendChild(this.elem); | |
this.width = this.elem.width = this.elem.offsetWidth; | |
this.height = this.elem.height = this.elem.offsetHeight; | |
return this.elem.getContext("2d"); | |
} | |
}; | |
///////////////////////////////////////////////////////////////// | |
const webgl = { | |
init(canvas, options) { | |
this.elem = document.createElement("canvas"); | |
this.gl = ( | |
this.elem.getContext("webgl", options) || | |
this.elem.getContext("experimental-webgl", options) | |
); | |
if (!this.gl) return false; | |
const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER); | |
this.gl.shaderSource(vertexShader, ` | |
precision highp float; | |
attribute vec3 aPosition; | |
uniform vec2 uResolution; | |
void main() { | |
gl_PointSize = 1.0; | |
gl_Position = vec4( | |
( aPosition.x / uResolution.x * 2.0) - 1.0, | |
(-aPosition.y / uResolution.y * 2.0) + 1.0, | |
0.0, | |
1.0 | |
); | |
}` | |
); | |
this.gl.compileShader(vertexShader); | |
const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER); | |
this.gl.shaderSource(fragmentShader, ` | |
precision highp float; | |
void main() { | |
gl_FragColor = vec4(0.2, 0.3, 1.0, 1.0); | |
}` | |
); | |
this.gl.compileShader(fragmentShader); | |
const program = this.gl.createProgram(); | |
this.gl.attachShader(program, vertexShader); | |
this.gl.attachShader(program, fragmentShader); | |
this.gl.linkProgram(program); | |
this.gl.useProgram(program); | |
this.aPosition = this.gl.getAttribLocation(program, "aPosition"); | |
this.gl.enableVertexAttribArray(this.aPosition); | |
this.positionBuffer = this.gl.createBuffer(); | |
this.elem.width = canvas.width; | |
this.elem.height = canvas.height; | |
const uResolution = this.gl.getUniformLocation(program, "uResolution"); | |
this.gl.enableVertexAttribArray(uResolution); | |
this.gl.uniform2f(uResolution, canvas.width, canvas.height); | |
this.gl.viewport( | |
0, | |
0, | |
this.gl.drawingBufferWidth, | |
this.gl.drawingBufferHeight | |
); | |
return this.gl; | |
}, | |
drawBuffer(data, num) { | |
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); | |
this.gl.vertexAttribPointer(this.aPosition, 2, this.gl.FLOAT, false, 0, 0); | |
this.gl.bufferData( | |
this.gl.ARRAY_BUFFER, | |
data, | |
this.gl.DYNAMIC_DRAW | |
); | |
this.gl.drawArrays(this.gl.GL_POINTS, 0, num); | |
} | |
}; | |
///////////////////////////////////////////////////////////////// | |
const ctx = canvas.init(); | |
const gl = webgl.init(canvas, { | |
alpha: false, | |
stencil: false, | |
antialias: false, | |
depth: false, | |
}); | |
perlin.init(); | |
const nParticles = 30000; | |
const velocities = new Float32Array(nParticles * 2); | |
const particles = new Float32Array(nParticles * 2); | |
let frame = 0; | |
for (let i = 0; i < nParticles; i++) { | |
const p = i * 2; | |
particles[p+0] = Math.random() * canvas.width; | |
particles[p+1] = Math.random() * canvas.height; | |
} | |
///////////////////////////////////////////////////////////////// | |
const run = () => { | |
requestAnimationFrame(run); | |
frame++; | |
gl.clear(gl.COLOR_BUFFER_BIT); | |
ctx.globalCompositeOperation = "source-over"; | |
ctx.fillStyle = "rgba(0, 0, 0, 0.025)"; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
ctx.globalCompositeOperation = "lighter"; | |
for (let i = 0; i < nParticles; i++) { | |
const p = i * 2; | |
let n = 80 * perlin.noise2d(particles[p+0] * 0.001, particles[p+1] * 0.001); | |
velocities[p+0] += 0.1 * Math.cos(n); | |
velocities[p+1] += 0.1 * Math.sin(n); | |
particles[p+0] += (velocities[p+0] *= 0.99); | |
particles[p+1] += (velocities[p+1] *= 0.99); | |
particles[p+0] = (canvas.width + particles[p+0]) % canvas.width; | |
particles[p+1] = (canvas.height + particles[p+1]) % canvas.height; | |
} | |
webgl.drawBuffer(particles, nParticles); | |
if (frame > 30) ctx.drawImage(webgl.elem, 0, 0); | |
}; | |
requestAnimationFrame(run); | |
///////////////////////////////////////////////////////////////// | |
["click", "touchdown"].forEach(event => { | |
document.addEventListener(event, e => perlin.reset(), false); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html, body { | |
overflow: hidden; | |
margin: 0; | |
padding: 0; | |
width: 100%; | |
height: 100%; | |
background:#000; | |
} | |
canvas { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
background:#000; | |
cursor: pointer; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment