Reducing Draw Calls in Godot 4 with MultiMeshInstance3D
Learn how to use Godot 4's MultiMeshInstance3D to render thousands of identical objects in a single draw call, reducing CPU overhead and improving frame rates.
22 Sept 2025, 12:56 UTC

The Performance Wall of Individual Nodes
Adding a few hundred MeshInstance3D nodes to a scene is straightforward, but as you scale to thousands of identical objects—like a forest of grass, a field of asteroids, or a crowd of simple drones—you will hit a performance wall. This isn't usually because the GPU cannot handle the polygons, but because the CPU becomes overwhelmed by the number of draw calls (commands sent from the CPU to the GPU to render an object).
The solution is GPU Instancing. In Godot 4, the MultiMeshInstance3D node allows you to render thousands of copies of the same mesh and material in a single draw call, shifting the burden of positioning and coloring from individual nodes to a single data buffer on the GPU.
How MultiMesh Works
A MultiMeshInstance3D is a node that acts as a container. The actual data—the mesh to be repeated, the number of instances, and the specific transform (position, rotation, scale) for each instance—is stored in a MultiMesh resource.
Unlike standard nodes, instances within a MultiMesh do not have their own Node3D properties. They exist only as entries in an array. This removes the overhead of the scene tree for every single object, drastically reducing CPU usage during the rendering pipeline.
Implementing a Dynamic Instance Field
To use this, you first create a MultiMeshInstance3D, assign a MultiMesh resource to it, and define the Mesh (e.g., a BoxMesh or SphereMesh). To manipulate these instances via code, you must access the MultiMesh resource directly.
The following example demonstrates how to initialize a grid of 1,000 instances and randomly offset their positions. Run this script on a MultiMeshInstance3D node in Godot 4.x.
extends MultiMeshInstance3D
func _ready():
# 1. Configure the MultiMesh resource
# Ensure the MultiMesh resource is already created in the inspector
var mm = multimesh
# Set the total number of instances
mm.instance_count = 1000
# 2. Position each instance
for i in range(mm.instance_count):
# Create a transform for each instance
var position = Vector3(randf_range(-10, 10), 0, randf_range(-10, 10))
var basis = Basis().rotated(Vector3.UP, randf_range(0, TAU))
var xform = Transform3D(basis, position)
# Apply the transform to the specific instance index
mm.set_instance_transform(i, xform)
Verification and Diagnostics
To verify that instancing is working, open the Debugger tab and check the Monitors section. Compare the Objects > Draw Calls metric when using 1,000 individual MeshInstance3D nodes versus one MultiMeshInstance3D. You should see the draw call count drop from 1,000+ to nearly 1 for that specific group of objects.
Adding Variety with Custom Data
A common concern with instancing is that every object looks identical. You can solve this without creating new materials by using Custom Data. In the MultiMesh resource settings, you can enable custom data (usually 4 floats per instance).
By writing a simple Spatial Shader, you can access this data using the INSTANCE_CUSTOM built-in variable. For example, you can assign a random value to instance_set_custom_data(i, color_vector) in GDScript and use that vector to drive the ALBEDO color in your shader. This allows 1,000 uniquely colored objects to still be rendered in a single draw call.
Engineering Trade-offs and Limitations
While powerful, MultiMeshInstance3D is not a universal replacement for standard nodes. Consider these constraints:
- Culling Granularity: Godot performs frustum culling (not rendering things outside the camera view) on the
MultiMeshInstance3Dnode as a whole. If you have one MultiMesh covering your entire world, the GPU will process every instance even if only one is visible. Solution: Split your instances into smaller "chunks" (multiple MultiMesh nodes) to allow the engine to cull entire sections of the map. - Animation: MultiMesh does not support skeletal animation (bones). If your objects need unique animations, you cannot use this method.
- Material Uniqueness: All instances must share the same material. You cannot change the material of a single instance; you must use shaders and custom data to simulate variety.
- CPU Updates: While rendering is fast, calling
set_instance_transformthousands of times per frame in GDScript can still create a CPU bottleneck. For highly dynamic objects, consider using a Compute Shader or updating only a subset of instances per frame.
Summary Checklist
When deciding to move to MultiMesh, verify these three points:
- Are the meshes identical? (Yes → MultiMesh)
- Do they share a single material? (Yes → MultiMesh)
- Is the instance count high enough to cause draw call lag? (Yes → MultiMesh)
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.