Instanced particle morph animation

const color = new THREE.Color();
    const sphereGeometry = new THREE.SphereGeometry(0.008, 16, 16);
    const particles = new THREE.InstancedMesh(
      sphereGeometry,
      new THREE.MeshStandardMaterial(),
      8000
    );
    particles.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
    const matrix = new THREE.Matrix4();
    const secondMatrix = new THREE.Matrix4();
    const group = new THREE.Group();
    scene.add(group);

    const globeRadius = 1.3;

    let sampler;
    let model;
    loader.load('/earth.glb', (gltf) => {
      model = gltf.scene;
      gltf.scene.traverse((obj) => {
        if (obj.isMesh) {
          obj.scale.set(0.4, 0.4, 0.4);
          sampler = new MeshSurfaceSampler(obj).build();
          for (let i = 0; i < 30000; i++) {
            const sample = new THREE.Vector3();
            sampler.sample(sample);
            sample.normalize();
            const phi = Math.atan2(sample.z, sample.x);
            const gradientPosition = (phi + Math.PI) / (2 * Math.PI);

            if (gradientPosition < 0.5) {
              color.lerpColors(new THREE.Color(0x006dff), new THREE.Color(0xfc0001), gradientPosition * 2);
            } else {
              color.lerpColors(new THREE.Color(0xfc0001), new THREE.Color(0xf2e300), (gradientPosition - 0.5) * 2);
            }

            sample.multiplyScalar(globeRadius);
            matrix.makeTranslation(sample.x, sample.y, sample.z);
            particles.setMatrixAt(i, matrix);
            particles.setColorAt(i, color);
          }

          group.add(particles)
          group.scale.set(0.36, 0.36, 0.36);
        }
      });
    });


    let secondModel;
    loader.load('/land.glb', (gltf) => {
      secondModel = gltf.scene;
      gltf.scene.traverse((obj) => {
        if (obj.isMesh) {
          obj.scale.set(0.4, 0.4, 0.4);
          sampler = new MeshSurfaceSampler(obj).build();
          for (let i = 0; i < 30000; i++) {
            const sample = new THREE.Vector3();
            sampler.sample(sample);

            secondMatrix.makeTranslation(sample.x, sample.y, sample.z);
            // particles.setMatrixAt(i, secondMatrix);
            particles.setColorAt(i, color);
          }
          group.scale.set(0.36, 0.36, 0.36);
        }
      });
    });

  // tl.to(?, {
  //   x:?,
  //   y:?,
  //   z:?,
  //   scrollTrigger: {
  //     trigger: "#page1",
  //     start: "bottom bottom",
  //     end: "bottom top",
  //     scrub: true,
  //     markers: true,
  //   }
  // })

i need to animate the particles like in this website WEBSITE
do you guys know how to achieve this morphing animation like in this site . i used gsap but its not working correctly. is shader is must wanted for these kind of morphing. or is there any other simple ways ?
if any one knows please help .

I would use points for such type of visuals. PointsMaterial needs minor changes to move particles from one formation to another.

But I don’t insist :slight_smile:

As you use instancing, you can interpolate position of an instance with JS :thinking:
Have a look at this example and its source code, to see how to change instance matrices: three.js examples

1 Like

i couldn’t find something helpfull. can you explain any solution please

Having two arrays with the same amount of positions for different formations, you can set position of each instance by interpolation between start and end points. For example, .lerpVectors()

dummy.positon.lerpVectors(start[i], end[i], a);
dummy.updateMatrix();
instancedMesh.setMatrixAt(i, dummy.matrix);
// where 'i' is the index of an instance

bro. i just switched to Points ,PointsMaterial .

const group = new THREE.Group();
    scene.add(group);
    const color = new THREE.Color();
    const particlesGeometry = new THREE.BufferGeometry();
    const Ppositions = [];
    const Pcolors = [];

    const globeRadius = 1.3;

    let sampler;
    let model;
    loader.load('/earth.glb', (gltf) => {
      model = gltf.scene;
      gltf.scene.traverse((obj) => {
        if (obj.isMesh) {
          obj.scale.set(0.4, 0.4, 0.4);
          sampler = new MeshSurfaceSampler(obj).build();
          for (let i = 0; i < 8000; i++) {
            const sample = new THREE.Vector3();
            sampler.sample(sample);
            sample.normalize();
            const phi = Math.atan2(sample.z, sample.x);
            const gradientPosition = (phi + Math.PI) / (2 * Math.PI);

            if (gradientPosition < 0.5) {
              color.lerpColors(new THREE.Color(0x006dff), new THREE.Color(0xfc0001), gradientPosition * 2);
            } else {
              color.lerpColors(new THREE.Color(0xfc0001), new THREE.Color(0xf2e300), (gradientPosition - 0.5) * 2);
            }

            sample.multiplyScalar(globeRadius);
            Ppositions.push(sample.x, sample.y, sample.z);
            Pcolors.push(color.r, color.g, color.b);
          }

          particlesGeometry.setAttribute('position', new THREE.Float32BufferAttribute(Ppositions, 3));
          particlesGeometry.setAttribute('color', new THREE.Float32BufferAttribute(Pcolors, 3));

          const material = new THREE.PointsMaterial({ size: 0.008, vertexColors: true, map: sprite });
          const particles = new THREE.Points(particlesGeometry, material);
          group.add(particles);
          group.scale.set(0.36, 0.36, 0.36);
        }
      });
    });





    let secondModel;
    loader.load('/land.glb', (gltf) => {
      secondModel = gltf.scene;
      gltf.scene.traverse((obj) => {
        if (obj.isMesh) {
          obj.scale.set(0.4, 0.4, 0.4);
          sampler = new MeshSurfaceSampler(obj).build();
          for (let i = 0; i < 8000; i++) {
            const sample = new THREE.Vector3();
            sampler.sample(sample);
            // Ppositions.push(sample.x, sample.y, sample.z);
            Pcolors.push(color.r, color.g, color.b);
          }

          particlesGeometry.setAttribute('position', new THREE.Float32BufferAttribute(Ppositions, 3));
          particlesGeometry.setAttribute('color', new THREE.Float32BufferAttribute(Pcolors, 3));
        }
      });
    });

can you say that minor changes that need for this particle animation .like this please

This is the example with morphing between models: https://codepen.io/prisoner849/pen/yLGGKqo

2 Likes

Hi @unni_krishnan , I have created an npm package to which can work for your use case. You can easily animate the particles from on formation to another using it. Though I have created it for React three fiber.
You can check it out!
r3f-points-fx - npm (npmjs.com)

hey Vendant, firstly, I want to express my sincere gratitude for your incredible contribution in crafting the R3f Points FX library. Your dedication and expertise have truly made a difference, and for that, I am immensely grateful.

I have been exploring the capabilities of your library in my current project. While I have found it to be a remarkable tool, I have encountered a hurdle that I am hoping you might be able to assist me with.

Specifically, I am endeavoring to employ a scroll trigger (using GSAP) to seamlessly transition shapes from model A to model B as users navigate through different sections of my application. However, despite my efforts, I am encountering a challenge where the animation fails to display smoothly, and the model changes abruptly. Interestingly, when I utilize a button trigger, mirroring your provided example, everything functions flawlessly.

I understand that you have invested significant time and effort into developing this library, and I greatly appreciate any assistance you can offer.

Thank you once again for your remarkable work, and I eagerly await your guidance.

[CodeSandBox - sample]

1 Like