Three 之 three.js (webgl)模型的删除/场景的清空/内存的释放 的简单整理

Three 之 three.js (webgl)模型的删除/场景的清空/内存的释放 的简单整理

目录

Three 之 three.js (webgl)模型的删除/场景的清空/内存的释放 的简单整理

一、简单介绍

二、实现原理

三、场景中的模型 group (mesh)

四、清空 scene 场景,释放内存

五、参考补充:Three.js 内存释放问题(如果不正确处理,可能并未释放,还会占用内存)


一、简单介绍

Three js 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用。

本节介绍, three.js (webgl)模型的删除/场景的清空/内存的释放 的简单整理,供大家一个优化思考的方向,如果有不足之处,欢迎指出,或者你有更好的方法,欢迎留言。

通过Threejs开发Web3D应用的时候,可能需要删除场景中的模型对象,如果想从一个场景Scene或组对象Group删除一个三维模型对象,不仅仅只是.remove(),记得必要时还要·dispose()

在开发3D场景时,若遇到需要动态添加删改模型、场景,页面切换重渲染时,为避免内存叠加占用,需要手动清除场景所占用的内存,避免溢出与资源浪费。

二、实现原理

1、一般的模型记住,要对 geometry 和 material 进行 dispose(),在对 模型 mesh 进行 remove()

2、清空 scene 场景,释放内存,关键以下

  • 使用 dispose() 清除所有网格模型几何体的顶点缓冲区占用内存
  • 使用 object.clear() 销毁模型对象,清除页面内存
  • 暂停 requestAnimationFrame() 方法,避免无意义运行
  • 清空 canvas画布,置空dom与相关元素

三、场景中的模型 group (mesh)

1、删除场景对象中Scene一个子对象Group,并释放组对象Group中所有网格模型几何体的顶点缓冲区占用内存

deleteObject(group) {
    // 递归遍历组对象group释放所有后代网格模型绑定几何体占用内存
    group.traverse(function(obj) {
        if (obj.type === 'Mesh') {
        obj.geometry.dispose();
        obj.material.dispose();
      }
    })

    // 删除场景对象scene的子对象group
    scene.remove(group);
}

2、另一种方法,清除Group

 clearGroup(group) {
    // 释放 几何体 和 材质
    const clearCache = (item) => {
      item.geometry.dispose();
      item.material.dispose();
    };
    
    // 递归释放物体下的 几何体 和 材质
    const removeObj = (obj) => {
      let arr = obj.children.filter((x) => x);
      arr.forEach((item) => {
        if (item.children.length) {
          removeObj(item);
        } else {
          clearCache(item);
          item.clear();
        }
      });
      obj.clear();
      arr = null;
    };

    // 移除 group
    removeObj(group);
  }

记得:
1)如果是场景中
scene.remove(group); // 删除组

2)如果是 组中
groups.remove(group);// 删除模型

四、清空 scene 场景,释放内存

 // 清空场景,包括 scene 场景下的动画,子物体,renderer,camera,control,以及自己使用过的变量置空处理 等
 clearScene() {

    cancelAnimationFrame(this.animationId);

    this.scene.traverse((child) => {
      if (child.material) {
        child.material.dispose();
      }
      if (child.geometry) {
        child.geometry.dispose();
      }
      child = null;
    });

    // 场景中的参数释放清理或者置空等
    this.sceneDomElement.innerHTML = '';
    this.renderer.forceContextLoss();
    this.renderer.dispose();
    this.scene.clear();
    this.flows = [];
    this.scene = null;
    this.camera = null;
    this.controls = null;
    this.renderer.domElement = null;
    this.renderer = null;
    this.sceneDomElement = null;
    console.log('clearScene');
  }

五、参考补充:Three.js 内存释放问题(如果不正确处理,可能并未释放,还会占用内存)

参考博文:Three.js 内存释放问题

1、常规方法不能彻底清除掉scene场景内的一些geometry、texture等,而且就算页面离开也不会自动释放内存。打开任务管理器,可能看到CPU一直被占用,切换页面时越来越高。

//注意: 这里释放场景,并未处理必要的scene子物体的 geometry 、material 等
beforeDestroy() {
	try {
		scene.clear();
		renderer.dispose();
		renderer.forceContextLoss();
		renderer.content = null;
		cancelAnimationFrame(animationID) // 去除animationFrame
		let gl = renderer.domElement.getContext("webgl");
		gl && gl.getExtension("WEBGL_lose_context").loseContext();
	}catch (e) {
		console.log(e)
	}
}

2、这边封装了一个track方法,来管理释放场景中的 mesh、geometry、texture、object3D、外部加载的obj模型、gltf模型等 的释放

1)TrackResource 方法:

import * as THREE from 'three/build/three.module'
export default class ResourceTracker {
   constructor() {
   	this.resources = new Set();
   }
   track(resource) {
   	if (!resource) {
   		return resource;
   	}

   	// handle children and when material is an array of materials or
   	// uniform is array of textures
   	if (Array.isArray(resource)) {
   		resource.forEach(resource => this.track(resource));
   		return resource;
   	}

   	if (resource.dispose || resource instanceof THREE.Object3D) {
   		this.resources.add(resource);
   	}
   	if (resource instanceof THREE.Object3D) {
   		this.track(resource.geometry);
   		this.track(resource.material);
   		this.track(resource.children);
   	} else if (resource instanceof THREE.Material) {
   		// We have to check if there are any textures on the material
   		for (const value of Object.values(resource)) {
   			if (value instanceof THREE.Texture) {
   				this.track(value);
   			}
   		}
   		// We also have to check if any uniforms reference textures or arrays of textures
   		if (resource.uniforms) {
   			for (const value of Object.values(resource.uniforms)) {
   				if (value) {
   					const uniformValue = value.value;
   					if (uniformValue instanceof THREE.Texture ||
   						Array.isArray(uniformValue)) {
   						this.track(uniformValue);
   					}
   				}
   			}
   		}
   	}
   	return resource;
   }
   untrack(resource) {
   	this.resources.delete(resource);
   }
   dispose() {
   	for (const resource of this.resources) {
   		if (resource instanceof THREE.Object3D) {
   			if (resource.parent) {
   				resource.parent.remove(resource);
   			}
   		}
   		if (resource.dispose) {
   			resource.dispose();
   		}
   	}
   	this.resources.clear();
   }
}

2)引入track,初始化

	import ResourceTracker from "../../../common/3D/dispose";
	// 在外层定义resMgr和track
	let resMgr = new ResourceTracker();
	const track = resMgr.track.bind(resMgr);

3)将所有的mesh、geometry、texture、object3D、外部加载的obj模型、gltf模型等等需要add to scene的物体,全部调用一下track方法

let xxx = track(new Three.Vector3());
obj.geometry.computeBoundingBox();
obj.geometry.boundingBox.getCenter(center);
let wrapper = track(new Three.Object3D());
wrapper.position.set(center.x,center.y,center.z);
obj.position.set(-center.x,-center.y,-center.z);
wrapper.add(obj);
scene.add(wrapper);
return warpper;

4)track到的3D物体内存释放

beforeDestroy() {
	try {
		scene.clear();
		resMgr && resMgr.dispose()
		renderer.dispose();
		renderer.forceContextLoss();
		renderer.content = null;
		cancelAnimationFrame(animationID)
		let gl = renderer.domElement.getContext("webgl");
		gl && gl.getExtension("WEBGL_lose_context").loseContext();
		console.log(renderer.info)   //查看memery字段即可
	}catch (e) {
		console.log(e)
	}
}

猜你喜欢

转载自blog.csdn.net/u014361280/article/details/124309410