Course outline
Draw Calls and Instancing
In one line
Each mesh costs one draw call. When many objects share a shape and a material, InstancedMesh, BatchedMesh or merged geometry send them in one call. The counts prove it.
Think of it like this
A postman with 1000 letters can make 1000 trips to the same street, or one trip with a full bag. The letters are the same. The trips are the cost.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- instancing
- Drawing many copies of one mesh in a single draw call, each with its own matrix (and maybe colour).
- InstancedMesh
- A three.js mesh that draws N copies of one geometry and one material in one call.
- BatchedMesh
- A mesh that draws many copies of different geometries with one material, in one call.
- merged geometry
- Several geometries copied into one buffer, so they draw as one static mesh.
- attribute
- One per-vertex array in a geometry, such as position, normal or uv.
Step by step
01Make 1000 shapes four ways
Run the first example and read the four lines: same shape, material and places, only the packaging differs.
02Know what each gives up
Instances share geometry and material. Merged geometry cannot be moved one by one. BatchedMesh needs more CPU. Plain meshes are simplest but cost one call each.
03Break the rules on purpose
The second example merges mismatched geometries (an error and null), merges with two materials (2 draw calls), sets an instance count of 0 (nothing drawn), and moves instances far away (the mesh vanishes until its bounding sphere is recomputed).
04Look at the real bike
The third example loads the course's bike twice: as exported (23 meshes plus the two instanced wheels' spokes) and after glTF Transform's join step.
Try it yourself
- 1
Count one more way
In the first example, change
COUNTto5000. The first line prints 5000 draw calls and 400000 triangles, and the other three still print 1 draw call.BatchedMeshneeds no bigger buffers here: its vertex and index space is for the one geometry, not for each copy. - 2
Spend fewer instances
In the second example, change
[1000, 500, 0]to[1000, 250, 0]. The middle line prints1 draw calls, 3000 triangles:countis how many you draw, and the number given to the constructor is how many you may draw.
Code & diagrams
Seeded placement, so every run is the same. All four versions draw the same 80,000 triangles.
Console
Run the scene to see its console.Expected console output
1000 meshes 1000 draw calls 80000 triangles | geometries so far: 1
InstancedMesh 1 draw calls 80000 triangles | geometries so far: 1
merged geometry 1 draw calls 80000 triangles | geometries so far: 2
BatchedMesh 1 draw calls 80000 triangles | geometries so far: 3The first line is a real error from three.js, followed by what mergeGeometries returns.
Console
Run the scene to see its console.Expected console output
THREE.BufferGeometryUtils: .mergeGeometries() failed with geometry at index 1. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.
merge box + bare: null
merged, two materials 2 draw calls, 24 triangles
InstancedMesh, count 1000 1 draw calls, 12000 triangles
InstancedMesh, count 500 1 draw calls, 6000 triangles
InstancedMesh, count 0 0 draw calls, 0 triangles
moved instances, old bounding sphere 0 draw calls, 0 triangles
after computeBoundingSphere() 1 draw calls, 12000 trianglesBoth files are in /data/3d/models/. Phase 10 made them with glTF Transform.
Console
Run the scene to see its console.Expected console output
lumen-bike-draco.glb meshes 23 | InstancedMesh 2 | draw calls 25 | triangles 8168
lumen-bike-draco-joined.glb meshes 5 | InstancedMesh 2 | draw calls 7 | triangles 8136Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Instances that vanish
In the second example, delete the line instanced.computeBoundingSphere();.
Myth vs fact
Myth
Instancing makes the GPU draw faster.
Fact
The GPU still shades the same triangles and pixels. Instancing removes CPU work per object. If you are GPU-bound, it will not help.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
InstancedMeshalso supports per-instance colour (setColorAt) and, for picking,raycaster.intersectObject(mesh)returns aninstanceId. Phase 8's picking works on parts by name, so the bike's spokes are not separately pickable, which is what we want.
Remember this
- 1
The problem this solves: every draw call makes three.js and the browser set up state and talk to the GPU driver. That setup costs CPU time per call, and on a phone it adds up fast. A scene of 1000 small, identical objects (bolts, trees, bollards, spokes) can be CPU-bound with only 80,000 triangles.
- 2
**
InstancedMesh.** One geometry, one material, N copies. You give each copy a 4 x 4 matrix withsetMatrixAt(i, matrix)(and optionally a colour withsetColorAt). One draw call, whatever N is. Each matrix is 16 floats, 64 bytes, so 100,000 copies need 6.4 MB of matrices. Use it when many objects share both shape and material. The Lumen bike's 18 spokes per wheel have been instanced since Phase 7. - 3
Merged geometry.
mergeGeometries([...])fromBufferGeometryUtilscopies many geometries into one big buffer: one mesh, one call. It suits static things that never move separately. You lose individual transforms and picking. All geometries must have the same attributes, or it prints an error and returnsnull. WithuseGroups = trueit keeps one group per source, so two materials still mean two draw calls. - 4
**
BatchedMesh. Like an instanced mesh, but the copies may use different geometries** (still one material). You add each geometry withaddGeometry, make copies withaddInstance, and move them withsetMatrixAt. It can also hide copies (setVisibleAt), cull each copy against the camera, and sort for transparency. It costs more CPU thanInstancedMesh, so use it only when shapes differ. - 5
The numbers. The first example makes 1000 copies four ways: 1000 meshes give 1000 draw calls, and
InstancedMesh, merged geometry andBatchedMesheach give 1. The triangle count is identical: the GPU does the same shading work. Draw calls are a CPU saving. - 6
The "100 draw calls" rule. You will read that a phone should stay under about 100 draw calls. It is a community rule of thumb from three.js forums and talks, not a standard, and it moves with the phone and the shaders. Treat it as a reason to *measure*: find your own limit on your weakest device and write it into the budget (Topic 17.7).
Explain it without notes
When would you choose BatchedMesh over InstancedMesh, and what does it cost?
Why does a merged mesh draw the same number of triangles as 1000 meshes, but cost less?
Practice
A scene has 300 identical lamp posts and 300 trees from 3 different models, all with one material each. Name the draw-call count if you use plain meshes, and the best arrangement.
mergeGeometries returns null. Give two likely causes.
Done when you can
Make 1000 copies as plain meshes,
InstancedMesh, merged andBatchedMesh, and read the draw callsSay what each one gives up
Fix an instanced mesh that vanishes after you moved its instances
Say honestly where the "100 draw calls" rule comes from