5
votes

I want to overlay/switch on&off a texture from a GLTF model, but can't find how or if is it possible. I was able to load it perfectly, but when I try to reload or overlay the texture nothing happens or I get some errors. The last thing I tried don't give me any errors, but the model keeps the original texture. I also tried to unload the model and reload again with other texture, no success either.

I'm using the standard THREE.GLTFLoader example from three.js docs, just changed the 3d loaded model and added a function to do this changes after some time. I'm probabily confusing everything, if you can help I will appreciate, thanks in advance.

Accessed links that does not help me: ThreeJS: Remove object from scene; three.js Switching objects on click; How do I change the texture of a GLTF model dynamically?; Change texture of loaded .obj in three.js at runtime; Import another Texture at runtime within THREE.JS and GLTF

Code I'm using:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>three.js webgl - glTF loader</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            font-family: Monospace;
            background-color: #000;
            color: #fff;
            margin: 0px;
            overflow: hidden;
        }
        #info {
            color: #fff;
            position: absolute;
            top: 10px;
            width: 100%;
            text-align: center;
            z-index: 100;
            display:block;
        }
        #info a {
            color: #75ddc1;
            font-weight: bold;
        }
    </style>
</head>

<body>
    <div id="info"><!--
        <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader<br />
        Battle Damaged Sci-fi Helmet by
        <a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br /> -->
    </div>

    <script src="build/three.js"></script>

    <script src="js/controls/OrbitControls.js"></script>
    <script src="js/loaders/GLTFLoader.js"></script>

    <script src="js/Detector.js"></script>
    <script src="js/libs/stats.min.js"></script>

    <script>

        if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

        var container, stats, controls;
        var camera, scene, renderer, light;
        var globalObject;

        init();
        animate();

        function init() {

            container = document.createElement( 'div' );
            document.body.appendChild( container );

            camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 2000 );
            camera.position.set( -250.8, 260.9, 262.7 );


            controls = new THREE.OrbitControls( camera );
            controls.target.set( 0, -0.2, -0.2 );
            controls.update();

            // envmap
            var path = 'textures/cube/Bridge2/';
            var format = '.jpg';
            var envMap = new THREE.CubeTextureLoader().load( [
                path + 'posx' + format, path + 'negx' + format,
                path + 'posy' + format, path + 'negy' + format,
                path + 'posz' + format, path + 'negz' + format
            ] );

            scene = new THREE.Scene();
            scene.background = envMap;

            light = new THREE.HemisphereLight( 0xbbbbff, 0x444422 );
            light.position.set( 0, 1, 0 );
            scene.add( light );

            // model
            var loader = new THREE.GLTFLoader();
            loader.load( 'models/testeFinal2-1/testeFinal2-1.gltf', function ( gltf ) {

                globalObject = gltf.scene;

                gltf.scene.traverse( function ( child ) {

                    if ( child.isMesh ) {

                        child.material.envMap = envMap;

                    }

                } );

                scene.add( gltf.scene );

            } );

            renderer = new THREE.WebGLRenderer( { antialias: true } );
            renderer.setPixelRatio( window.devicePixelRatio );
            renderer.setSize( window.innerWidth, window.innerHeight );
            renderer.gammaOutput = true;
            container.appendChild( renderer.domElement );

            window.addEventListener( 'resize', onWindowResize, false );

            // stats
            stats = new Stats();
            container.appendChild( stats.dom );

        }

        function onWindowResize() {

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

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

        }

        //

        function animate() {

            requestAnimationFrame( animate );

            renderer.render( scene, camera );

            stats.update();

        }

        setTimeout(newTexture(), 7000); //Do the changes after 7s.

        function newTexture() {

            scene.remove( globalObject ); //This don't give me any error, but does nothing.

            // envmap
            var path = 'textures/cube/Bridge2/';
            var format = '.jpg';
            var envMap = new THREE.CubeTextureLoader().load( [
                path + 'posx' + format, path + 'negx' + format,
                path + 'posy' + format, path + 'negy' + format,
                path + 'posz' + format, path + 'negz' + format
            ] );

            var loader = new THREE.GLTFLoader(); //Trying to reload the second time, does nothing either.
            loader.load( 'models/testeFinal2-1/2/testeFinal2-1.gltf', function ( gltf ) {

                gltf.scene.traverse( function ( child ) {

                    if ( child.isMesh ) {

                        child.material.envMap = envMap;

                    }

                } );

                scene.add( gltf.scene );

            } );
        }           

    </script>

</body>

UPDATE1 (removed the newTexture function and edited the model's loader):

// model
            var loader = new THREE.GLTFLoader();
            loader.load( 'models/testeFinal2-1/testeFinal2-1.gltf', function ( gltf ) {

                globalObject = gltf.scene;

                gltf.scene.traverse( function ( child ) {

                    if ( child.isMesh ) {

                        child.material.envMap = envMap;

                        setTimeout(function () {
                            child.material.map.image.currentSrc = "/models/testeFinal2-1/2/finalTest2_ORTO2.jpg";
                            child.material.map.image.src = "/models/testeFinal2-1/2/finalTest2_ORTO2.jpg";
                        }, 5000);

                    }

                } );

                scene.add( gltf.scene );

            } );
1
A couple suggestions: (1) if you load the same model on gltf-viewer.donmccurdy.com and switch between different environment maps, do you see a difference? (2) you shouldn't need to reload the model to change the environment map, try modifying the original meshes, (3) on each child where child.isMesh === true, try logging the value of child.material. Are any of the materials an array, like [ Material, Material ]? If you have a multi-material mesh you'll need to add the envMap to each individually. - Don McCurdy
(1) The background remains white, but I notice changes in the model's lighting. (2) I tried to change only the texture characteristics, I put the edits as UPDATE1 if you want to see. Again I did not receive any errors, but nothing happens either. (3) I received one item: "MeshStandardMaterial", with no arrays. - leonardofmed
(1) yes it only affects the model's lighting unless you change a setting, (2) modifying the image .src will have no effect, you'll need to use a TextureLoader instance here, (3) hm ok that's not the issue then. Maybe I'm confused — is changing .map not working, .envMap, or both? You can reassign .map with something like: var texLoader = new THREE.TextureLoader(); var texture = texLoader.load('foo.png'); child.material.map = texture. - Don McCurdy
I think my problem is related to .map, the model's texture (.jpg). The problem with the TextureLoader instance is that the texture gets the wrong position in the model, even if I reload the original texture file. That is why I was trying to load it with GLTFLoader, but no success either. - leonardofmed
Ah I see now. On the baseColor (.map) texture, be sure to set texture.flipY=false. - Don McCurdy

1 Answers

6
votes

After loading a model, you can use THREE.TextureLoader to attach a new texture to the model. When doing so, be sure to set texture.flipY=false to match the UV orientation of a glTF model.

var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load( 'foo.png' );
texture.flipY = false;

var loader = new THREE.GLTFLoader();
loader.load( 'foo.glb', ( gltf ) => {
  var model = gltf.scene;
  model.traverse ( ( o ) => {
    if ( o.isMesh ) {
      // note: for a multi-material mesh, `o.material` may be an array,
      // in which case you'd need to set `.map` on each value.
      o.material.map = texture;
    }
  } );
  scene.add( model );
} );