使用MultiMeshes优化
对于大量的实例(成千上万), 需要不断处理(需要保留一定的控制), 建议优化 直接使用服务 .
当对象数量达到数十万或数百万时, 这些方法都不再有效. 尽管如此, 根据要求, 还有另一种可能的优化方法.
MultiMeshes
A MultiMesh 是一个单次绘制的基本单元, 可以一次性绘制多达数百万个对象. 它的效率非常高, 因为它使用GPU硬件来做这件事(不过在OpenGL ES 2.0中, 它的效率较低, 因为没有硬件支持).
唯一的缺点是, 对于单个实例, 不可能进行 屏幕 或 视锥 剔除. 这意味着, 根据整个MultiMesh的可见性, 数百万个对象将被 始终 或 不会 绘制. 可以为它们提供一个自定义的可见性矩形, 但它将始终是 全或无 的可见性.
如果对象足够简单(只有几个顶点), 这通常不是什么大问题, 因为大多数现代GPU都为这种用例进行了优化. 一个变通的方法是为世界的不同区域创建多个MultiMeshes.
也可以在顶点着色器中执行一些逻辑(使用 INSTANCE_ID
或 INSTANCE_CUSTOM
内置常量). 关于在MultiMesh中对数千个对象进行动画制作的例子, 请参见 Animating thousands of fish 教程. 可以通过纹理向着色器提供信息(有浮点 Image 格式是理想的格式).
另一种选择是使用GDNative和C++, 效率应该非常高(可以通过 VisualServer.multimesh_set_as_bulk_array() 函数来设置所有对象使用线性内存的全部状态). 这样一来, 可以用多个线程创建数组, 然后在一次调用中设置, 提供了很高的缓存效率.
最后, 并不是所有的MultiMesh实例都必须是可见的. 可以通过 MultiMesh.visible_instance_count 属性来控制可见的数量. 典型的工作流程是先分配最大数量的实例, 然后根据当前需要的数量改变可见的数量.
多重网格示例
这里是一个从代码中使用MultiMesh的例子.GDScript以外的其他语言对于数百万个对象来说可能更有效, 但对于几千个对象来说,GDScript应该是可以的.
GDScript
C#
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 Godot;
using System;
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)));
}
}
}