Optimization using MultiMeshes
When the amount of objects reach the hundreds of thousands or millions, none of these approaches are efficient anymore. Still, depending on the requirements, there is one more optimization possible.
A is a single draw primitive that can draw up to millions of objects in one go. It’s extremely efficient because it uses the GPU hardware to do this (in OpenGL ES 2.0, it’s less efficient because there is no hardware support for it, though).
If the objects are simple enough (just a couple of vertices), this is generally not much of a problem as most modern GPUs are optimized for this use case. A workaround is to create several MultiMeshes for different areas of the world.
It is also possible to execute some logic inside the vertex shader (using the or INSTANCE_CUSTOM
built-in constants). For an example of animating thousands of objects in a MultiMesh, see the Animating thousands of fish tutorial. Information to the shader can be provided via textures (there are floating-point formats which are ideal for this).
Finally, it’s not required to have all MultiMesh instances visible. The amount of visible ones can be controlled with the MultiMesh.visible_instance_count property. The typical workflow is to allocate the maximum amount of instances that will be used, then change the amount visible depending on how many are currently needed.
Multimesh example
Here is an example of using a MultiMesh from code. Languages other than GDScript may be more efficient for millions of objects, but for a few thousands, GDScript should be fine.
using Godot;
using System;
public class YourClassName : MultiMeshInstance
{
public override void _Ready()
Multimesh = new MultiMesh();
// Set the format first.
Multimesh.TransformFormat = MultiMesh.TransformFormatEnum.Transform3d;
Multimesh.ColorFormat = MultiMesh.ColorFormatEnum.None;
Multimesh.CustomDataFormat = MultiMesh.CustomDataFormatEnum.None;
// Then resize (otherwise, changing the format is not allowed)
Multimesh.InstanceCount = 1000;
// Set the transform of the instances.
for (int i = 0; i < Multimesh.VisibleInstanceCount; i++)
{
Multimesh.SetInstanceTransform(i, new Transform(Basis.Identity, new Vector3(i * 20, 0, 0)));
}
}