【问题标题】:How do I clear the memory properly in THREE.js如何在 THREE.js 中正确清除内存
【发布时间】:2018-02-21 11:28:48
【问题描述】:

我可以让模型渲染,我可以旋转和缩放,一切都很好,直到我尝试清除场景。我想通过单击button#clear 清除场景。

我希望遍历场景,并将其子元素添加到数组中。然后,我希望遍历数组,并为数组中的每个项目调用dispose() 清除它,如果它是几何体或材质,然后从场景中移除网格。

<html>
  <head>
    <title>Three.js</title>
    <link rel="stylesheet" href="css/main.css" />
  </head>
  <body>
    <button id="clear">Clear Three Out</button>
    <script src="js/three.js"></script>
    <script src="js/OrbitControls.js"></script>
    <script src="js/ObjectLoader.js"></script>
    <script>
    <script>
      (function(){var script=document.createElement('script');script.onload=function(){var stats=new Stats();document.body.appendChild(stats.dom);requestAnimationFrame(function loop(){stats.update();requestAnimationFrame(loop)});};script.src='//rawgit.com/mrdoob/stats.js/master/build/stats.min.js';document.head.appendChild(script);})();
      var scene = new THREE.Scene();

      var width = window.innerWidth;
      var height = window.innerHeight;
      var camera = new THREE.OrthographicCamera(width / - 2, width / 2, height / 2, height / - 2, -1000, 1000); // new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

      var blackReflectiveMaterial = new THREE.MeshBasicMaterial();     
      var renderer = new THREE.WebGLRenderer();
      renderer.setSize( window.innerWidth, window.innerHeight );
      document.body.appendChild( renderer.domElement );

      // handle resize event
      window.addEventListener( 'resize', function() {
        var width = window.innerWidth;
        var height = window.innerHeight;

        renderer.setSize(width, height);
        camera.aspect = width / height;
        camera.updateProjectionMatrix();
      } );

      // controls
      controls = new THREE.OrbitControls( camera, renderer.domElement );
      camera.position.z = 3;

      // model loader
      var loader = new THREE.ObjectLoader();

      loader.load(
        'models/revol-uv.json', // model of a compound bow
        function( object ) {
          console.log('object: ', object);
          object.scale.x = object.scale.y = object.scale.z = 200;

          object.traverse( function(child) {
            child.material = blackReflectiveMaterial;
          });

          scene.add( object );
        }
      );

      scene.translateY(-345);

      // array to hold Scene's children
      var holder = [];

      // when click the #clear button, call this function
      document.getElementById('clear').onclick = function() {
        scene.traverse( function ( child ) {
          if (child.type === "Mesh") {
            holder.push(child);
          }

        } );
        console.log('holder before clean: ', holder);
        for (var i = 0; i < holder.length; i++) {
          if (holder[i].geometry) {
            holder[i].geometry.dispose();
          }

          console.log('holder[i].material: ', holder[i].material);
          if (holder[i].material) {
            if (holder[i].map) {
              holder[i].material.map.dispose();
            }
            holder[i].material.dispose();
          }

          console.log('holder[i]: ', holder[i]);
          scene.remove(holder[i]);
        }
        console.log('holder after clean: ', holder); // all the objects are still there
      }

      // logic
      var update = function() {

        // any code to rotate ... blah blah not important
      };

      // draw Scene
      var render = function() {
        renderer.render( scene, camera );
      };

      // run loop (update, render, repeat)
      var bowLoop = function() {
        requestAnimationFrame( bowLoop );

        update();
        render();
      };
      bowLoop();
    </script>
  </body>
</html>

这是控制台日志:

场景的孩子

