6

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

            } );
leonardofmed
  • 842
  • 3
  • 13
  • 47
  • A couple suggestions: (1) if you load the same model on https://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 Sep 10 '18 at 05:46
  • (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 Sep 11 '18 at 00:50
  • (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 Sep 11 '18 at 02:47
  • 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 Sep 11 '18 at 13:20
  • Ah I see now. On the baseColor (`.map`) texture, be sure to set `texture.flipY=false`. – Don McCurdy Sep 11 '18 at 18:32
  • That is it! This is what I was looking for! I really appreciate your help! Do you want to write the answer? – leonardofmed Sep 11 '18 at 18:47

1 Answers1

8

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 );
} );
Don McCurdy
  • 10,975
  • 2
  • 37
  • 75
  • 1
    You need to store a reference to the model after it loads, then modify that in your click event handler — if you're not sure how to set up a click event handler or use one with three.js, I'd recommend opening a new SO question. :) – Don McCurdy Sep 14 '18 at 00:03
  • How to set all sides and set different texture(Ex: left:tex1, Right:tex2....) – vadivel a Feb 27 '20 at 09:15
  • @vadivela see https://stackoverflow.com/questions/17418118/three-js-cube-with-different-texture-on-each-face. – Don McCurdy Feb 27 '20 at 16:13