MultiMeshを使用した最適化

大量のインスタンス(数千)で、常に処理する必要がある(および一定量の制御を保持する必要がある)場合、using servers directly が推奨される最適化です。

オブジェクトの数が数十万または数百万に達すると、これらのアプローチはいずれももはや効率的ではありません。それでも、要件によっては、もう1つの最適化が可能です。

MultiMesh

MultiMesh は、一度に最大数百万のオブジェクトを描画できる単一の描画プリミティブです。 GPUハードウェアを使用してこれを行うため、非常に効率的です(ただし、OpenGL ES 2.0では、ハードウェアサポートがないため効率が低下します)。

唯一の欠点は、個々のインスタンスに対して screen または frustum カリングができないことです。これは、MultiMesh全体の可視性に応じて、何百万ものオブジェクトが常にまたは決して描画されないことを意味します。それらにカスタムの可視性rectを提供することは可能ですが、常に all-or-none の可視性になります。

オブジェクトが十分に単純な場合(ほんの2、3の頂点)、ほとんどの最新のGPUはこのユースケースに最適化されているため、これは一般にそれほど問題ではありません。回避策は、ワールドのさまざまな地域に複数のマルチメッシュを作成することです。

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

別の方法はGDNativeとC++を使用することです。これは非常に効率的です(VisualServer.multimesh_set_as_bulk_array() 関数を使用して、線形メモリを使用してすべてのオブジェクトの状態全体を設定できます)。この方法では、配列を複数のスレッドで作成し、1回の呼び出しで設定することで、高いキャッシュ効率を実現できます。

最後に、すべてのMultiMeshインスタンスを表示する必要はありません。表示されるものの量は、MultiMesh.visible_instance_count プロパティで制御できます一般的なワークフローでは、使用するインスタンスの最大量を割り当て、現在必要なインスタンス数に応じて表示される量を変更します。

Multimeshの例

以下は、コードからMultiMeshを使用する例です。 GDScript以外の言語は、数百万のオブジェクトに対してより効率的かもしれませんが、数千に対してはGDScriptで十分です。

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(Basis.Identity, new Vector3(i * 20, 0, 0)));
        }
    }
}