Array(10)
0:THREE.Mesh {uuid: "5574129D-7093-4567-8927-4F07BB7A3928", name: "cable-arm", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
1:THREE.Mesh {uuid: "ED867F5C-F6C4-45BC-BBF7-9DD5061A0CF6", name: "cams", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
2:THREE.Mesh {uuid: "D69C58BB-1E03-4D00-8050-6F2F4739DFE9", name: "handles", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
3:THREE.Mesh {uuid: "50A98720-4E8F-4873-B987-0C993D05FE65", name: "hardware", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
4:THREE.Mesh {uuid: "6CE44421-0614-40E9-8013-FE939CFA0191", name: "limb-pockets", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
5:THREE.Mesh {uuid: "60FF4521-B19B-477D-8223-8B4BFA2A0F5A", name: "limbs", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
6:THREE.Mesh {uuid: "D2980C91-0B19-4757-A831-A3B46546E579", name: "limbsavers", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
7:THREE.Mesh {uuid: "D9858B1C-E4F7-40C4-98DD-163230F9A917", name: "riser", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
8:THREE.Mesh {uuid: "BC7C199B-2867-4446-86FE-D244D7A32E07", name: "string-supressor", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
9:THREE.Mesh {uuid: "02A038E9-FB0B-43FF-B3F2-BCCAFD7E10AC", name: "strings", type: "Mesh", parent: T…E.Scene, children: Array(0), …}

clean之前的holder数组:

Array(10)
0:THREE.Mesh {uuid: "5574129D-7093-4567-8927-4F07BB7A3928", name: "cable-arm", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
1:THREE.Mesh {uuid: "ED867F5C-F6C4-45BC-BBF7-9DD5061A0CF6", name: "cams", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
2:THREE.Mesh {uuid: "D69C58BB-1E03-4D00-8050-6F2F4739DFE9", name: "handles", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
3:THREE.Mesh {uuid: "50A98720-4E8F-4873-B987-0C993D05FE65", name: "hardware", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
4:THREE.Mesh {uuid: "6CE44421-0614-40E9-8013-FE939CFA0191", name: "limb-pockets", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
5:THREE.Mesh {uuid: "60FF4521-B19B-477D-8223-8B4BFA2A0F5A", name: "limbs", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
6:THREE.Mesh {uuid: "D2980C91-0B19-4757-A831-A3B46546E579", name: "limbsavers", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
7:THREE.Mesh {uuid: "D9858B1C-E4F7-40C4-98DD-163230F9A917", name: "riser", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
8:THREE.Mesh {uuid: "BC7C199B-2867-4446-86FE-D244D7A32E07", name: "string-supressor", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
9:THREE.Mesh {uuid: "02A038E9-FB0B-43FF-B3F2-BCCAFD7E10AC", name: "strings", type: "Mesh", parent: T…E.Scene, children: Array(0), …}

clean后的holder数组:

Array(10)
0:THREE.Mesh {uuid: "5574129D-7093-4567-8927-4F07BB7A3928", name: "cable-arm", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
1:THREE.Mesh {uuid: "ED867F5C-F6C4-45BC-BBF7-9DD5061A0CF6", name: "cams", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
2:THREE.Mesh {uuid: "D69C58BB-1E03-4D00-8050-6F2F4739DFE9", name: "handles", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
3:THREE.Mesh {uuid: "50A98720-4E8F-4873-B987-0C993D05FE65", name: "hardware", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
4:THREE.Mesh {uuid: "6CE44421-0614-40E9-8013-FE939CFA0191", name: "limb-pockets", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
5:THREE.Mesh {uuid: "60FF4521-B19B-477D-8223-8B4BFA2A0F5A", name: "limbs", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
6:THREE.Mesh {uuid: "D2980C91-0B19-4757-A831-A3B46546E579", name: "limbsavers", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
7:THREE.Mesh {uuid: "D9858B1C-E4F7-40C4-98DD-163230F9A917", name: "riser", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
8:THREE.Mesh {uuid: "BC7C199B-2867-4446-86FE-D244D7A32E07", name: "string-supressor", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
9:THREE.Mesh {uuid: "02A038E9-FB0B-43FF-B3F2-BCCAFD7E10AC", name: "strings", type: "Mesh", parent: T…E.Scene, children: Array(0), …}

内存没有变化...对象仍在堆中。

【问题讨论】:

标签: javascript three.js


【解决方案1】:

想到两件事:

  • 看起来holder 在调用 clear 函数后仍然持有对所有对象的引用(h/t @pailhead)。您应该在函数末尾添加类似holder = []; 的内容以摆脱它们。还要确保没有其他可访问的变量持有对任何对象的引用。

  • 请注意,对象不会自动从内存中消失,只有在垃圾收集器完成工作后才会消失。取决于发生了多少,否则可能需要一点时间。

【讨论】:

    【解决方案2】:

    @gaitat - 感谢您提供大量解决方案。

    我只需要修改一些行来满足我的需要:

      function disposeNode(node) {
        if (node instanceof THREE.Mesh) {
          if (node.geometry) {
            node.geometry.dispose();
            node.geometry = undefined; // fixed problem
          }
    
          if (node.material) {
            if (node.material instanceof THREE.MeshFaceMaterial || node.material instanceof THREE.MultiMaterial) {
              node.material.materials.forEach( function(mtrl, idx) {
                if (mtrl.map) mtrl.map.dispose();
                if (mtrl.lightMap) mtrl.lightMap.dispose();
                if (mtrl.bumpMap) mtrl.bumpMap.dispose();
                if (mtrl.normalMap) mtrl.normalMap.dispose();
                if (mtrl.specularMap) mtrl.specularMap.dispose();
                if (mtrl.envMap) mtrl.envMap.dispose();
    
                mtrl.dispose();
                mrtl = undefined; // fixed problem
              } );
            }
            else {
              if (node.material.map) node.material.map.dispose();
              if (node.material.lightMap) node.material.lightMap.dispose();
              if (node.material.bumpMap) node.material.bumpMap.dispose();
              if (node.material.normalMap) node.material.normalMap.dispose();
              if (node.material.specularMap) node.material.specularMap.dispose();
              if (node.material.envMap) node.material.envMap.dispose();
    
              node.material.dispose();
              node.material = undefined; // fixed problem
            }
          }
        }
        console.log('node before removal: ', node);
        scene.remove( node );
        renderer.dispose(); // ***EDIT*** improved even memory more original scene heap is 12.4 MB; add objects increases to 116 MB or 250 MB (different models), clearing always brings down to 13.3 MB ... there still might be some artifacts.  
        node = undefined; // unnecessary
      }
    
      function disposeHierchy(node, callback) {
        for (var i = node.children.length - 1; i >= 0; i--) {
          var child = node.children[i];
    
          disposeHierchy(child, callback);
          callback(child);
        }
      }
    

    叫它:

    document.getElementById('clear').onclick = function() {
        disposeHierchy(scene, disposeNode);
        console.log('renderer.info.memory after: ', renderer.info);
      }
    

    【讨论】:

    • 我不认为这个 Q/A 对其他人有很大帮助。很多代码通过梳理,也许你应该概述 - 放在一个单独的块或注释中,完成修复的行。 node = undefined 实际上似乎不应该做任何事情。如果你在其他地方持有对那个东西的引用,它仍然会在那里。
    • @pailhead。完成。
    • 我认为您仍然可能会遇到同样的问题。只需拥有var foo = someNode.geometry,该对象就会保留下来。您遍历的方式似乎很有意义,但我不确定可以给出什么样的建议,或者确保您不会将参考悬垂在您不想要的地方的最佳实践.
    • 你有什么建议?加载对象时,堆为 134MB,当我清除它时,它下降到 27.9MB。结果是一致的。
    • 让我们看看在three.js中foo.dispose()的主要职责是什么...因为纹理和几何图形需要在GPU上才能使用,它们也必须从GPU 通过实际的 webgl 调用,例如 gl.deleteBuffer( buffer )。您可以释放对几何图形的所有引用,但否则它的内容将保留在 gpu 上。不过回到 JS 领域,实际上并没有 delete myGeometryInstance,而是您必须自己管理它并了解垃圾收集的工作原理。
    【解决方案3】:

    请检查以下代码以加载 GLTF 文件并释放内存...

    let ZomModel = undefined;
    function loadZoom() {
        const gltfLoader = new GLTFLoader();
        gltfLoader.load(modelName, (gltf) => {
            const root = gltf.scene;
            setmaterial(root);
            ZomModel = root;
            root.position.set(0, -3, 0);
            root.scale.set(5, 5, 5);
            scene.add(root);
            console.log(root, 'root')
        });
    }
    function loadNew(isLoad) {
        console.log("reload " + isLoad);
        // return;
        if (isLoad) {
            if (ZomModel == undefined)
                loadZoom();
        } else {
            // defultmaterial(ZomModel);
            if (ZomModel == undefined) return;
            ZomModel.traverse(object => {
                if (!object.isMesh) return
                scene.remove(object);
                console.log('dispose geometry!')
                object.geometry.dispose()
                if (object.material.isMaterial) {
                    cleanMaterial(object.material)
                } else {
                    // an array of materials
                    for (const material of object.material) cleanMaterial(material)
                }
                object.geometry = undefined;
                object = undefined;
            });
            scene.remove(ZomModel);
            ZomModel = undefined;
        }
    }
    
    const cleanMaterial = material => {
        console.log('dispose material!')
        // dispose textures
        for (const key of Object.keys(material)) {
            const value = material[key]
            if (value && typeof value === 'object' && 'minFilter' in value) {
                console.log('dispose texture! ' + key)
                value.dispose();
            }
        }
        material.dispose();
        material = undefined;
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-21
      • 2019-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-10
      • 2018-03-13
      相关资源
      最近更新 更多