吾曰:朕饿了~

116 阅读3分钟

three优化篇

清除dispose()

/* 清除不再使用内存模型几何体与材质 防止泄露 */
mesh.remove()
1.
mesh.traverse((obj:any)=> {
    if (obj.type === 'Mesh') {
      obj.geometry.dispose();
      obj.material.dispose();
    }
})

2.
model.traverse((obj) => {
    if (!obj.isMesh) return;
    obj.geometry.dispose();
    obj.material.dispose();
});
model = null;

终极奥义dispose()
function disposeChild(mesh) {
    if (mesh instanceof THREE.Mesh) {
      if (mesh.geometry?.dispose) {
        mesh.geometry.dispose(); //删除几何体
      }
      if (mesh.material?.dispose) {
        mesh.material.dispose(); //删除材质
      }
      if (mesh.material?.texture?.dispose) {
        mesh.material.texture.dispose();
      }
    }
    if (mesh instanceof THREE.Group) {
      mesh.clear();
    }
    if (mesh instanceof THREE.Object3D) {
      mesh.clear();
    }
  }
  
scene.traverse(item => {
  disposeChild(item);
})
THREE.Cache.clear();
scene.clear();
renderer.dispose();
renderer.forceContextLoss();

懒加载优化requestAnimationFrame()

1. 节流渲染
let animId: boolean;
let timeOut: string | number | NodeJS.Timeout | null | undefined = null;
onMounted(() => {
  /* 四.渲染循环 */
  let render = () => {
    stats.update();
    if (animId) {
      controls.update();
      composer.render();
    }
    /* 动画事件 */
    requestAnimationFrame(render);
  }  
  /* 防抖事件 */
  const timeRender = ()=>{
    animId = true;
    if (timeOut) {
      clearTimeout(timeOut);
    }
    timeOut = setTimeout(()=>{
      animId = false;
    },5000)
  }
  controls.addEventListener('change', () => {
    timeRender()
  });
  window.addEventListener('mousemove',()=>{
    timeRender()
  });
  /* 每次材质和纹理更新,触发重新渲染,并且在闲置状态会停止渲染 */
  THREE.DefaultLoadingManager.onLoad = function () {
      timeRender();
  };
}
2.判断循环事件
/* 雪 */
if (snowbol) {
  snowGroup.children.forEach((sprite: any) => {
    sprite.position.y -= t * 50;
    if (sprite.position.y < 0) {
      sprite.position.y = 600 * Math.random(); // 与y 对应
    }
  })
}

压缩模型

1.
pnpm i -g gltf-pipeline

public/model/test01.gltf -o 表示需要压缩的模型文件地址
public/model/copy.gltf -d 表示压缩出来的存储位置及名字
gltf-pipeline -i three.gltf -t 保存单独的纹理

pnpm gltf-pipeline -i public/three/shunx2.glb -o /public/three/shunx2plus.glb -d 
2.
打开Blender导入模型
导出模型、 点击数据、 勾选压缩

加载压缩模型

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';

/* 压缩 */

function loaderDraco() {
    const loader = new GLTFLoader();
    const draco = new DRACOLoader();
    // DRACOLoader依赖examples\jsm\libs\下的 draco 里面多个解压文件
    draco.setDecoderPath('./draco/');//根据pubic里面解压文件结构设置
    loader.setDRACOLoader(draco);
    return loader;
}

export default loaderDraco;
import * as THREE from 'three';
import  loaderDraco  from '../hooks/DracoLoader';

let group = new THREE.Group();
loaderDraco().load(`/three/测试.glb`, (gltf) => {
    group = gltf2.scene
}

删除不必要的面

一个面不会被渲染,是被遮挡的,建模的时候,可以不绘制。比如一个箱子放在地面固定不动,底部平面就可以不绘制。

平面删除多余的顶点

美术三维建模的时候,为了提升渲染性能,首先要注意的就是三角形数量或者说顶点数量。

平面删除多余的顶点

比如一个矩形平面,一般四个顶点就可以了,从视觉上看,多了也没什么用,除非说特殊情况。

曲面控制顶点数量

曲面在不影响光滑程度的情况下,尽量减少面数。

一般来说,距离相机越远,对表面细分程度要求越低,你可以Blender生成一个球体,同样细分程度,距离相机位置不同,然后在threejs测试对比。

Blender模型合并演示

选择多个Mesh:

-   方式1:Shift快捷键+鼠标点击,选择多个模型。
-   方式2:右侧目录树:右键父节点,点击选择层级
-   方式3:选择——选择相连元素——材质 (或者快捷键Shift + L)

合并多个Mesh,转化成一个Mesh:物体——合并 (或者快捷键Ctrl + J)

Blender设置纹理贴图

在Blender中创建一个球体网格模型,然后通过材质,设置一个颜色贴图,实现上面代码同样的效果球体+环境贴图效果。

设置好贴图之后,导出gltf,threejs加载查看设置贴图的模型。

纹理贴图大小(性能优化)

如果纹理贴图像素比较大,一方面会占用渲染的更多硬件资源,另一方面网页从服务器加载图片文件的时间会更长。

共享几何体,减小模型文件

对于外形一样的物体,可以考虑共享几何体。

后处理细分度

根据相机远近动态改变 ssaaRenderPass.sampleLevel 值渲染

参考值

单个场景的所有模型总面数不要超过100万,最低可承受的设备水平为:GTX 1050

控制元素数量,可以有效的降低场景的卡顿
单个场景建议元素数量 小于1000

企业微信截图_17173815733731.png