How to make outline for model of gltf file?

I use raycaster for dectect the model of ‘gltf file’, I want to when I mouse move through object, threre are outline around, and when I mouse move out of object outline will disapear. But I’ve only got outline when I move through. Please tell me what i need to do. i’m new one in three.js. Here is my code.

let pickableMeshes = [];

  {

    const gltfLoader = new GLTFLoader();

    gltfLoader.load('models/gltf/room/big_room/scene.gltf', (gltf) => {

      const root = gltf.scene;

      scene.add(root);

     // console.log(dumpObject(root).join('\n'));

      // compute the box that contains all the stuff

      // from root and below

      const box = new THREE.Box3().setFromObject(root);

      const boxSize = box.getSize(new THREE.Vector3()).length();

      const boxCenter = box.getCenter(new THREE.Vector3());

      // set the camera to frame the box

      frameArea(boxSize * 0.5, boxSize, boxCenter, camera);

      // get a list of all the meshes in the scen

      root.traverse((node) => {

        if (node instanceof THREE.Mesh) {

          pickableMeshes.push(node);

        }

      });

      // update the Trackball controls to handle the new size

      controls.maxDistance = boxSize/5;

      //controls.minDistance = boxSize;

      controls.target.copy(boxCenter);

      controls.update();

    });

  }

  function resizeRendererToDisplaySize(renderer) {

    const canvas = renderer.domElement;

    const width = canvas.clientWidth;

    const height = canvas.clientHeight;

    const needResize = canvas.width !== width || canvas.height !== height;

    if (needResize) {

      renderer.setSize(width, height, false);

    }

    return needResize;

  }

  class PickHelper {

    constructor() {

      this.raycaster = new THREE.Raycaster();

      this.pickedObject = null;

      this.pickedObjectSavedMaterial = null;

      this.selectMaterial = new THREE.MeshBasicMaterial();

      this.infoElem = document.querySelector('#info');

    }

    pick(normalizedPosition, scene, camera, time) {

      // restore the color if there is a picked object

      if (this.pickedObject) {

        this.pickedObject.material = this.pickedObjectSavedMaterial;

        this.pickedObject = undefined;

        this.infoElem.textContent = '';

      }

      // cast a ray through the frustum

      this.raycaster.setFromCamera(normalizedPosition, camera);

      // get the list of objects the ray intersected

      const intersectedObjects = this.raycaster.intersectObjects(pickableMeshes);

      if (intersectedObjects.length) {

        // pick the first object. It's the closest one

        this.pickedObject = intersectedObjects[0].object;

        // save its color

        this.pickedObjectSavedMaterial = this.pickedObject.material;

        this.infoElem.textContent = this.pickedObject.name;

        if(this.infoElem.textContent=='mesh_28')

        {
           var outlineMaterial = new THREE.MeshLambertMaterial( { color: 0x00ffff, side: THREE.BackSide  } );

           outlineMaterial.onBeforeCompile = (shader) => {

           const token = '#include <begin_vertex>'

           const customTransform = `

          vec3 transformed = position + objectNormal*0.5;

          `

          shader.vertexShader = 

          shader.vertexShader.replace(token,customTransform)

          } 

          var outlineMesh = new THREE.Mesh( this.pickedObject.geometry, outlineMaterial );

          this.pickedObject.add(outlineMesh);

          const geometry1 = new THREE.BoxGeometry( 1, 1, 1 );

          const material1 = new THREE.MeshBasicMaterial( {color: 0x00ffff} );

          const cube1 = new THREE.Mesh( geometry1, material1 );

          this.pickedObject.add( cube1 );

        }

        else

        {
           scene.remove(this.pickedObject);

        }

      }

    }

  }

  const pickPosition = {x: 0, y: 0};

  const pickHelper = new PickHelper();

  clearPickPosition();

  function render(time) {

    if (resizeRendererToDisplaySize(renderer)) {

      const canvas = renderer.domElement;

      camera.aspect = canvas.clientWidth / canvas.clientHeight;

      camera.updateProjectionMatrix();

    }

   // composer.render();

    pickHelper.pick(pickPosition, scene, camera, time);

    renderer.render(scene, camera);

    requestAnimationFrame(render);

  }

  requestAnimationFrame(render);

  function getCanvasRelativePosition(event) {

    const rect = canvas.getBoundingClientRect();

    return {

      x: event.clientX - rect.left,

      y: event.clientY - rect.top,

    };

  }

  function setPickPosition(event) {

    const pos = getCanvasRelativePosition(event);

    pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;

    pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y

  }

  function clearPickPosition() {

    // unlike the mouse which always has a position

    // if the user stops touching the screen we want

    // to stop picking. For now we just pick a value

    // unlikely to pick something

    pickPosition.x = -100000;

    pickPosition.y = -100000;

    //pickHelper.pickedObject.remove();

  }

  window.addEventListener('mousemove', setPickPosition);

  window.addEventListener('mouseout', clearPickPosition);

  window.addEventListener('mouseleave', clearPickPosition);

  window.addEventListener('touchstart', (event) => {

    // prevent the window from scrolling

    event.preventDefault();

    setPickPosition(event.touches[0]);

  }, {passive: false});

  window.addEventListener('touchmove', (event) => {

    setPickPosition(event.touches[0]);

  });

  window.addEventListener('touchend', clearPickPosition);

}

main();

Both this and this should be able to help you (also possibly this-ultra-pretty-new-pixel-demo-with-normals-and-depth-based-outline-that-did-I-mention-looks-super-pretty-:pleading_face:, but it’s not merged yet :sweat_smile: )

1 Like