Performance and instancing
Quick answer
This tutorial attacks draw-call overhead rather than triangle count. It builds trunk and leaf InstancedMesh objects for thousands of trees, fills matrices with setMatrixAt and colors with setColorAt, and flags instanceMatrix and instanceColor for upload. It then merges static varied shapes with BufferGeometryUtils.mergeGeometries and switches detail levels through LOD.update. Readers can choose instancing, merging, culling or LOD based on renderer.info.render.calls.
Key points
- Set instanceMatrix.needsUpdate after every batch of setMatrixAt calls.
- Merge static geometry with BufferGeometryUtils.mergeGeometries after baking transforms.
- Call lod.update(camera) inside the render loop for distance-based switching.
Example
const COUNT = 8000;
const trunk = new THREE.CylinderGeometry(0.6, 0.9, 12, 6);
const leaf = new THREE.SphereGeometry(3.4, 8, 6);
const trunkMesh = new THREE.InstancedMesh(trunk, trunkMaterial, COUNT);
const leafMesh = new THREE.InstancedMesh(leaf, leafMaterial, COUNT);
const dummy = new THREE.Object3D();
const color = new THREE.Color();
for (let i = 0; i < COUNT; i++) {
const x = (Math.random() - 0.5) * 1600;
const z = (Math.random() - 0.5) * 1600;
const s = 0.7 + Math.random() * 0.6;
dummy.position.set(x, 6 * s, z);
dummy.rotation.y = Math.random() * Math.PI * 2;
dummy.scale.set(s, s, s);
dummy.updateMatrix();
trunkMesh.setMatrixAt(i, dummy.matrix);
dummy.position.y = 12 * s + 2.4 * s;
dummy.scale.setScalar(s);
dummy.updateMatrix();
leafMesh.setMatrixAt(i, dummy.matrix);
color.setHSL(0.28 + Math.random() * 0.08, 0.45, 0.32 + Math.random() * 0.12);
leafMesh.setColorAt(i, color);
}
trunkMesh.instanceMatrix.needsUpdate = true;
leafMesh.instanceMatrix.needsUpdate = true;
leafMesh.instanceColor.needsUpdate = true;
scene.add(trunkMesh, leafMesh);import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
const parts = [];
buildings.forEach(function (b) {
const geo = new THREE.BoxGeometry(b.w, b.h, b.d);
geo.translate(b.x, b.h / 2, b.z);
geo.deleteAttribute('uv'); // 属性不一致会导致合并失败
parts.push(geo);
});
const merged = mergeGeometries(parts, false);
const mesh = new THREE.Mesh(merged, new THREE.MeshStandardMaterial({
color: 0x9aa7b4,
roughness: 0.9
}));
scene.add(mesh);
Notes
- Instance counts are fixed at construction, and extra setMatrixAt calls write out of bounds.
- Merging geometries with mismatched attributes returns null with only a console warning.
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Atlaslog (2026). Performance and instancing. https://www.jingweizhi.com/en/three/tutorials/instancing.html (accessed 2026-11-03).