three.jshtml5-canvasraycastingmouse-picking

ThreeJs Badly sees the coordination of the mouse through the canvas


Structure of my project 66% threejs model, 44% html (side control) using Bootstrap. I’m trying to make mouse picker, when pointing at an object so that it is shown on which object it is pointed. As I understand it, he sees the coordination of the mouse badly. Please help me figure out and set up the correct coordination mouse with Canvas.

Project Structure Screenshot: enter image description here

Code:

export class CustomizerComponent implements OnInit {
  public textureSrc: string;
  public textureList = [
    { id: 1, name: 'Wood 2', src: '/assets/textures/wood2.jpg' },
    { id: 2, name: 'Wood 1', src: '/assets/textures/wood1.jpg' },
  ];

  public selectedTexture(e): void {
    let find = this.textureList.find((x) => x?.src === e.target.value);
    // console.log(find?.src);
  }

  ngOnInit(): void {
    let scene, kitchen, camera, renderer, canvas, controls, mouse, raycaster, loader, BACKGROUND_COLOR = 0xffffff;

    // Scene
    scene = new THREE.Scene();
    scene.background = new THREE.Color(BACKGROUND_COLOR);

    // Renderer with canvas
    canvas = document.getElementById('canvas');

    renderer = new THREE.WebGLRenderer({ canvas: canvas });
    renderer.setSize(canvas.width * 3.5, canvas.height * 5);
    console.log(canvas.clientWidth);
    renderer.shadowMap.enabled = true;

    // Set Camera
    camera = new THREE.PerspectiveCamera(
      75,
      canvas.clientWidth / canvas.clientHeight,
      0.1,
      1000
    );
    camera.position.z = 5;
    camera.position.x = 5;
    camera.position.y = 1;

    mouse = new THREE.Vector2();
    raycaster = new THREE.Raycaster();

    // Controls with options
    controls = new OrbitControls(camera, renderer.domElement);
    controls.maxPolarAngle = Math.PI / 2;
    controls.minPolarAngle = Math.PI / 3;
    controls.enableDamping = true;
    controls.enablePan = false;
    controls.target.set(5, 1, 0);
    controls.maxAzimuthAngle = 0.8;
    controls.minAzimuthAngle = -0.8;
    controls.enableZoom = false;
    controls.dampingFactor = 0.1;
    controls.autoRotate = false;
    controls.autoRotateSpeed = 0.2; // 30

    loader = new GLTFLoader();
    loader.load(
      'assets/3d_models/kitch.glb',
      (gltf) => {
        kitchen = gltf.scene;

        scene.add(kitchen);
        console.log(kitchen);
      },
      undefined,
      (err) => {
        console.log(err);
      }
    );

    function hoverPieces() {
      const material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
      raycaster.setFromCamera(mouse, camera);
      const intersects = raycaster.intersectObjects(scene.children);
      // console.log(intersects);
      for (let i = 0; i < intersects.length; i++) {
        intersects[i].object.material = material;
      }
      console.log(intersects);
    }

    // Set Lights
    setLights(scene);

    function animate() {
      requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
      hoverPieces();
      if (resizeRendererToDisplaySize(renderer)) {
        const canvas = renderer.domElement;
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
      }
    }

    animate();

    function onMouseMove(event) {
      // calculate pointer position in normalized device coordinates
      // (-1 to +1) for both components

      mouse.x = ((event.clientX - 250) / canvas.clientWidth) * 2 - 1;
      mouse.y = -(event.clientY / canvas.clientHeight) * 2 + 1;
    }
    window.addEventListener('mousemove', onMouseMove);
  }
}

function setLights(scene) {
  // Add lights
  var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.61);
  hemiLight.name = 'hemiLight';
  hemiLight.position.set(0, 50, 0);
  // Add hemisphere light to scene
  scene.add(hemiLight);

  var dirLight = new THREE.DirectionalLight(0xffffff, 0.54);
  dirLight.name = 'dirLight';
  dirLight.position.set(-8, 12, 8);
  // dirLight.castShadow = true;
  dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
  // Add directional Light to scene
  scene.add(dirLight);

  var light = new THREE.DirectionalLight(0xffffff, 3);
  light.name = 'light';
  light.position.set(10, 10, 10);
  scene.add(light);
}
function resizeRendererToDisplaySize(renderer) {
  const canvas = renderer.domElement;
  var width = canvas.clientWidth;
  var height = canvas.clientHeight;
  var canvasPixelWidth = canvas.clientWidth / window.devicePixelRatio;
  var canvasPixelHeight = canvas.clientHeight / window.devicePixelRatio;

  const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height;
  if (needResize) {
    renderer.setSize(width, height, false);
  }
  return needResize;
}

Gist link : https://gist.github.com/artur33s/e15abda28707231863ded08ccfe0887d


Solution

  • I suggest you use pointermove instead of mousemove and also use getBoundingClientRect() in your event listener:

    function onPointerMove(event) {
        const rect = renderer.domElement.getBoundingClientRect();
    
        mouse.x = ( ( event.clientX - rect.left ) / ( rect.right - rect.left ) ) * 2 - 1;
        mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;
    }
    window.addEventListener('pointermove', onPointerMove);