mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.58k stars 35.36k forks source link

why raycaster.intersectObjects return emtpy Even though the ray went through the model? #25790

Closed hangaoke1 closed 1 year ago

hangaoke1 commented 1 year ago

Description

I have a GLTF model that I imported into Blender and exported without making any changes. Strangely, the original model can detect click events through raycasting, but the exported model cannot, even though the ray passes through the model.

stacy.glb can detect mesh

image

stacy2.glb can not detect mesh

image

Reproduction steps

  1. copy the html or open sandbox
  2. use stacy.glb
  3. click model
  4. change to stacy2.glb
  5. click model

Code

<html>
  <head>
    <meta charset="utf-8" />
    <title>demo</title>
    <style>
      body {
        margin: 0;
      }
    </style>
  </head>
  <body>
    <script
      async
      src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"
    ></script>
    <script type="importmap">
      {
        "imports": {
          "three": "https://unpkg.com/three@0.151.2/build/three.module.js",
          "three/addons/": "https://unpkg.com/three@0.151.2/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      import * as THREE from "three";
      import { OrbitControls } from "three/addons/controls/OrbitControls.js";
      import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
      import { OBJLoader } from "three/addons/loaders/OBJLoader.js";
      import { MTLLoader } from "three/addons/loaders/MTLLoader.js";

      const scene = new THREE.Scene();
      scene.background = new THREE.Color(0xa0a0a0);

      const manager = new THREE.LoadingManager();
      manager.onProgress = (url, itemsLoaded, itemsTotal) => {
        console.log(
          `Loading file: ${url}. Loaded ${itemsLoaded} of ${itemsTotal} files.`
        );
        if (itemsLoaded === itemsTotal) {
          animate();
        }
      };

      const gridHelper = new THREE.GridHelper(50, 50, 0x444444, 0x888888);
      scene.add(gridHelper);

      const camera = new THREE.PerspectiveCamera(
        70,
        window.innerWidth / window.innerHeight,
        0.1,
        2000
      );
      camera.position.set(-2, 2, 2);

      const axesHelper = new THREE.AxesHelper(100);
      scene.add(axesHelper);

      const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
      hemiLight.position.set(0, 20, 0);
      scene.add(hemiLight);

      const dirLight = new THREE.DirectionalLight(0xffffff);
      dirLight.position.set(3, 10, 10);
      dirLight.castShadow = true;
      scene.add(dirLight);

      const renderer = new THREE.WebGLRenderer({
        antialias: true,
      });
      renderer.shadowMap.enabled = true;
      renderer.outputEncoding = THREE.sRGBEncoding;
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      let gltfObjModel;
      const loader = new GLTFLoader(manager);
      const gltfObj = [];

      loader.load(
        // "https://hgkcdn.oss-cn-shanghai.aliyuncs.com/3d/stacy.glb", // ok
        "https://hgkcdn.oss-cn-shanghai.aliyuncs.com/3d/stacy2.glb", // not ok
        (gltf) => {
          gltfObjModel = gltf.scene;
          scene.add(gltf.scene);
          gltfObjModel.traverse((object) => {
            if (object.isMesh) {
              gltfObj.push(object);
              object.castShadow = true;
              object.receiveShadow = true;
            }
          });
        }
      );

      const controls = new OrbitControls(camera, renderer.domElement);
      controls.enableZoom = true;
      controls.enableRotate = true;
      controls.enablePan = true;
      controls.target.set(0, 0, 0);
      controls.update();

      const raycaster = new THREE.Raycaster();
      const canvas = renderer.domElement;
      const canvasRect = canvas.getBoundingClientRect();
      let arrowHelper;
      function onClick(event) {
        const pageX =
          event.pageX !== undefined
            ? event.pageX
            : event.clientX +
              document.body.scrollLeft +
              document.documentElement.scrollLeft;
        const pageY =
          event.pageY !== undefined
            ? event.pageY
            : event.clientY +
              document.body.scrollTop +
              document.documentElement.scrollTop;
        const ndcX = ((pageX - canvasRect.left) / canvas.clientWidth) * 2 - 1;
        const ndcY = (-(pageY - canvasRect.top) / canvas.clientHeight) * 2 + 1;
        const mouse = new THREE.Vector2();
        mouse.x = ndcX;
        mouse.y = ndcY;

        raycaster.setFromCamera(mouse, camera);

        if (arrowHelper) {
          scene.remove(arrowHelper);
        }
        arrowHelper = new THREE.ArrowHelper(
          raycaster.ray.direction,
          raycaster.ray.origin,
          100,
          0xff0000
        );
        scene.add(arrowHelper);

        const intersects = raycaster.intersectObjects(scene.children, true);
        if (intersects.length > 0) {
          const selectedObject = intersects[0].object;
          console.log("selectedObject", selectedObject);
        }
      }

      document.body.addEventListener("click", onClick, false);
      let animationId;
      function animate() {
        animationId = requestAnimationFrame(animate);
        doRender();
      }

      function doRender() {
        renderer.render(scene, camera);
      }
    </script>
  </body>
</html>

Live example

https://codesandbox.io/s/xenodochial-euclid-6glof3?file=/index.html

Screenshots

No response

Version

0.151.2

Device

No response

Browser

No response

OS

No response

Mugen87 commented 1 year ago

That happens because the three.js still uses the wrong bounding volume for raycasting. I'll file a PR with a fix.

Mugen87 commented 1 year ago

@hangaoke1 The failing ray casting is indeed a bug but I have noticed that your model has strange scale settings. You can easily see this in the three.js editor that your assets consist of a hierarchy of 3D objects (normal) with very different scale settings per 3d object (not normal). That is required because your skeleton is quite big for whatever reasons (you can see this at the position of the bones).

I recommend you fix your model in Blender such that no scale adjustments are required and the model still has a real-world scale.

hangaoke1 commented 1 year ago

@hangaoke1 The failing ray casting is indeed a bug but I have noticed that your model has strange scale settings. You can easily see this in the three.js editor that your assets consist of a hierarchy of 3D objects (normal) which very different scale settings (not normal). That is required because your skeleton is quite big for whatever reasons (you can see this at the position of the bones).

I recommend you fix your model in Blender such that no scale adjustments are required and the model still has a real-world scale.

Thank you very much for your answer. I'm not sure if it's Blender's fault, but when I import a normal model into Blender and export it without any processing, it ends up like this.

mrdoob commented 1 year ago

https://github.com/mrdoob/three.js/pull/25954