Three.js海量渲染模型

使用BatchedMesh渲染海量树模型,帧率接近60,页面流畅

批处理网格(BatchedMesh)

此处为官方文档的解释

Mesh 的特殊版本,支持多绘制批量渲染。如果您必须渲染大量具有相同材质但具有不同世界变换和几何形状的对象,请使用 BatchedMesh。使用 BatchedMesh 将帮助您减少绘制调用的数量,从而提高应用程序的整体渲染性能。

构造函数

less 复制代码
BatchedMesh( maxInstanceCount : Integer, maxVertexCount : Integer, maxIndexCount : Integer, material : Material, )
// 参数
maxInstanceCount - 计划添加的单个几何体的最大数量。
maxVertexCount - 所有几何体使用的最大顶点数。
maxIndexCount - 所有几何图形使用的最大索引数。
material - Material 的一个实例。默认是新的 MeshBasicMaterial。

BatchedMesh官方文档

核心代码

"three": "^0.178.0"

node版本 v18.12.0

ini 复制代码
// 加载模型
async initModel() {
  store.setProgress({ value: 80, status: true });

  // **2. 并行加载其他模型**
  const models = await Promise.all([
      this.PIModel.initFbx('model/yichang'),
      this.PIModel.initFbx('model/sphere'),
      this.PIModel.initFbx('model/shu1'),
      this.PIModel.initFbx('model/shu2'),
  ]);

  // **3. 处理加载完成的模型**
  const [yichang, sphere, tree1, shu] = models;

  yichang.children[0].material.transparent = true
  this.scene.add(yichang)
  this.helper.initWater();

  let pos = []
  let pos1 = []
  // sphere.traverse(ch => {
  //   if (ch.isMesh) {
  //     if (ch.name.includes('Sphere')) {
  //       pos.push(ch.position)
  //     } else if (ch.name.includes('Box')) {
  //       pos1.push(ch.position)
  //     }
  //     // const geometry = new THREE.BoxGeometry( 10, 10, 20 );
  //     // const material = new THREE.MeshBasicMaterial( {color: 0x00ffff} );
  //     // const cube = new THREE.Mesh( geometry, material );
  //     // cube.position.set(ch.position.x, ch.position.y, ch.position.z)
  //     // this.scene.add( cube );
  //   }
  // })
  for (let i = 0; i < 1000; i++) {
    const x = (Math.random() * 10000) - 5000
    const y = -60
    const z = (Math.random() * 10000) - 5000
    pos.push({ x, y, z })
  }
  for (let i = 0; i < 500; i++) {
    const x = Math.random() * 10000 - 5000
    const y = -60
    const z = Math.random() * 10000 - 5000
    pos1.push({ x, y, z })
  }
  this.pos = pos
  this.pos1 = pos1

  // this.scene.add(tree1)
  // this.scene.add(shu)
  // console.log(tree1)

  let originModel = []
  tree1.traverse(child => {
    if (child.isMesh) {
      child.castShadow = true
      child.material.transparent = true
      child.material.side = THREE.DoubleSide
      child.material.alphaTest = 0.65
      if (child.name === 'leaves') {
        child.material.color.set(0x3F5B2D)
        child.material.emissive.set(0x3F5B2D)
      } else if (child.name === 'branch') {
        child.material.color.set(0x443d3b)
        child.material.emissive.set(0x443d3b)
      }
      child.material.emissiveIntensity = 1
      child.material.emissiveMap = child.material.map

      originModel.push(child)
    }
  })
  let originModel2 = []
  shu.traverse(child => {
    if (child.isMesh) {
      child.castShadow = true
      child.material.transparent = true
      child.material.alphaTest = 0.7
      child.material.side = THREE.DoubleSide
      child.material.emissiveIntensity = 1
      child.material.emissiveMap = child.material.map

      originModel2.push(child)
    }
  })

  let dummy = new THREE.Object3D();
  let scales = {}

  const drawModel = (len, len1, model, pos, scaleC, rotation) => {
    const geometry = model.geometry.clone();
    // geometry.applyMatrix4(model.matrixWorld);
    const mesh = new THREE.BatchedMesh( len, len1 * 2048, len1 * 2048, model.material.clone() )
    const id = mesh.addGeometry(geometry)
    getInstance(len, mesh, scaleC, pos, id, rotation)
    // mesh.matrixWorldNeedsUpdate = true
    this.scene.add( mesh );
  }
  originModel.forEach(model => {
    drawModel(this.pos.length, originModel.length, model, this.pos, model.scale, { x: -Math.PI / 2 })
  })
  originModel2.forEach(model => {
    drawModel(this.pos1.length, originModel2.length, model, this.pos1, model.scale, { x: -Math.PI / 2 })
  })

  function getInstance(len, mesh, scaleC, pos1, index, rotation = {}) {
    for ( let i = 0; i < len; i ++ ) {

      const id = mesh.addInstance( index );
      const pos = pos1[i]
      const scale = scales[id] || Math.random() * 0.6 + 0.5;
      dummy.position.x = pos.x;
      dummy.position.y = pos.y - 15;
      dummy.position.z = pos.z;
      dummy.rotation.x = rotation.x || 0
      dummy.rotation.y = rotation.y || 0
      dummy.rotation.z = rotation.z || 0

      if (!scales[id]) {
        scales[id] = scale
      }
      dummy.scale.x = scaleC.x * scale
      dummy.scale.y = scaleC.y * scale
      dummy.scale.z = scaleC.z * scale

      dummy.updateMatrix();
      mesh.setMatrixAt(id, dummy.matrix)
    }
  }
  store.setProgress({ value: 100, status: false });
}

github源码

相关推荐
Flynt1 小时前
axios 被投毒半年后,我把 npm 的"安全防线"实测了一遍:最信任的 SHA512 是安慰剂
前端·npm·node.js
excel2 小时前
Base62 和 Base64 有什么区别?
前端
风骏时光牛马3 小时前
大模型底层架构与AI源码深度剖析
前端
掘金挖土4 小时前
前端手摸手跑路之 AI 应用开发(九)
前端·后端
中文呀6 小时前
三维 GIS 离线缓存实战:逆向 CesiumOfflineCache,并亲手为它补上"自动更新"能力
前端
前进前进前进7 小时前
TRAE 全链路实战:做了一个警示牌一键生成器:纯 Canvas、单文件、零依赖
前端
小万7428 小时前
God‘s Eye View 深度解析:15 路公开信号 + CesiumJS 的浏览器级 OSINT 控制台,60 帧渲染的“真实间谍卫星模拟器”
前端
IT星宿8 小时前
浏览器标签页全是一个图标:我用名字哈希生成 favicon,让每个项目都有身份标识
前端
全栈项目管理程序猿8 小时前
ArcGIS JS 基础教程(32):体元系列 - VoxelSlice 体元切片
前端·javascript