├── .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 |
39 | 3d flow-field particles experiment made with THREE.js, by Szymon Kaliski. Source code. 40 |
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 | --------------------------------------------------------------------------------