Raycast returns wrong object when creating meshes with BufferAttribute

This is the code I’m using for creating the meshes:

const depth = 0.1;
const width = 0.017;
for (let i = 0; i < 10; i++) {
  const geometry = new THREE.BufferGeometry();
  const v1 = [-0.075 + i * width, 0.06, 0];
  const v2 = [-0.075, 0.06, -depth];
  const v3 = [
    -0.075 + (i + 1) * width,
    0.06,
    -depth,
  ];
  const v4 = [
    -0.075 + (i + 1) * width,
    0.06,
    0,
  ];

  const v5 = [v1[0], 0.15, v1[2]];
  const v6 = [v2[0], 0.15, v2[2]];
  const v7 = [v3[0], 0.15, v3[2]];
  const v8 = [v4[0], 0.15, v4[2]];

  const vertices = new Float32Array([
    //bottom
    ...v1,
    ...v2,
    ...v3,

    ...v3,
    ...v4,
    ...v1,

    //top
    ...v5,
    ...v6,
    ...v7,

    ...v7,
    ...v8,
    ...v5,

    //north
    ...v1,
    ...v4,
    ...v5,

    ...v4,
    ...v5,
    ...v8,

    //east
    ...v1,
    ...v2,
    ...v5,

    ...v5,
    ...v6,
    ...v2,

    //south
    ...v2,
    ...v3,
    ...v6,

    ...v6,
    ...v7,
    ...v3,

    //west
    ...v3,
    ...v4,
    ...v7,

    ...v7,
    ...v8,
    ...v4,
  ]);

  geometry.setAttribute(
    "position",
    new THREE.BufferAttribute(vertices, 3)
  );

  const material =
    new THREE.MeshBasicMaterial({
      color: 0xff0000,
      side: 2,
      transparent: true,
      opacity: 0,
    });
  const mesh = new THREE.Mesh(
    geometry,
    material
  );
  const box = new THREE.BoxHelper(
    mesh,
    0xff0000
  );
  console.log(box);

  scene.add(mesh);
  // scene.add(box);
  meshes.push(mesh);
  boxes.push(box);
  console.log(mesh.id, box.id);
}

This is the final output: enter image description here when using pointermove or mousemove event and hovering the mouse over the same box the console prints different ids!!
Any explanations for this matter??

  window.addEventListener("pointermove", () => {
    const intersections =
      raycaster.intersectObjects(meshes, true);
    if (intersections.length > 0) {
      const object = intersections[0];
      for (const element of intersections) {
        element.object.material.opacity = 0;
      }

      console.log(object.object.id);
    }
  });

enter image description here

I’m trying to turn on the material opacity for the box that raycast intersects.

You are not updating the raycaster origin and direction on ‘pointermove’.

I think you should do something like this:

// In your pointermove 
onPointerMove( event: PointerEvent ) {
    this.pointer.x = ( event.clientX / this.canvasWidth ) * 2 - 1;
    this.pointer.y = - ( event.clientY / this.canvasHeight ) * 2 + 1;
}

// Then in your render loop:
animate() {
    // There are different ways to update the raycaster origin and direction
    // but this is one of the easiest ones:
    this.raycaster.setFromCamera( this.pointer,this.camera );

    const intersection = this.raycaster.intersectObject( this.meshes );

    if ( intersection.length > 0 ) {
        const instanceId = intersection[ 0 ].instanceId;
        console.log("I found an intersection! with ID:", instanceId );
        // the rest of your raycasting logic
    }
}

A little remark:
The pointermove event is prone to fire A LOT of times when moving the mouse (specially if it’s not debounced/throttled), more than once per rendered frame, so it’s better if you just use it to update the pointer/mouse object coordinates and then use that object in the animate/render loop in your requestAnimationFrame loop to actually perform the raycasting as the little code snippet I above shows.

I expected that to be already included in the animation loop since I’m using React three fiber along with the useThree() hook to get the raycast, pointer, etc.

anyway I added that piece of code and the problem persist:

const mouse = new THREE.Vector3();
  window.addEventListener(
    "pointermove",
    (event) => {
      mouse.x =
        (event.clientX / window.innerWidth) * 2 -
        1;
      mouse.y =
        -(event.clientY / window.innerHeight) *
          2 +
        1;
    }
  );
  const renderLoop = () => {
    // console.log("the pointer ", pointer, mouse);
    raycaster.setFromCamera(mouse, camera);

    gl.render(scene, camera);
  };
  window.requestAnimationFrame(renderLoop);

Here is the react three fiber code

  const {
    camera,
    gl,
    scene,
    raycaster,
    pointer,
  } = useThree();
  <Splat
    position={[0, 0, 0]}
    src={URL}
    onClick={(e) => {
      // console.log(
      //   "clicking here",
      //   e,
      //   e.point
      // );
      // const newMesh = new THREE.Mesh(
      //   new THREE.SphereGeometry(0.05),
      //   new THREE.MeshBasicMaterial({
      //     color: new THREE.Color("red"),
      //   })
      // );
      // newMesh.position.set(
      //   e.point.x,
      //   e.point.y,
      //   e.point.z
      // );
      // scene.add(newMesh);
    }}
  />

  <CameraControls
    makeDefault
  />
  <Preload all />

Share a codepen with your problem an we might be able to help you.

1 Like