
使用BatchedMesh渲染海量树模型,帧率接近60,页面流畅
批处理网格(BatchedMesh)
此处为官方文档的解释
Mesh 的特殊版本,支持多绘制批量渲染。如果您必须渲染大量具有相同材质但具有不同世界变换和几何形状的对象,请使用 BatchedMesh。使用 BatchedMesh 将帮助您减少绘制调用的数量,从而提高应用程序的整体渲染性能。
构造函数
less
BatchedMesh( maxInstanceCount : Integer, maxVertexCount : Integer, maxIndexCount : Integer, material : Material, )
// 参数
maxInstanceCount - 计划添加的单个几何体的最大数量。
maxVertexCount - 所有几何体使用的最大顶点数。
maxIndexCount - 所有几何图形使用的最大索引数。
material - Material 的一个实例。默认是新的 MeshBasicMaterial。
核心代码
"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 });
}