ThreeJS的性能优化方面

1、网格合并

简介

多数情况下使用组可以很容易地操纵和管理大量网格。但是当对象的数量非常多时,性能就会成为一个瓶颈。使用组,每个对象还是独立的,仍然需要对它们分别进行处理和渲染。通过
THREE.Geometry.merge() 函数,你可以将多个几何体合并起来创建一个联合体。

当我们使用普通组的情况,绘制20000个立方体,帧率在15帧左右,如果我们选择合并以后,再绘制两万,就会发现,我们可以轻松的渲染20000个立方体,而且没有性能的损失。合并的代码如下:

javascript 复制代码
//合并模型,则使用merge方法合并
var geometry = new THREE.Geometry();
//merge方法将两个几何体对象或者Object3D里面的几何体对象合并,(使用对象
的变换)将几何体的顶点,面,UV分别合并.
//THREE.GeometryUtils: .merge() has been moved to Geometry.
Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.
for(var i=0; i<gui.numberOfObjects; i++){
var cube = addCube();
cube.updateMatrix();
geometry.merge(cube.geometry, cube.matrix);
}
scene.add(new THREE.Mesh(geometry, cubeMaterial));

THREE.GeometryUtils.merge() 已经将此方法移动到了 THREE.Geometry 对象的上面了,我们使用 addCube 方法进行立方体的创建,为了确保能正确的定位和旋转合并的 THREE.Geometry 对象,我们不仅向 merge 函数提供 THREE.Geometry 对象,还提供该对象的变换矩阵。当我们将此矩阵添加到 merge 函数后,那么合并的方块将被正确定位。

和组的优缺点对比

缺点: 组能够对每个单独的个体进行操作,而合并网格后则失去对每个对象的单独控制。想要移

动、旋转或缩放某个方块是不可能的。
优点: 性能不会有损失。因为将所有的的网格合并成为了一个,性能将大大的增加。如果需要创建大型的、复杂的几何体。我们还可以从外部资源中创建、加载几何体。

2、尽量重用Material和Geometry

这里以Material和Geometry为例(使用比较频繁)

javascript 复制代码
for (var i = 0; i < 100; i++) {
    var material = new THREE.MeshBasicMaterial();
    var geometry = new THREE.BoxGeometry(10, 10, 10);
    var mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
}

改为

javascript 复制代码
var material = new THREE.MeshBasicMaterial();
var geometry = new THREE.BoxGeometry(10, 10, 10);
for (var i = 0; i < 100; i++) {
    var mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
}

3、尽量使用clone方法

这个的原理其实跟2是一样的

4、重点优化requestAnimationFrame内的方法

我们知道几乎所有的资源都是花费在requestAnimationFrame内的方法,所以我们在需要的时候去执行,如果不需要则不执行。

5、删除模型时,将材质和几何体从内存中清除

使用 remove() 将模型从场景内删除掉,大家会发现内存基本上没有怎么降低。因为几何体和材质还保存在内存当中,我们需要手动调用 dispose() 方法将其从内存中删除。

javascript 复制代码
1. item.geometry.dispose(); //删除几何体
2. item.material.dispose(); //删除材质

6、在循环渲染中避免使用更新

这里的更新指的是当前的几何体、材质、纹理等发生了修改,需要 Three.js 重新更新显存的数据,具体包括:

几何体:

javascript 复制代码
geometry.verticesNeedUpdate = true; //顶点发生了修改
geometry.elementsNeedUpdate = true; //面发生了修改
geometry.morphTargetsNeedUpdate = true; //变形目标发生了修改
geometry.uvsNeedUpdate = true; //uv映射发生了修改
geometry.normalsNeedUpdate = true; //法向发生了修改
geometry.colorsNeedUpdate = true; //顶点颜色发生的修改

材质

javascript 复制代码
material.needsUpdate = true

纹理

javascript 复制代码
texture.needsUpdate = true;

如果它们发生更新,则将其设置为true,Three.js会通过判断,将数据重新传输到显存当中,并将配置项重新修改为false。这是一个很耗运行效率的过程,所以我们尽量只在需要的时候修改,不要放到render()方法当中循环设置。

只在需要的时候渲染

如果在没有操作的时候,让循环一直渲染属于浪费资源,接下来我来带给大家一个只在需要时渲染的方法。

首先在循环渲染中加入一个判断,如果判断值为true时,才可以循环渲染:

javascript 复制代码
var renderEnabled;
function animate() {
if (renderEnabled) {
    renderer.render(scene, camera);
}
    requestAnimationFrame(animate);
}
animate();

然后设置一个延迟器函数,每次调用后,可以将 renderEnabled 设置为 true ,并延迟三秒将其设

置为 false ,这个延迟时间大家可以根据需求来修改:

javascript 复制代码
//调用一次可以渲染三秒
let timeOut = null;
function timeRender() {
//设置为可渲染状态
    renderEnabled = true;
//清除上次的延迟器
if (timeOut) {
    clearTimeout(timeOut);
}
    timeOut = setTimeout(function () {
    renderEnabled = false;
}, 3000);
}

接下来,我们在需要的时候调用这个 timeRender() 方法即可,比如在相机控制器更新后的回调

中:

javascript 复制代码
controls.addEventListener('change', function(){
    timeRender();
});

如果相机位置发生变化,就会触发回调,开启循环渲染,更新页面显示。

如果我们添加了一个模型到场景中,直接调用一下重新渲染即可:

javascript 复制代码
scene.add(mesh);
timeRender();
javascript 复制代码
最后,一个重点问题,就是材质的纹理由于是异步的,我们需要在图片添加完成后,触发回调。好在
Three.js已经考虑到了这一点,Three.js的静态对象THREE.DefaultLoadingManager的onLoad回调会在
每一个纹理图片加载完成后触发回调,依靠它,我们可以在Three.js的每一个内容发生变更后触发重新
渲染,并且在闲置状态会停止渲染。
javascript 复制代码
//每次材质和纹理更新,触发重新渲染
THREE.DefaultLoadingManager.onLoad = function () {
    timeRender();
};
相关推荐
小彭努力中2 天前
138. CSS3DRenderer渲染HTML标签
前端·深度学习·3d·webgl·three.js
优雅永不过时·2 天前
three.js实现地球 外部扫描的着色器
前端·javascript·webgl·three.js·着色器
阿铎前端5 天前
Three.js PBR材质
vue·three.js
二三ErSan8 天前
深入探索ES 3D Editor:一个基于ThreeJS + Vue 3 + Naive UI的三维编辑器
three.js
Sword999 天前
【ThreeJs原理解析】第2期 | 旋转、平移、缩放实现原理
前端·three.js·源码阅读
小白菜学前端10 天前
Threejs 材质贴图、光照和投影详解
前端·3d·three.js
破浪前行·吴11 天前
使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件
前端·react.js·three.js
格瑞@_@18 天前
11.Three.js使用indexeddb前端缓存模型优化前端加载效率
前端·javascript·缓存·three.js·indexeddb缓存
谢小飞18 天前
我做了三把椅子原来纹理这样加载切换
前端·three.js
小白菜学前端19 天前
ThreeJS创建一个3D物体的基本流程
3d·three.js