Three.js Collada - 如何正确地dispose()和释放内存(垃圾回收)?

10

我已经成功通过ColladaLoader导入了一个.dae场景。

问题是,我需要在几个.dae文件之间切换。

我似乎无法正确实现dispose方法。

        dae.traverse(function(obj) {

            console.log('unloading ' + obj.id);

            scene.remove(obj);

            if(obj.geometry)
                obj.geometry.dispose();
            if(obj.material)
                obj.material.dispose();
            if(obj.mesh)
                obj.mesh.dispose();
            if(obj.texture)
                obj.texture.dispose();

        });

        scene.remove(dae);

我到底做错了什么?

非常感谢您的帮助!


编辑:

这是整个代码。

    var renderer = null;
    var scene = null;
    var camera = null;
    var controls = null;
    var dae = null;
    //var loader = null;

    function init() {


        renderer = new THREE.WebGLRenderer( { alpha: 1, antialias: true, clearColor: 0xffffff } );
        renderer.setSize( 800, 600 );

        var elem = $('.main3d')[0];
        elem.appendChild( renderer.domElement );

        scene = new THREE.Scene();

        camera = new THREE.PerspectiveCamera( 20, 800/600, 1, 1000 );
        camera.position.set( 0, -100, 50 );
        //camera.lookAt( scene.position );
        controls = new THREE.TrackballControls( camera, renderer.domElement );

        var light = new THREE.AmbientLight( 0xffffff ); // soft white light
        scene.add( light );

        threeAnimate();


    }

    function load(url) {
        loader = new THREE.ColladaLoader();

            loader.load(url, function (collada) {
                dae = collada.scene;
                scene.add(dae);

            });

    }

    function unload() {

        dae.traverse(function(obj) {

            console.log('unloading ' + obj.id);

            scene.remove(obj);

            if(obj.geometry)
                obj.geometry.dispose();
            if(obj.material)
                obj.material.dispose();
            if(obj.mesh)
                obj.mesh.dispose();
            if(obj.texture)
                obj.texture.dispose();

        });

        scene.remove(dae);

    }

    var animFrame = null;
    function animate()  {

        animFrame = requestAnimationFrame( threeAnimate );
        renderer.render( scene, camera );
        controls.update();

    }
4个回答

31

这应该能完成工作:

function disposeNode (node)
{
    if (node instanceof THREE.Mesh)
    {
        if (node.geometry)
        {
            node.geometry.dispose ();
        }

        if (node.material)
        {
            if (node.material instanceof THREE.MeshFaceMaterial)
            {
                $.each (node.material.materials, function (idx, mtrl)
                {
                    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 ();
                    if (mtrl.alphaMap)          mtrl.alphaMap.dispose();
                    if (mtrl.aoMap)             mtrl.aoMap.dispose();
                    if (mtrl.displacementMap)   mtrl.displacementMap.dispose();
                    if (mtrl.emissiveMap)       mtrl.emissiveMap.dispose();
                    if (mtrl.gradientMap)       mtrl.gradientMap.dispose();
                    if (mtrl.metalnessMap)      mtrl.metalnessMap.dispose();
                    if (mtrl.roughnessMap)      mtrl.roughnessMap.dispose();

                    mtrl.dispose ();    // disposes any programs associated with the material
                });
            }
            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 ();
                if (node.material.alphaMap)         node.material.alphaMap.dispose();
                if (node.material.aoMap)            node.material.aoMap.dispose();
                if (node.material.displacementMap)  node.material.displacementMap.dispose();
                if (node.material.emissiveMap)      node.material.emissiveMap.dispose();
                if (node.material.gradientMap)      node.material.gradientMap.dispose();
                if (node.material.metalnessMap)     node.material.metalnessMap.dispose();
                if (node.material.roughnessMap)     node.material.roughnessMap.dispose();

                node.material.dispose ();   // disposes any programs associated with the material
            }
        }
    }
}   // disposeNode

function disposeHierarchy (node, callback)
{
    for (var i = node.children.length - 1; i >= 0; i--)
    {
        var child = node.children[i];
        disposeHierarchy (child, callback);
        callback (child);
    }
}

并且你使用它

disposeHierarchy (YOUR_OBJECT3D, disposeNode);

哇!这真是相当全面!我稍后会查看一下。感谢您抽出时间来回答! - だらんぎん じょん
太棒了!我使用disposeHierarchy()函数成功释放了700MB+的内存!太厉害了,谢谢你。希望这个答案也能帮助其他人。 - だらんぎん じょん
我发布了另一个问题(与此有关)。如果您能分享一下您对这个主题的知识,那就太棒了!提前感谢您! - だらんぎん じょん
感谢您的评论。我的答案基于我自己的研究和开发。 - gaitat
1
@gaitat,Light类上没有dispose方法。 - kishore
显示剩余5条评论

6

我对gaitat已经很好的答案进行了微调,只使用现在内置的场景遍历函数,去除$并处理MultiMaterial。为什么THREE没有内置的内存清理功能呢!?当你执行scene.dispose()时,它肯定应该做到这一点。我仍然在努力追踪我正在使用但似乎没有被dispose()的几个纹理,根据renderer.info.memory.textures

this.disposeNode = function (parentObject) {

    parentObject.traverse(function (node) {
        if (node instanceof THREE.Mesh) {
            if (node.geometry) {
                node.geometry.dispose();
            }

            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();    // disposes any programs associated with the material
                    });
                }
                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();   // disposes any programs associated with the material
                }
            }
        }
    });
}

1
这就是为什么它不会被“自动”删除。Delocating heap objects... 程序员必须参与其中,因为它并不像你想象的那样简单。在足够大的项目中,事情变得复杂,你最终会在场景之间重复使用材质、几何体甚至网格(我没有开玩笑,你可能最终会做到以上所有事情)。因此,轻易地删除东西只会带来痛苦和悲伤。拥有学习曲线并需要程序员参与可以防止您犯傻瓜错误。 - tfrascaroli
现在官方文档中有一篇非常好的文章介绍如何处理对象的释放:https://threejs.org/docs/index.html?q=dispose#manual/en/introduction/How-to-dispose-of-objects - Maccesch

3

在这里,基于之前的回答,这个代码处理材料的数组。

function disposeNode(parentObject) {
    parentObject.traverse(function (node) {
        if (node instanceof THREE.Mesh) {
            if (node.geometry) {
                node.geometry.dispose();
            }
            if (node.material) {
                var materialArray;
                if (node.material instanceof THREE.MeshFaceMaterial || node.material instanceof THREE.MultiMaterial) {
                    materialArray = node.material.materials;
                }
                else if(node.material instanceof Array) {
                    materialArray = node.material;
                }
                if(materialArray) {
                    materialArray.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();
                    });
                }
                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();
                }
            }
        }
    });
}

1

在网上进行了一些研究和重构后,我得出了以下结论:

  disposeNode = ( node, recursive = false ) => {

    if ( !node ) return;

    if ( recursive && node.children)
      for ( const child of node.children )
        disposeNode( child , recursive );

    node.geometry && node.geometry.dispose();
    
    if ( !node.material ) return;

    const materials = node.material.length === undefined ? [ node.material ] : node.material

    for ( const material of materials ) {

        for ( const key in material ) {

          const value = material[key];

          if ( value && typeof value === 'object' && 'minFilter' in value )
            value.dispose();

        }

        material && material.dispose();    
        
    }

  }

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