Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

THREE.js dynamically add points to a Points geometry does not render

I am using Three.js r83.

I am trying to dynamically add points to a geometry, but the scene never gets updated.

This works :

var tmaterial = new THREE.PointsMaterial({
    color: 0xff0000,
    size: 5,
    opacity: 1
});

var tgeometry = new THREE.Geometry();
var pointCloud = new THREE.Points(tgeometry, tmaterial);

for(var i = 0; i< 1000; i++) {
    x = (Math.random() * 200) - 100;
    y = (Math.random() * 200) - 100;
    z = (Math.random() * 200) - 100;
    tgeometry.vertices.push(new THREE.Vector3(x, y, z));
}
tgeometry.verticesNeedUpdate = true;
tgeometry.computeVertexNormals();

scene.add(pointCloud);

This doesn't work:

var tmaterial = new THREE.PointsMaterial({
    color: 0xff0000,
    size: 5,
    opacity: 1
});

var tgeometry = new THREE.Geometry();
var pointCloud = new THREE.Points(tgeometry, tmaterial);
scene.add(pointCloud);

for(var i = 0; i< 1000; i++) {
    x = (Math.random() * 200) - 100;
    y = (Math.random() * 200) - 100;
    z = (Math.random() * 200) - 100;
    tgeometry.vertices.push(new THREE.Vector3(x, y, z));
}
tgeometry.verticesNeedUpdate = true;
tgeometry.elementsNeedUpdate = true;
tgeometry.computeVertexNormals();
renderer.render(scene, camera);

As you can see, the only difference is the fact that I add scene.add(pointCloud); before adding vertexes.

What do I miss?

You can find a fiddle Thanks to @hectate

To see what I means, just replace

init(); setPoints(); animate();

by

init(); animate(); setPoints();

like image 949
Ant Avatar asked Feb 06 '23 12:02

Ant


2 Answers

I am not sure why the THREE.Geometry object doesn't update Points after initial rendering, but I got it working with a THREE.BufferGeometry instead.

Thanks to @Hectate who got a working fiddle for me and @WestLangley who directed me to the hints, here is the working fiddle

BufferGeometry has a fixed number of Vertices, but you can decide how many of them you want to render. The trick is to make use of geometry.attributes.position.needsUpdate = true; and geometry.setDrawRange( 0, nbPointsYouWantToDisplay );

var MAX_POINTS = 1000000;
var geometry = new THREE.BufferGeometry();
var positions = new Float32Array( MAX_POINTS * 3 ); 
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );

Then you can create your cloudpoints and add it to the scene:

//material and scene defined in question
pointCloud = new THREE.Points(geometry, material);
scene.add(pointCloud);

Now I want to add and render 500 new points every 10 milliseconds.

var nbPoints = 500;
var INTERVAL_DURATION = 10;

All I have to do is :

var interval = setInterval(function() {
  setPoints();
}, INTERVAL_DURATION)

function setPoints() {

  var positions = pointCloud.geometry.attributes.position.array;

  var x, y, z, index;

  var l  = currentPoints + nbPoints;
  if(l >= MAX_POINTS) {
    clearInterval(interval);
  }

  for ( var i = currentPoints; i < l; i ++ ) {
    x = ( Math.random() - 0.5 ) * 300;
    y = ( Math.random() - 0.5 ) * 300;
    z = ( Math.random() - 0.5 ) * 300;
    positions[ currentPointsIndex ++ ] = x;
    positions[ currentPointsIndex ++ ] = y;
    positions[ currentPointsIndex ++ ] = z;
  }
  currentPoints = l;
  pointCloud.geometry.attributes.position.needsUpdate = true;   
  pointCloud.geometry.setDrawRange( 0, currentPoints );  
  controls.update();
  renderer.render(scene, camera);

}
like image 56
Ant Avatar answered Feb 08 '23 03:02

Ant


Here's a fiddle with your first setup installed: https://jsfiddle.net/87wg5z27/236/

var scene, renderer, camera;
var cube;
var controls;

init();
animate();

function init()
{
    renderer = new THREE.WebGLRenderer( {antialias:true} );
    var width = window.innerWidth;
    var height = window.innerHeight;
    renderer.setSize (width, height);
    document.body.appendChild (renderer.domElement);

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera (45, width/height, 1, 10000);
    camera.position.y = 160;
    camera.position.z = 400;
    camera.lookAt (new THREE.Vector3(0,0,0));

    controls = new THREE.OrbitControls (camera, renderer.domElement);

    var tmaterial = new THREE.PointsMaterial({
      color: 0xff0000,
      size: 5,
      opacity: 1
   });

   var tgeometry = new THREE.Geometry();
   var pointCloud = new THREE.Points(tgeometry, tmaterial);

   for(var i = 0; i< 1000; i++) {
     x = (Math.random() * 200) - 100;
     y = (Math.random() * 200) - 100;
     z = (Math.random() * 200) - 100;
     tgeometry.vertices.push(new THREE.Vector3(x, y, z));
   }
   tgeometry.verticesNeedUpdate = true;
   tgeometry.computeVertexNormals();

   scene.add(pointCloud);

   window.addEventListener ('resize', onWindowResize, false);
}

function onWindowResize ()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize (window.innerWidth, window.innerHeight);
}

function animate()
{
    controls.update();
    requestAnimationFrame ( animate );  
    renderer.render (scene, camera);
}

Here's one with your second: https://jsfiddle.net/87wg5z27/237/

var scene, renderer, camera;
var cube;
var controls;

init();
animate();

function init()
{
    renderer = new THREE.WebGLRenderer( {antialias:true} );
    var width = window.innerWidth;
    var height = window.innerHeight;
    renderer.setSize (width, height);
    document.body.appendChild (renderer.domElement);

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera (45, width/height, 1, 10000);
    camera.position.y = 160;
    camera.position.z = 400;
    camera.lookAt (new THREE.Vector3(0,0,0));

    controls = new THREE.OrbitControls (camera, renderer.domElement);

    var tmaterial = new THREE.PointsMaterial({
      color: 0xff0000,
      size: 5,
      opacity: 1
    });

    var tgeometry = new THREE.Geometry();
    var pointCloud = new THREE.Points(tgeometry, tmaterial);
    scene.add(pointCloud);

    for(var i = 0; i< 1000; i++) {
     x = (Math.random() * 200) - 100;
     y = (Math.random() * 200) - 100;
     z = (Math.random() * 200) - 100;
     tgeometry.vertices.push(new THREE.Vector3(x, y, z));
    }
    tgeometry.verticesNeedUpdate = true;
    tgeometry.elementsNeedUpdate = true;
    tgeometry.computeVertexNormals();
    renderer.render(scene, camera);

    window.addEventListener ('resize', onWindowResize, false);
}

function onWindowResize ()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize (window.innerWidth, window.innerHeight);
}

function animate()
{
    controls.update();
    requestAnimationFrame ( animate );  
    renderer.render (scene, camera);
}

In both cases the point cloud shows for me perfectly fine (release 82). Perhaps there is something else missing where you're neglecting to render something? I notice that your first example doesn't show at what step you call render(). I hope this helps!

like image 34
Hectate Avatar answered Feb 08 '23 01:02

Hectate