├── .gitignore
├── README.md
├── compute-noise.js
├── flow-field.js
├── index.html
├── index.js
├── package.json
├── particles.js
└── web.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | .DS_Store
3 |
4 | # Node.js
5 | node_modules/
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 3d flow-field particles experiment in THREE.js
2 |
3 | [Live version](http://szymonkaliski.github.io/threejs-exp-particles/)
4 |
5 | ## Installation
6 |
7 | ```
8 | git clone ...
9 | npm install
10 | ```
11 |
12 | ## Usage
13 |
14 | ```
15 | npm start # open http://localhost:3000
16 | ```
17 |
18 |
--------------------------------------------------------------------------------
/compute-noise.js:
--------------------------------------------------------------------------------
1 | var Perlin = require('perlin-simplex');
2 | var THREE = require('three');
3 |
4 | var noise = new Perlin();
5 |
6 | module.exports = function(x, y, z) {
7 | var eps = 1.0;
8 | var n1, n2, a, b;
9 | var curl = new THREE.Vector3();
10 |
11 | n1 = noise.noise3d(x, y + eps, z);
12 | n2 = noise.noise3d(x, y - eps, z);
13 |
14 | a = (n1 - n2) / (2 * eps);
15 |
16 | n1 = noise.noise3d(x, y, z + eps);
17 | n2 = noise.noise3d(x, y, z - eps);
18 |
19 | b = (n1 - n2) / (2 * eps);
20 |
21 | curl.x = a - b;
22 |
23 | n1 = noise.noise3d(x, y, z + eps);
24 | n2 = noise.noise3d(x, y, z - eps);
25 |
26 | a = (n1 - n2)/(2 * eps);
27 |
28 | n1 = noise.noise3d(x + eps, y, z);
29 | n2 = noise.noise3d(x + eps, y, z);
30 |
31 | b = (n1 - n2)/(2 * eps);
32 |
33 | curl.y = a - b;
34 |
35 | n1 = noise.noise3d(x + eps, y, z);
36 | n2 = noise.noise3d(x - eps, y, z);
37 |
38 | a = (n1 - n2)/(2 * eps);
39 |
40 | n1 = noise.noise3d(x, y + eps, z);
41 | n2 = noise.noise3d(x, y - eps, z);
42 |
43 | b = (n1 - n2)/(2 * eps);
44 |
45 | curl.z = a - b;
46 |
47 | return curl;
48 | };
49 |
--------------------------------------------------------------------------------
/flow-field.js:
--------------------------------------------------------------------------------
1 | var THREE = require('three');
2 | var computeNoise = require('./compute-noise');
3 |
4 | function FlowField(size) {
5 | this.field = [];
6 | this.size = size;
7 |
8 | for (var x = 0; x < size; ++x) {
9 | this.field[x] = [];
10 |
11 | for (var y = 0; y < size; ++y) {
12 | this.field[x][y] = [];
13 |
14 | for (var z = 0; z < size; ++z) {
15 | var mod = 0.07;
16 |
17 | this.field[x][y][z] = computeNoise(x * mod, y * mod, z * mod);
18 | }
19 | }
20 | }
21 | }
22 |
23 | FlowField.prototype.sample = function(x, y, z) {
24 | x = Math.round(x) + this.size / 2;
25 | y = Math.round(y) + this.size / 2;
26 | z = Math.round(z) + this.size / 2;
27 |
28 | return (this.field[x] && this.field[x][y] && this.field[x][y][z]) ? this.field[x][y][z] : undefined;
29 | };
30 |
31 | module.exports = FlowField;
32 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | 3D flow-field particles in THREE.js / WebGL
4 |
5 |
36 |
37 |
38 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var THREE = require('three');
2 | var OrbitControls = require('three-orbit-controls')(THREE);
3 |
4 | var FlowField = require('./flow-field');
5 | var Particles = require('./particles');
6 |
7 | var renderer = new THREE.WebGLRenderer();
8 | var width = window.innerWidth;
9 | var height = window.innerHeight;
10 |
11 | renderer.setSize(width, height);
12 | document.body.appendChild(renderer.domElement);
13 |
14 | var scene = new THREE.Scene();
15 | scene.fog = new THREE.Fog(0x000000, 10, 190);
16 |
17 | var camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000);
18 | var controls = new OrbitControls(camera);
19 |
20 | camera.position.z = 100;
21 |
22 | var flowField = new FlowField(100);
23 | var particles = new Particles(20000, 100, flowField);
24 |
25 | scene.add(particles.points);
26 |
27 | function render() {
28 | requestAnimationFrame(render);
29 |
30 | particles.update();
31 |
32 | renderer.render(scene, camera);
33 | }
34 |
35 | render();
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "threejs-exp-particles",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "budo index.js:web.js --live --port 3000",
8 | "dist": "browserify index.js | uglifyjs > web.js"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "perlin-simplex": "0.0.2",
14 | "three": "^0.75.0",
15 | "three-orbit-controls": "^72.0.0"
16 | },
17 | "devDependencies": {
18 | "browserify": "^13.0.0",
19 | "budo": "^8.2.1",
20 | "uglifyjs": "^2.4.10"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/particles.js:
--------------------------------------------------------------------------------
1 | var THREE = require('three');
2 |
3 | function Particles(num, size, flowField) {
4 | this.flowField = flowField;
5 |
6 | var geometry = new THREE.Geometry();
7 | this.velocities = [];
8 |
9 | for (var i = 0; i < num; ++i) {
10 | var vertex = new THREE.Vector3(
11 | (Math.random() - 0.5),
12 | (Math.random() - 0.5),
13 | (Math.random() - 0.5)
14 | );
15 |
16 | geometry.vertices.push(vertex);
17 |
18 | this.velocities.push(new THREE.Vector3(
19 | Math.random() - 0.5,
20 | Math.random() - 0.5,
21 | Math.random() - 0.5
22 | ));
23 | }
24 |
25 | var material = new THREE.PointsMaterial({
26 | size: 0.01,
27 | color: 0xffffff
28 | });
29 |
30 | this.points = new THREE.Points(geometry, material);
31 | }
32 |
33 | Particles.prototype.update = function() {
34 | for (var i = 0; i < this.points.geometry.vertices.length; ++i) {
35 | var vertex = this.points.geometry.vertices[i];
36 | var velocity = this.velocities[i];
37 |
38 | var flow = this.flowField.sample(vertex.x, vertex.y, vertex.z);
39 |
40 | if (flow) {
41 | var steer = flow.clone().sub(velocity);
42 |
43 | velocity.add(steer.multiplyScalar(0.02));
44 | vertex.add(velocity.multiplyScalar(1.0));
45 | }
46 | else {
47 | vertex.set(
48 | Math.random() - 0.5,
49 | Math.random() - 0.5,
50 | Math.random() - 0.5
51 | );
52 |
53 | velocity.set(
54 | Math.random() - 0.5,
55 | Math.random() - 0.5,
56 | Math.random() - 0.5
57 | );
58 | }
59 | }
60 |
61 | this.points.geometry.verticesNeedUpdate = true;
62 | };
63 |
64 | module.exports = Particles;
65 |
66 |
--------------------------------------------------------------------------------