Three.js Collada - dispose() 和释放内存(垃圾收集)的正确方法是啥?

Posted

技术标签:

【中文标题】Three.js Collada - dispose() 和释放内存(垃圾收集)的正确方法是啥?【英文标题】:Three.js Collada - What's the proper way to dispose() and release memory (garbage collection)?Three.js Collada - dispose() 和释放内存(垃圾收集)的正确方法是什么? 【发布时间】:2016-01-14 02:58:01 【问题描述】:

我已经通过 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();

    

【问题讨论】:

【参考方案1】:

这应该可以完成工作:

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 光照类没有 dispose 方法【参考方案2】:

我调整了 gaitat 已经很棒的答案,只使用现在内置的场景遍历功能,删除 $ 并处理 MultiMaterial。为什么,哦为什么没有内置的内存清理三!?当你执行scene.dispose()时,它当然应该这样做。我仍在尝试追踪我正在使用的更多纹理,但根据renderer.info.memory.textures

似乎没有得到 dispose() ed
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
                
            
        
    );

【讨论】:

这就是它没有被自动删除的原因。 Delocating heap objects...。程序员必须参与进来,因为它并不像你想象的那么简单。在足够大的项目中,事情变得复杂,你最终会重复使用场景之间的材质、几何形状甚至网格(不是开玩笑,你最终可能会完成上述所有操作)。所以如此愉快地删除东西只会带来痛苦和悲伤。拥有学习曲线并且必须以程序员的身份参与进来,可以防止你犯愚蠢的错误。 现在官方文档中有一篇非常不错的关于处理东西的文章:threejs.org/docs/index.html?q=dispose#manual/en/introduction/…【参考方案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();
                
            
        
    );

【讨论】:

以上是关于Three.js Collada - dispose() 和释放内存(垃圾收集)的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Collada 纹理上的线框覆盖 - three.js

如何将 collada 对象加载到 three.js?

在 Three.JS 中通过鼠标单击选择 Collada 对象

Three.js - 从文件输入加载 Collada 文件(和纹理)

在 Three.js 中使用来自 COLLADA 文件的解析数据的骨骼动画

如何使用 THREE.js 向 collada 文件 (.dae) 添加纹理?