Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
使用 MultiMesh 进行优化
对于大量(成千上万)需要不断处理(且需要保留一定控制)的实例,建议直接使用服务器进行优化。
当对象数量达到数十万或数百万时,这些方法都不再有效。不过,根据需求,还有一种优化方法可用。
MultiMesh
MultiMesh 是一个单次绘制的图元,可以一次性绘制多达数百万个对象。它的效率非常高,因为它使用 GPU 硬件来完成这项工作。
唯一的缺点是,无法对单个实例进行屏幕或视锥剔除。这意味着,根据整个 MultiMesh 的可见性,数百万个对象将全都或全都不被绘制。虽然可以为它们提供一个自定义的可见性矩形,但可见性始终是全有或全无的。
如果对象足够简单(只有几个顶点),这通常不是什么大问题,因为大多数现代 GPU 都针对这种用例进行了优化。一种变通方法是为场景的不同区域创建多个 MultiMesh。
也可以在顶点着色器中执行一些逻辑(使用 INSTANCE_ID 或 INSTANCE_CUSTOM 内置常量)。关于在 MultiMesh 中对数千个对象进行动画的示例,请参见动画化数以千计条鱼教程。可以通过纹理向着色器提供信息(有适合此用途的浮点 Image 格式)。
另一种选择是使用 GDExtension 和 C++,这应该非常高效(可以通过 RenderingServer.multimesh_set_buffer() 函数使用线性内存设置所有对象的全部状态)。这样,可以使用多个线程创建数组,然后在单次调用中设置,从而提供较高的缓存效率。
最后,并非所有的 MultiMesh 实例都必须是可见的。可见实例的数量可以通过 MultiMesh.visible_instance_count 属性来控制。典型的工作流程是先分配将要使用的最大实例数量,然后根据当前需要的数量更改可见数量。
MultiMesh 示例
这是一个从代码中使用 MultiMesh 的示例。对于数百万个对象,GDScript 以外的语言可能更高效,但对于几千个对象,GDScript 应该没问题。
extends MultiMeshInstance3D
func _ready():
# Create the multimesh.
multimesh = MultiMesh.new()
# Set the format first.
multimesh.transform_format = MultiMesh.TRANSFORM_3D
# Set the mesh that will be duplicated.
multimesh.mesh = BoxMesh.new()
# 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, Transform3D(Basis(), Vector3(i * 20, 0, 0)))
using Godot;
public partial class MyMultiMeshInstance3D : MultiMeshInstance3D
{
public override void _Ready()
{
// Create the multimesh.
Multimesh = new MultiMesh();
// Set the format first.
Multimesh.TransformFormat = MultiMesh.TransformFormatEnum.Transform3D;
// 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 Transform3D(Basis.Identity, new Vector3(i * 20, 0, 0)));
}
}
}