Skip to content

Instantly share code, notes, and snippets.

@johanmcos
Created August 13, 2020 14:48
Show Gist options
  • Save johanmcos/20c3868386f122940c3011c03b9d10b5 to your computer and use it in GitHub Desktop.
Save johanmcos/20c3868386f122940c3011c03b9d10b5 to your computer and use it in GitHub Desktop.
three.js point cloud example
<div id="canvas-holder">
<canvas id="maincanvas" width="300" height="300"></canvas>
</div>
/*
* http://math.hws.edu/graphicsbook/demos/c5/point-cloud.html
*/
var canvas, scene, renderer, camera; // Standard basic objects for three.js
var pointCloud; // The object that represents the visible cloud of points.
var MAX_POINTS = 10000; // Number of points available
var points; // An array of Vector3, containing all possible points
var spinSpeeds; // An array of Quarterians, containing point rotations for "spin" animation
var driftSpeeds; // An array of Vector3, contianing point velocities for "drift" animation
var settings = {
animation: '',
points: 1000,
pointSize: 2,
}
var geometry; // Object of type THREE.Geometry containing the visible points.
var material; // Object of type THREE.PointCloudMaterial for point color and size.
/* Render function draws the content of the canvas.
* If animating is true, it also updates the vertex locations
* for this frame of the animation and calles
* requestAnimationFrame(render) to arrange for the next frame.
*/
function render() {
if (settings.animation) {
if (settings.animation === 'spin') {
updateForSpin();
}
else {
updateForDrift();
}
requestAnimationFrame(render);
}
renderer.render(scene,camera);
}
function updateForSpin() {
for (var i = 0; i < geometry.vertices.length; i++) {
var v = points[i];
v.applyQuaternion( spinSpeeds[i] ); // applies a rotation about the y-axis
}
geometry.verticesNeedUpdate = true;
}
function updateForDrift() {
for (var i = 0; i < geometry.vertices.length; i++) {
var v = points[i];
v.add( driftSpeeds[i] );
if (v.length() > 1) {
// When outside the sphere, change to a random velocity,
// and multiply the point by 0.9997 to move it back
// towards the inside of the sphere. Using a value
// close to 1 allows the sphere to get a little
// fuzzy.
driftSpeeds[i] = randomVelocity();
v.multiplyScalar(0.9997);
}
else if (Math.random() < 0.001) {
// change to a new random velocity, with a small probability
driftSpeeds[i] = randomVelocity();
}
}
geometry.verticesNeedUpdate = true;
}
function createWorld() {
scene = new THREE.Scene();
renderer.setClearColor(0x333333);
camera = new THREE.PerspectiveCamera(30,canvas.width/canvas.height,1,10);
camera.position.z = 5;
points = new Array(MAX_POINTS);
spinSpeeds = new Array(MAX_POINTS);
driftSpeeds = new Array(MAX_POINTS);
var i = 0;
var yaxis = new THREE.Vector3(0,1,0);
while (i < MAX_POINTS) {
var x = 2*Math.random() - 1;
var y = 2*Math.random() - 1;
var z = 2*Math.random() - 1;
if ( x*x + y*y + z*z < 1 ) { // only use points inside the unit sphere
var angularSpeed = 0.001 + Math.random()/50; // angular speed of rotation about the y-axis
spinSpeeds[i] = new THREE.Quaternion();
spinSpeeds[i].setFromAxisAngle(yaxis,angularSpeed); // The quaternian for rotation by angularSpeed radians about the y-axis.
driftSpeeds[i] = randomVelocity();
points[i] = new THREE.Vector3(x,y,z);
i++;
}
}
geometry = new THREE.Geometry();
for (i = 0; i < settings.points; i++) {
geometry.vertices.push(points[i]);
}
material = new THREE.PointsMaterial({
color: "yellow",
size: settings.pointSize,
sizeAttenuation: false
});
pointCloud = new THREE.Points(geometry,material);
scene.add(pointCloud);
}
function randomVelocity() {
var dx = 0.001 + 0.003*Math.random();
var dy = 0.001 + 0.003*Math.random();
var dz = 0.001 + 0.003*Math.random();
if (Math.random() < 0.5) {
dx = -dx;
}
if (Math.random() < 0.5) {
dy = -dy;
}
if (Math.random() < 0.5) {
dz = -dz;
}
return new THREE.Vector3(dx,dy,dz);
}
/* This will be called when the user changes the setting on the
* animation radio buttons.
*/
function checkAnimation() {
if (!settings.animation) {
animating = false; // Will stop the animation if one is in progress.
}
else if ( ! animating ) { // Start animating, if not currently animating.
animating = true;
requestAnimationFrame(render);
}
}
function init() {
try {
canvas = document.getElementById("maincanvas");
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
}
catch (e) {
document.getElementById("canvas-holder").innerHTML =
"<p><b>Sorry, an error occurred:<br>" + e + "</b></p>";
return;
}
var gui = new dat.GUI(/*{ autoPlace: false }*/);
//document.getElementById('settings').appendChild(gui.domElement);
var prevAnim = settings.animation;
gui.add(settings, 'animation', { None: '', Spin: 'spin', Drift: 'drift' })
.onChange(anim => {
if (!prevAnim) { // Start animating, if not currently animating.
requestAnimationFrame(render);
}
prevAnim = anim;
});
gui.add(settings, 'points', 10, MAX_POINTS)
.onChange(p => {
//http://math.hws.edu/graphicsbook/demos/c5/point-cloud.html
// Change the number of points. I don't know why I had to create
// a whole new PointCloud object to implement this. I was not
// able to get the renderer to recognize a change in the length
// of the vertex array in the geometry, or to recognize a new
// geometry object in the original pointCloud object.
geometry.dispose();
geometry = new THREE.Geometry();
for (var i = 0; i < p; i++) {
geometry.vertices.push(points[i]);
}
scene.remove(pointCloud);
pointCloud = new THREE.Points(geometry, material);
scene.add(pointCloud);
if (!settings.animation) {
render();
}
});
gui.add(settings, 'pointSize', .1, 10)
.onChange(size => {
// Change the size of the points by modifying the size property of the matrial.
material.size = size;
if (!settings.animation) {
render();
}
});
createWorld();
render();
}
init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.3/dat.gui.min.js"></script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment