Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override GLTF materials in three.js

I am trying to create a dynamic way to display solar data on a gltf imported model in three.js. The aim would be to associate different solid colours to different segments of the model and be able to switch these off and on. My current hurdle is changing the colour of the material in gltf.

I have tried using an ObjLoader instead so that I can input my own material, but this didn't work :/

Here is the js I have currently:

        const gltfLoader = new THREE.GLTFLoader();
        gltfLoader.load('Building.glb', (gltf) => {
            const root = gltf.scene;
            scene.add(root);

            const box = new THREE.Box3().setFromObject(root);
            const boxSize = box.getSize(new THREE.Vector3()).length();
            const boxCenter = box.getCenter(new THREE.Vector3());

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

            controls.maxDistance = boxSize * 10;
            controls.target.copy(boxCenter);
            controls.update();
          });


    function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
        const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
        const halfFovY = THREE.Math.degToRad(camera.fov * .5);
        const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
        const direction = (new THREE.Vector3())
        .subVectors(camera.position, boxCenter)
        .multiply(new THREE.Vector3(1, 0, 1))
        .normalize();

        camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));

        camera.near = boxSize / 100;
        camera.far = boxSize * 100;

        camera.updateProjectionMatrix();

        // point the camera to look at the center of the box
        camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
    }

    renderer = new THREE.WebGLRenderer({canvas: myCanvas, antialias: true});
    renderer.setClearColor(0x000000);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild( renderer.domElement );



    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;
    }

    function render() {
        if (resizeRendererToDisplaySize(renderer)) {
            const canvas = renderer.domElement;
            camera.aspect = canvas.clientWidth / canvas.clientHeight;
             camera.updateProjectionMatrix();
            }
            renderer.render(scene, camera);
            requestAnimationFrame(render);
         }
         requestAnimationFrame(render);
like image 254
Jamie Vincent Avatar asked Jun 19 '19 05:06

Jamie Vincent


2 Answers

The gltf.scene result returned by the loader is a THREE.Scene instance – once you have it, it doesn't matter much what file format was used to load the model. In this case, you'll want to traverse that scene and change the materials of any meshes within it. The MeshStandardMaterial and Mesh documentation may be helpful.

var model = gltf.scene;
var newMaterial = new THREE.MeshStandardMaterial({color: 0xff0000});
model.traverse((o) => {
  if (o.isMesh) o.material = newMaterial;
});

If you need to change the textures, note the instructions in the GLTFLoader documentation.

like image 63
Don McCurdy Avatar answered Sep 28 '22 07:09

Don McCurdy


As an addendum to the answer above, a gltf.scene does not correspond to a THREE.Scene. A gltf.scene is a THREE.Group instance, but you should be able to traverse as stated above. https://github.com/mrdoob/three.js/blob/94f043c4e105eb73236529231388402da2b07cba/examples/jsm/loaders/GLTFLoader.js#L3890

In case of Three.js, it would be better to load models using the GLTFLoader, since the Wavefront (.obj) format only supports geometry data. It can't store other stuff like materials, animations, etc. which is most likely why it wasn't working for you.

Your best approach as Don stated is to just traverse the gltf.scene and access meshes individually (as per the GLTFLoader code, the meshes should be named according to the exported names in the gltf file, so you should be able to identify meshes using the mesh.name property if you use a DCC application like blender or maya to name them).

like image 34
Adr7000 Avatar answered Sep 28 '22 08:09

Adr7000