在three.js中运行时更改已加载的.obj文件的纹理

14

我正在尝试在加载的three.js .obj上在运行时交换图像纹理。以下是直接从three.js示例中获取的代码,稍作修改:

        var container, stats;
        var camera, scene, renderer;
        var mouseX = 0, mouseY = 0;
        var windowHalfX = window.innerWidth / 2;
        var windowHalfY = window.innerHeight / 2;


        init();
        animate();


        function init() {

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

            camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
            camera.position.z = 100;

            //scene
            scene = new THREE.Scene();

            var ambient = new THREE.AmbientLight( 0x101030 );
            scene.add( ambient );

            var directionalLight = new THREE.DirectionalLight( 0xffeedd );
            directionalLight.position.set( 0, 0, 1 );
            scene.add( directionalLight );

            //manager
            var manager = new THREE.LoadingManager();
            manager.onProgress = function ( item, loaded, total ) {

                console.log( item, loaded, total );

            };

            //model
            var loader = new THREE.OBJLoader( manager );
            loader.load( 'obj/female02/female02.obj', function ( object ) {
                object.traverse( function ( child ) {

                    if ( child instanceof THREE.Mesh ) {
                        //create a global var to reference later when changing textures
                        myMesh = child;
                        //apply texture
                        myMesh.material.map = THREE.ImageUtils.loadTexture( 'textures/ash_uvgrid01.jpg');
                        myMesh.material.needsUpdate = true;
                    }

                } );


                object.position.y = - 80;
                scene.add( object );

            } );

            //render
            renderer = new THREE.WebGLRenderer();
            renderer.setSize( window.innerWidth, window.innerHeight );
            container.appendChild( renderer.domElement );

            document.addEventListener( 'mousemove', onDocumentMouseMove, false );
            window.addEventListener( 'resize', onWindowResize, false );

        }

        function newTexture() {
            myMesh.material.map = THREE.ImageUtils.loadTexture( 'textures/land_ocean_ice_cloud_2048.jpg');
            myMesh.material.needsUpdate = true;
        }

        function onWindowResize() {

            windowHalfX = window.innerWidth / 2;
            windowHalfY = window.innerHeight / 2;

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

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

        }

        function onDocumentMouseMove( event ) {

            mouseX = ( event.clientX - windowHalfX ) / 2;
            mouseY = ( event.clientY - windowHalfY ) / 2;

        }

        //animate
        function animate() {

            requestAnimationFrame( animate );
            render();

        }

        function render() {

            camera.position.x += ( mouseX - camera.position.x ) * .05;
            camera.position.y += ( - mouseY - camera.position.y ) * .05;

            camera.lookAt( scene.position );

            renderer.render( scene, camera );

        }
我添加的唯一内容是newTexture函数和对网格的引用作为myMesh。这是原始示例(http://threejs.org/examples/webgl_loader_obj.html)。该函数不会抛出任何错误,但.obj文件不会更新。我知道我在这里只是漏了一些基本的东西...
更新:根据下面的优秀答案,这是正确的代码,并通过输入字段交换纹理的一些补充:
  var container, stats;
  var camera, scene, renderer;
  var mouseX = 0, mouseY = 0;
  var windowHalfX = window.innerWidth / 2;
  var windowHalfY = window.innerHeight / 2;
  var globalObject;

  init();
  animate();

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

      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
      camera.position.z = 100;

      //scene
      scene = new THREE.Scene();

      var ambient = new THREE.AmbientLight( 0x101030 );
      scene.add( ambient );

      var directionalLight = new THREE.DirectionalLight( 0xffeedd );
      directionalLight.position.set( 0, 0, 1 );
      scene.add( directionalLight );

      //manager
      var manager = new THREE.LoadingManager();
      manager.onProgress = function (item, loaded, total) {
        console.log( item, loaded, total );
      };

    //model
    var loader = new THREE.OBJLoader( manager );
    loader.load( 'obj/female02/female02.obj', function (object) {
        //store global reference to .obj
        globalObject = object;

      object.traverse( function (child) {
          if ( child instanceof THREE.Mesh ) {
              child.material.map = THREE.ImageUtils.loadTexture( 'textures/grid.jpg');
              child.material.needsUpdate = true;
          }
      });

      object.position.y = - 80;
      scene.add( object );
    });

    //render
    renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    container.appendChild( renderer.domElement );

    document.addEventListener( 'mousemove', onDocumentMouseMove, false );
    window.addEventListener( 'resize', onWindowResize, false );
  }

  function onWindowResize() {
    windowHalfX = window.innerWidth / 2;
    windowHalfY = window.innerHeight / 2;
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
  }

  function onDocumentMouseMove( event ) {
    mouseX = ( event.clientX - windowHalfX ) / 2;
    mouseY = ( event.clientY - windowHalfY ) / 2;
  }

  //animate
  function animate() {
    requestAnimationFrame( animate );
    render();
  }

  function render() {
    camera.position.x += ( mouseX - camera.position.x ) * .05;
    camera.position.y += ( - mouseY - camera.position.y ) * .05;
            camera.lookAt( scene.position );
            renderer.render( scene, camera );
  }

  function newTexture() {
    var newTexturePath = "textures/" + document.getElementById("texture").value + "";

    globalObject.traverse( function ( child ) {
      if (child instanceof THREE.Mesh) {
          //create a global var to reference later when changing textures
          child;
          //apply texture
          child.material.map = THREE.ImageUtils.loadTexture(newTexturePath);
          child.material.needsUpdate = true;
      }
    });
  }

在这种情况下,不需要设置child.material.needsUpdate = true;。它已经被loadTexture()为您设置了。如果异步纹理加载被延迟,自己设置needsUpdate可能会导致问题。 - WestLangley
感谢您更新代码 <3 - Peter Pint
您的解决方案能够与fabricjs一起使用吗? - naoval luthfi
1个回答

13
问题在于变量object中有多个子网格。由于只有一个myMesh全局变量,您仅存储了最后一个子网格。因此,当您尝试使用此全局变量更新纹理时,只有一个网格会获得纹理更新,可能是不清楚可见的小隐藏部分。
解决方法要么是:
  • 存储一个myMeshes数组全局变量,并在每次出现一个网格时将其推入myMesh中。然后,在您的newTexture()函数中,遍历此数组中的所有项目并更新它们的映射/材质。
  • 或者,将返回自OBJLoader回调的object变量存储在单个全局变量中。然后,在您的newTexture()函数中,像代码中所做的那样遍历所有网格(使用traverse()if语句),并更新它们的映射/材质。
最后,在代码中的某处调用newTexture()也会有所帮助;)

1
现在它可以工作了,谢谢!我使用了第二个选项。是的,我在控制台中调用了newTexture(),所以不太清楚。我已经发布了上面的工作代码,让大家受益。 - user1054351

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接