我们继续使用three.js清理内存

介绍



我最近写了关于我使用three.js在应用程序中清理内存的经验让我提醒您,目标是使用gltf模型重绘几个场景。



从那时起,我进行了许多实验,我认为有必要用这篇小文章来补充我之前所说的内容。以下几点可帮助我提高应用程序的性能。



主要部分



通过研究Three.js上的各种垃圾收集示例,我对threejsfundamentals.org上提出的方法感兴趣但是,在实现建议的配置并将所有材料和几何图形包装在this.track()中之后,事实证明,加载新场景时,GPU上的负载继续增长。而且,由于无法在这些类中使用track(),因此所提出的示例无法与EffectComposer以及其他用于后期处理的类一起正常工作。



由于显而易见的原因,在所有使用的类中添加ResourceTracker的解决方案并不吸引人,因此,我决定补充用于清理提到的类的方法。以下是一些已使用的技术:



接待1.粗糙



在cleanup方法之后添加renderer.info。我们逐一从应用程序中删除资源,以了解它们中的哪些构成了负载并隐藏在纹理或材质中。这不是解决问题的方法,而只是调试某人可能不知道的方法。



接待2.长



打开所用类的代码(例如AfterimagePass,可在three.js github上找到)后,我们查看在何处创建需要清理的资源,以在所需框架内保持几何形状和材料的数量。



this.textureComp = new WebGLRenderTarget( window.innerWidth, window.innerHeight, { ... }


那就是你所需要的。根据文档,WebGLRenderTarget具有清除功能以清除内存。我们得到类似:



class Scene {
//...
    postprocessing_init(){ //   
        this.afterimagePass = new AfterimagePass(0);
        this.composer.addPass(this.afterimagePass);
    }
//...
}
//...

class ResourceTracker {
//...
    dispose() {
    //...
    sceneObject.afterimagePass.WebGLRenderTarget.dispose();
    //...
    }
}


接待3



可以,但是在这种情况下清理代码会变得肿。让我们尝试使用上一篇文章中我们熟悉的方法。让我提醒您,在其中我们实现了disposeNode(节点)方法,该方法在资源中搜索可以清除的内容。disposeNode()可能看起来像这样:



disposeNode(node) {
            node.parent = undefined;
            if (node.geometry) {
                node.geometry.dispose();
            }
            let material = node.material;
            if (material) {
                if (material.map) {
                    material.map.dispose();
                }
                if (material.lightMap) {
                    material.lightMap.dispose();
                }
                if (material.bumpMap) {
                    material.bumpMap.dispose();
                }
                if (material.normalMap) {
                    material.normalMap.dispose();
                }
                if (material.specularMap) {
                    material.specularMap.dispose();
                }
                if (material.envMap) {
                    material.envMap.dispose();
                }
                material.dispose();
            }
        } else if (node.constructor.name === "Object3D") {
            node.parent.remove(node);
            node.parent = undefined;
        }
    }


太好了,现在让我们使用所有使用的其他类并将其添加到ResourceTracker中:



dispose() {
    for (let key in sceneObject.afterimagePass) {
        this.disposeNode(sceneObject.afterimagePass[key]);
    }
    for (let key in sceneObject.bloomPass) {
        this.disposeNode(sceneObject.bloomPass[key]);
    }
    for (let key in sceneObject.composer) {
        this.disposeNode(sceneObject.composer[key]);
    }
}


结果



所有这些操作的结果是,我大大提高了FPS并减少了应用程序中的GPU负载。我可能没有正确使用ResourceTracker,但无论如何对其他类都无济于事。我从未见过任何地方通过我们的disposeNode(节点)在EffectComposer上进行迭代会影响内存中的纹理数量(但实际上是这种情况)。此问题应分开考虑。



为了进行比较,以前的版本将保留在旧地址,而新版本则可以单独查看



该项目以某种形式出现在github上



我很高兴听到您在类似项目中的经验并讨论细节!



All Articles