Optimization using MultiMeshes

对于大量的实例(成千上万),需要不断处理(需要保留一定的控制),建议优化 直接使用服务

当对象数量达到数十万或数百万时,这些方法都不再有效。尽管如此,根据要求,还有另一种可能的优化方法。

MultiMeshes

A :ref:`MultiMesh<class_MultiMesh>`是一个单次绘制的基本单元,可以一次性绘制多达数百万个对象。它的效率非常高,因为它使用GPU硬件来做这件事(不过在OpenGL ES 2.0中,它的效率较低,因为没有硬件支持)。

唯一的缺点是,对于单个实例,不可能进行 屏幕视锥 剔除。这意味着,根据整个MultiMesh的可见性,数百万个对象将被 始终不会 绘制。可以为它们提供一个自定义的可见性矩形,但它将始终是 全或无 的可见性。

如果对象足够简单(只有几个顶点),这通常不是什么大问题,因为大多数现代GPU都为这种用例进行了优化。一个变通的方法是为世界的不同区域创建多个MultiMeshes。

也可以在顶点着色器中执行一些逻辑(使用 INSTANCE_IDINSTANCE_CUSTOM 内置常量)。关于在MultiMesh中对数千个对象进行动画制作的例子,请参见 Animating thousands of fish 教程。可以通过纹理向着色器提供信息(有浮点 Image 格式是理想的格式)。

另一种选择是使用GDNative和C++,效率应该非常高(可以通过 :ref:`VisualServer.multimesh_set_as_bulk_array() <class_VisualServer_method_multimesh_set_as_bulk_array>`函数来设置所有对象使用线性内存的全部状态)。这样一来,可以用多个线程创建数组,然后在一次调用中设置,提供了很高的缓存效率。

最后,并不是所有的MultiMesh实例都必须是可见的。可以通过 MultiMesh.visible_instance_count 属性来控制可见的数量。典型的工作流程是先分配最大数量的实例,然后根据当前需要的数量改变可见的数量。

Multimesh example

这里是一个从代码中使用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)))
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)));
        }
    }
}