Optimización usando MultiMeshes

Para un gran número de instancias (en miles) que deben ser procesadas constantemente (y se necesita cierto grado de control), se recomienda la optimización de "usar servidores directamente". Esto implica utilizar servidores de manera directa y eficiente para gestionar y procesar las instancias, lo cual puede ofrecer un rendimiento mejorado y mayor control sobre el procesamiento. Para obtener más información sobre cómo utilizar servidores directamente, puedes consultar la documentación en el enlace "usar servidores directamente".

Cuando la cantidad de objetos alcanza cientos de miles o millones, ninguno de estos enfoques es eficiente. Sin embargo, dependiendo de los requisitos, existe una optimización adicional posible.

MultiMeshes

Un MultiMesh es una primitiva de dibujo única que puede dibujar hasta millones de objetos de una vez. Es extremadamente eficiente porque utiliza el hardware de la GPU para hacer esto (en OpenGL ES 2.0, es menos eficiente porque no hay soporte de hardware para ello).

La única desventaja es que no es posible realizar un culling de pantalla o de frustum para instancias individuales. Esto significa que millones de objetos siempre se dibujarán o nunca se dibujarán, dependiendo de la visibilidad de todo el MultiMesh. Es posible proporcionar un rectángulo de visibilidad personalizado para ellos, pero siempre será una visibilidad todo o nada.

Si los objetos son lo suficientemente simples (solo un par de vértices), esto generalmente no es un gran problema, ya que la mayoría de las GPUs modernas están optimizadas para este caso de uso. Una solución alternativa es crear varios MultiMeshes para diferentes áreas del mundo.

También es posible ejecutar cierta lógica dentro del vertex shader utilizando las constantes integradas "INSTANCE_ID" o "INSTANCE_CUSTOM". Por ejemplo, para animar miles de objetos en un MultiMesh, puedes consultar el tutorial "Animando miles de peces". La información para el shader puede ser proporcionada a través de texturas (hay formatos de imágenes de punto flotante, Image, que son ideales para esto).

Otra alternativa es utilizar GDNative y C++, lo cual debería ser extremadamente eficiente. Es posible establecer el estado completo para todos los objetos utilizando memoria lineal a través de la función VisualServer.multimesh_set_as_bulk_array(). De esta manera, el array puede ser creado con múltiples hilos y luego establecido en una sola llamada, lo que proporciona una alta eficiencia de caché.

Finalmente, no es necesario que todas las instancias de MultiMesh sean visibles. La cantidad de instancias visibles se puede controlar con la propiedad MultiMesh.visible_instance_count. El flujo de trabajo típico consiste en asignar la cantidad máxima de instancias que se utilizarán y luego cambiar la cantidad visible según la cantidad necesaria en ese momento.

Ejemplo de Multimesh

Aquí tienes un ejemplo de cómo usar un MultiMesh desde el código. Otros lenguajes además de GDScript pueden ser más eficientes para millones de objetos, pero para unos pocos miles, GDScript debería ser suficiente.

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