Optimierungen durch MultiMeshes

Für eine große Anzahl von Instanzen (Tausende) die ständig verarbeitet werden müssen (und ein gewisses Maß an Kontrolle muss beibehalten werden), wird Folgendes empfohlen Server direkt verwenden.

Wenn die Anzahl der Objekte Hunderttausende oder Millionen erreicht, ist keiner dieser Ansätze mehr effizient. Abhängig von den Anforderungen ist jedoch noch eine weitere Optimierung möglich.

MultiMeshes

Ein MultiMesh ist ein einzelnes Zeichnungselement, das bis zu Millionen von Objekten auf einmal zeichnen kann. Es ist äußerst effizient, da hierfür die GPU-Hardware verwendet wird (in OpenGL ES 2.0 ist es weniger effizient, da es jedoch keine Hardwareunterstützung gibt).

Der einzige Nachteil ist, dass für einzelne Instanzen kein Bildschirm oder Kegelstumpf wegschneiden möglich ist. Dies bedeutet, dass Millionen von Objekten immer oder nie gezeichnet werden, abhängig von der Sichtbarkeit des gesamten MultiMesh. Es ist möglich eine benutzerdefinierte Sichtbarkeitsrahmen für sie bereitzustellen, aber es wird immer eine alles oder nichts Sichtbarkeit sein.

Wenn die Objekte einfach genug sind (nur ein paar Eckpunkte) ist dies im Allgemeinen kein großes Problem, da die meisten modernen GPUs für diesen Anwendungsfall optimiert sind. Eine Umgehung des Problems besteht darin, mehrere MultiMeshes für verschiedene Regionen der Welt zu erstellen.

Es ist auch möglich eine Logik innerhalb des Vertex-Shaders auszuführen (unter Verwendung der integrierten Konstanten INSTANCE_ID oder INSTANCE_CUSTOM). Ein Beispiel für das Animieren von Tausenden von Objekten in einem MultiMesh finden Sie in der Anleitung Animieren von Tausenden von Fischen. Informationen für den Shader können über Texturen bereitgestellt werden (es gibt Gleitkomma Bild-Formate die dafür ideal sind).

Eine andere Alternative ist die Verwendung von GDNative und C++, die äußerst effizient sein sollten (es ist möglich den gesamten Status für alle Objekte mithilfe des linearen Speichers über die Funktion VisualServer.multimesh_set_as_bulk_array() festzulegen). Auf diese Weise kann das Array mit mehreren Threads erstellt und dann in einem Aufruf festgelegt werden, wodurch eine hohe Cache-Effizienz erzielt wird.

Schließlich müssen nicht alle MultiMesh-Instanzen sichtbar sein. Die Anzahl der sichtbaren kann mit der Eigenschaft MultiMesh.visible_instance_count gesteuert werden. Der typische Ablauf besteht darin, die maximale Anzahl der verwendeten Instanzen zuzuweisen und dann die sichtbare Anzahl zu ändern, je nachdem wie viele derzeit benötigt werden.

Ein Beispiel von MultiMeshes

Hier ist ein Beispiel für die Verwendung eines MultiMesh aus Code heraus. Andere Sprachen als GDScript sind möglicherweise für Millionen von Objekten effizienter, aber für einige Tausend sollte GDScript in Ordnung sein.

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)))
public class YourClassName : MultiMeshInstance
{
    public override void _Ready()
    {
        // Create the multimesh.
        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(Basis.Identity, new Vector3(i * 20, 0, 0)));
        }
    }
}