# ThreeJS - Using Raycaster to select faces on model with OBJ Loader

**URL:** https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356
**Category:** Questions
**Tags:** raycaster, geometry
**Created:** [May 18, 2021, 2:02am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356 "2021-05-18T02:02:26Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![Joshua\_Paul](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/joshua_paul/32/18016_2.png) [@Joshua\_Paul](https://discourse.threejs.org/u/Joshua_Paul)
#### Post date: [May 18, 2021, 2:02am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/1 "2021-05-18T02:02:26Z")

</div>

I have been trying to get use raycaster to be able to select faces on the OBJ Model and make them transparent when the cursor is over them, but I am unable to get it working.

Heres the code:

```javascript
<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>

    <link rel="stylesheet" href="css/main.css">

</head>

<body>

    <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script> -->

    <script src="Three.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.2/TweenMax.min.js"></script>

    <script type = "module">

    import * as THREE from './three.module.js';

    import { MTLLoader } from './MTLLoader.js';

    import { OBJLoader } from './OBJLoader.js';

    import { OrbitControls } from './OrbitControls.js';

    let camera, scene, renderer;

    init();

    animate();

    var controls, mouse, raycaster;

        

function init() {

    

    const container = document.createElement( 'div' );

    document.body.appendChild( container );

    camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 20 );

    camera.position.set( 2, 0, 2 );

    // scene

    scene = new THREE.Scene();

    camera.lookAt( scene.position );

    const ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );

    scene.add( ambientLight );

    const pointLight = new THREE.PointLight( 0xffffff, 0.8 );

    camera.add( pointLight );

    scene.add( camera );

    // model

    const onProgress = function ( xhr ) {

        if ( xhr.lengthComputable ) {

            const percentComplete = xhr.loaded / xhr.total * 100;

            console.log( Math.round( percentComplete, 2 ) + '% downloaded' );

        }

    };

    const onError = function () { };

    new MTLLoader()

        .setPath( 'models/obj/' )

        .load( 'Solid1v2.mtl', function ( materials ) {

            

            materials.preload();

            new OBJLoader()

                .setMaterials( materials )

                .setPath( 'models/obj/' )

                .load( 'Solid1v2.obj', function ( object ) {

                    object.name = "Solid1";

                    scene.add( object );

                    console.log(object.name);

                }, onProgress, onError );

        } );

    //

    renderer = new THREE.WebGLRenderer();

    renderer.setPixelRatio( window.devicePixelRatio );

    renderer.setSize( window.innerWidth, window.innerHeight );

    container.appendChild( renderer.domElement );

    //

    mouse = new THREE.Vector2();

    raycaster = new THREE.Raycaster();

    function onMouseMove( event ) {

        // calculate mouse position in normalized device coordinates

        // (-1 to +1) for both components

        mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; //need to make the window.innerWidth same as the size of renderer.

        mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; //need to make the window.innerHeight same as the size of the renderer.

        /* console.log(mouse); */

        /* console.log(scene.children) */

    }

    

    //

    controls = new OrbitControls(camera, renderer.domElement);

    controls.update();

    window.addEventListener( 'resize', onWindowResize );

    window.addEventListener( 'mousemove', onMouseMove, false );

}

    

function onWindowResize() {

    camera.aspect = window.innerWidth / window.innerHeight;

    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );

}

function hoverPieces() {

    raycaster.setFromCamera(mouse, camera);

    console.log(scene.getObjectByName("Solid1"))

    const intersects = raycaster.intersectObjects(scene.getObjectByName("Solid1"),true); 

    if (intersects == true){

        console.log("hello")

    }

    for (let i = 0; i<intersects.length; i++) {

        intersects[i].object.material.transparent = true;

        intersects[i].object.material.opacity = 0.5;

        

    }

}

function animate() {

    requestAnimationFrame( animate );

    render();

}

function render() {

    hoverPieces();

    /* console.log(scene.getObjectByName("B1D239E1-056E-4455-B185-124CE29A8968",true)) */

    /* console.log(scene.getObjectByName("Solid1")); */

    renderer.render( scene, camera );

}

    

    </script>

</body>

</html>

```

---

<div class="post-metadata">

### Author: ![Joshua\_Paul](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/joshua_paul/32/18016_2.png) [@Joshua\_Paul](https://discourse.threejs.org/u/Joshua_Paul)
#### Post date: [May 18, 2021, 2:04am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/3 "2021-05-18T02:04:17Z")

</div>

Here is the model I am using:

[Solid1v2.obj](https://discourse.threejs.org/uploads/short-url/7TkLBYN3SdahtA2iAkvb5mXI5rd.obj) (15.5 KB)  
[Solid1v2.mtl](https://discourse.threejs.org/uploads/short-url/wAOcDIQYWv2o4Frn7BfnyNQbIm1.mtl) (619 Bytes)

---

<div class="post-metadata">

### Author: ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)
#### Post date: [May 18, 2021, 9:30am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/4 "2021-05-18T09:30:15Z")

</div>

> [@Joshua\_Paul](#):
>
> ```javascript
> intersects[i].object.material.transparent = true;
> 
> intersects[i].object.material.opacity = 0.5;
> 
> ```

This will make the material of the entire object transparent.

Notice that the faces of your geometry do no have individual materials. So you can’t configure material properties.

You should consider to use vertex colors for this use case. Meaning you create an additional four-component `color` buffer attribute, apply `(1,1,1,1)` as default color for all vertices and then set the `vertexColors` property of the material to `true`. When you click on the model, you can extract the face index from the intersection object and then modify the color buffer data (or more precise the alpha component of the color).

---

<div class="post-metadata">

### Author: ![rrrr\_rrrr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/rrrr_rrrr/32/7697_2.png) [@rrrr\_rrrr](https://discourse.threejs.org/u/rrrr_rrrr)
#### Post date: [May 18, 2021, 11:14am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/5 "2021-05-18T11:14:26Z")

</div>

Have you considered using **materialIndex**?

I do not use **BufferGeometry** , if you are OK with regular geometry ( pre 125 ), here is woking demo.

Hold down the **Ctrl** key and click on the mesh.  
[Z\_Trans.html](https://discourse.threejs.org/uploads/short-url/1OFKXMqrtLPrgEQMiFTR3rH0e86.html) (90.2 KB)

---

<div class="post-metadata">

### Author: ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)
#### Post date: [May 18, 2021, 12:14pm UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/6 "2021-05-18T12:14:08Z")

</div>

The problem with using multi-materials is that the OP would require a material per face. That would potentially kill performance since each face would produce a draw call.

---

<div class="post-metadata">

### Author: ![Joshua\_Paul](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/joshua_paul/32/18016_2.png) [@Joshua\_Paul](https://discourse.threejs.org/u/Joshua_Paul)
#### Post date: [May 19, 2021, 8:35am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/7 "2021-05-19T08:35:25Z")

</div>

I am still new to ThreeJS so I am not really sure how to implement this, is there an example or tutorial I could follow?

---

<div class="post-metadata">

### Author: ![rrrr\_rrrr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/rrrr_rrrr/32/7697_2.png) [@rrrr\_rrrr](https://discourse.threejs.org/u/rrrr_rrrr)
#### Post date: [May 19, 2021, 11:17am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/8 "2021-05-19T11:17:56Z")

</div>

> [@Mugen87](#):
>
> The problem with using multi-materials is that the OP would require a material per face. That would potentially kill performance since each face would produce a draw call.

Just wanted to make sure that one call per face applies only to BufferGeometry, not to regular geometry.

---

<div class="post-metadata">

### Author: ![rrrr\_rrrr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/rrrr_rrrr/32/7697_2.png) [@rrrr\_rrrr](https://discourse.threejs.org/u/rrrr_rrrr)
#### Post date: [May 21, 2021, 7:48am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/9 "2021-05-21T07:48:33Z")

</div>

Definitely there is a **bug** on how number of draw calls are calculated, when comes to regular geometry and material index.

---

<div class="post-metadata">

### Author: ![Fernando\_Caye](https://avatars.discourse-cdn.com/v4/letter/f/9dc877/32.png) [@Fernando\_Caye](https://discourse.threejs.org/u/Fernando_Caye)
#### Post date: [June 8, 2022, 3:36am UTC](https://discourse.threejs.org/t/threejs-using-raycaster-to-select-faces-on-model-with-obj-loader/26356/10 "2022-06-08T03:36:32Z")

</div>

Thanks a lot!.

Being new to three.js, how to write the actual code of the steps you described took a bit of time going back and forth with documentation, but in general the process you described was very useful.

1. Add vertexColors property to the material.

```javascript
          var material = new THREE.MeshBasicMaterial({
            map: texture,
            vertexColors: true,
          });

```

1. Add the ‘color’ attribute to the BufferGeometry.

```javascript
            // Get the length of attributes.
            const count = geometry.attributes.position.count;

            // Add color attribute
            geometry.setAttribute(
              "color",
              new THREE.BufferAttribute(new Float32Array(count * 3), 3)
            );

```

1. Initialize the colors of all the attributes (usually would be white, but I randomnized it so it could be easier to visualize the faces/vertices on my mesh)

```javascript
            // Initialize the colors to white
            const colors = geometry.attributes.color;
            for (let i = 0; i < count; i++) {
              colors.setXYZ(i, Math.random(), Math.random(), Math.random());
            }

```

1. When raycasting, get the intersection faces, set the color to black.

```javascript
  intersections.forEach((intersection) => {
    const colorAttribute = bufferedGeometry.getAttribute("color");
    colorAttribute.setXYZ(intersection.face.a, 0, 0, 0);
    colorAttribute.setXYZ(intersection.face.b, 0, 0, 0);
    colorAttribute.setXYZ(intersection.face.c, 0, 0, 0);
    colorAttribute.needsUpdate = true;
  });

```
