How to apply animation to the points of a PointCloud?

Example: Nuxt - Starter (forked) - StackBlitz

The example above allows to load pointclouds. The unloading of a pointcloud (before a new one is loaded) is done in the deletePlyModel function:

const deletePlyModel = () => {
    // If no model is currently loaded, do nothing
    if (!currentPlyModel) return;

    // Find the point cloud object in the scene
    const pointCloud = scene.getObjectByName(currentPlyModel);

    // If the point cloud object is found, dispose and remove it
    if (pointCloud) {
      pointCloud.geometry.dispose();
      pointCloud.material.dispose();
      scene.remove(pointCloud);
      
      // Clear the currentPlyModel reference
      currentPlyModel = null;
    }

    render(); // Update the scene
};

I would like to add some fadeOut animation, before the model is completely disposed of, but so far i was not successful. First I tried:

const deletePlyModel = () => {
  const timeline = gsap.timeline();
  timeline.to(pointCloud.material, {
    opacity: 0,
    duration: 1
  });
}

Then Gemini.at proposed some ideas, the last one being

function hexToNormalizedRgb(hex) {
  const r = (hex >> 16) & 0xFF;
  const g = (hex >> 8) & 0xFF;
  const b = hex & 0xFF;
  return [r / 255, g / 255, b / 255];
}

const deletePlyModel = () => {
  // [..]
  if (pointCloud) {
	const timeline = gsap.timeline();
	const normalizedFadeColor = hexToNormalizedRgb(0xffffff);
    pointCloud.geometry.attributes.color.array.forEach((color, i) => {
      // Access the color components directly
      const colorElement = pointCloud.geometry.attributes.color.array[i];
      timeline.to(colorElement, {
        x: normalizedFadeColor[0],
        y: normalizedFadeColor[1],
        z: normalizedFadeColor[2],
        duration: 1,
        delay: i / pointCloud.geometry.attributes.color.count * 0.5
      });
    });
  }
}

But that result in this error: GSAP target 0 not found

What is the proper way to access the points of a PointCloud in order to animate them (efficiently)?

normalizedFadeColor is a copy of the actual data… so you’re only animating the copy.

You could make a colorElement be a getter/setter for that loc in the array…

let ca = pointCloud.geometry.attributes.color.array;

colorElement={
  i,
   get x(){return ca [this.i]}
   get y(){return ca [this.i+1]}
   get z(){return ca [this.i+2]}
   set x(v){ca [this.i]=v}
   set y(v){ca [this.i+1]=v}
   set z(v){ca [this.i+2]=v}
}

and then gsap on that object maybe?

The other thing you need is to call geometry.attributes.color.needsUpdate = true;
each frame after the tween has run, to write the buffer back to the GPU
or you won’t see the results.

Thank you, @manthrax , for you reply!

I fooled around with that idea, but couldn’t get it working. I am surely doing something wrong here.

After some more research it seems that another solution would be to adapt the PointCloud shader by adding an opacity value, but this is way above my capabilities (and eventually might be to heavy on the machine anyway, when the point clouds are getting bigger and bigger).

I eventually settled with a simple opacity animation of the point cloud model’s material. I might pick up later on this topic again. So thank you very much again!!!

1 Like