Optimisations en utilisant des MultiMeshes

Pour un grand nombre d’instances (en milliers), qui doivent être traitées en permanence (et qui nécessitent un certain contrôle), utiliser directement les serveurs est l’optimisation recommandée.

Quand la quantité d’objets atteint des centaines de milliers ou même des millions, aucune de ces approches ne reste efficace. Il reste cependant, en fonction des besoins, une optimisation supplémentaire possible.

MultiMeshes

A MultiMesh 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).

The only drawback is that there is no screen or frustum culling possible for individual instances. This means, that millions of objects will be always or never drawn, depending on the visibility of the whole MultiMesh. It is possible to provide a custom visibility rect for them, but it will always be all-or-none visibility.

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 INSTANCE_ID 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 Image formats which are ideal for this).

Another alternative is to use GDNative and C++, which should be extremely efficient (it’s possible to set the entire state for all objects using linear memory via the VisualServer.multimesh_set_as_bulk_array() function). This way, the array can be created with multiple threads, then set in one call, providing high cache efficiency.

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.

Exemple Multimesh

Voici un exemple d’utilisation d’un MultiMesh depuis du code. Les langages autres que GDScript peuvent être plus efficaces pour des millions d’objets, mais pour quelques milliers, GDScript devrait convenir.

extends MultiMeshInstance

func _ready():
    # Create the multimesh.
    multimesh = MultiMesh.new()
    # Set the format first.
    multimesh.transform_format = MultiMesh.TRANSFORM_3D
    multimesh.color_format = MultiMesh.COLOR_NONE
    multimesh.custom_data_format = MultiMesh.CUSTOM_DATA_NONE
    # Then resize (otherwise, changing the format is not allowed).
    multimesh.instance_count = 10000
    # Maybe not all of them should be visible at first.
    multimesh.visible_instance_count = 1000
    # Set the transform of the instances.
    for i in multimesh.visible_instance_count:
        multimesh.set_instance_transform(i, Transform(Basis(), Vector3(i * 20, 0, 0)))
using System;

public class MultiMeshInstance : Godot.MultiMeshInstance
{
    public override void _Ready()
    {
        // Create the multimesh.
        var 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;
        // Maybe not all of them should be visible at first.
        multimesh.VisibleInstanceCount = 1000;
        // Set the transform of the instances.
        for (int i = 0; i < multimesh.VisibleInstanceCount; i++)
        {
            multimesh.SetInstanceTransform(i, new Transform(new Basis(), new Vector3(i * 20, 0, 0)));
        }
    }
}