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.

大世界坐标

备注

大世界坐标主要用于 3D 项目;2D 项目很少会用到。此外,启用大世界坐标后,2D 渲染目前无法从精度的增加中获益,但 3D 渲染可以。

为什么要使用大世界坐标?

在 Godot 中,物理仿真和渲染都依赖于浮点数。然而,计算机中浮点数的精度和范围是有限的,可能在太空、星球尺度的仿真游戏等拥有庞大世界的游戏中产生问题。

浮点数的精度在 0.0 附近是最高的。随着取值离 0.0 越来越远,精度就会变得越来越低。每次浮点数的指数变大时,精度就会降低,也就是浮点数取值越过 2 的幂(2、4、8、16……)的时候。此时浮点数的最小步长就会增大,精度因此变低。

在实践中,这意味着玩家远离世界原点(2D 游戏的 Vector2(0, 0) 和 3D 游戏的 Vector3(0, 0, 0)),精度就会下降。

精度的丢失可能会导致远离世界原点的对象看上去在“抖动”,因为模型的位置会吸附到最接近的浮点数能够表示的值。这种情况下,如果玩家远离世界原点,还可能导致物理方面的问题。

范围决定的是所能够存储的最小和最大值。如果玩家尝试移出这个范围就会直接失败。但是实际情况下,在能够受到范围影响之前几乎都会遇到浮点数精度问题。

范围和精度(两个指数间隔的最小步长)取决于浮点数的类型。单精度浮点数的理论范围支持存储极高的值,单精度很低。实践中,无法表示所有整数值的浮点数类型并不是很有用。极值附近的精度会变得非常低,低到连两个整数值也无法区分。

以下是浮点数能够表示整数值的范围:

  • 单精度浮点数范围(表示所有整数):-16,777,216 和 16,777,216 之间

  • 双精度浮点数范围(表示所有整数):-9 千万亿和 9 千万亿之间

范围

单精度步长

双精度步长

注释

[1; 2]

~0.0000001

~1e-15

0.0 附近精度会变大(本表省略)。

[2; 4]

~0.0000002

~1e-15

[4; 8]

~0.0000005

~1e-15

[8; 16]

~0.000001

~1e-14

[16; 32]

~0.000002

~1e-14

[32; 64]

~0.000004

~1e-14

[64; 128]

~0.000008

~1e-13

[128; 256]

~0.000015

~1e-13

[256; 512]

~0.00003

~1e-13

[512; 1024]

~0.00006

~1e-12

[1024; 2048]

~0.0001

~1e-12

[2048; 4096]

~0.0002

~1e-12

第一人称 3D 游戏的最大推荐单精度范围,不会有渲染和物理方面的问题。

[4096; 8192]

~0.0005

~1e-12

第三人称 3D 游戏的最大推荐单精度范围,不会有渲染和物理方面的问题。

[8192; 16384]

~0.001

~1e-12

[16384; 32768]

~0.0019

~1e-11

俯视角 3D 游戏的最大推荐单精度范围,不会有渲染和物理方面的问题。

[32768; 65536]

~0.0039

~1e-11

所有 3D 游戏的最大推荐单精度范围。双精度(大世界坐标)通常会超过这个点。

[65536; 131072]

~0.0078

~1e-11

[131072; 262144]

~0.0156

~1e-10

> 262144

> ~0.0313

~1e-10(0.0000000001)

超过这个值之后,双精度仍然比单精度要精确地多。

使用单精度浮点数时,可以超过建议的范围,但此时就会更多可见的渲染问题,物理问题也会变得更常见(例如玩家在某些方向上无法直线移动)。

参见

详见 Demystifying Floating Point Precision 一文。

大世界坐标的工作原理

大世界坐标(也叫双精度物理)能够增加引擎中所有浮点数计算的精度级别。

在 GDScript 中,float 默认为 64 位,但 Vector2, Vector3Vector4 为 32 位。这意味着向量类型的精度受到很大限制。为了解决这个问题,我们可以增加向量类型中用于表示浮点数的位数。这样一来,精度就会呈*指数*增长,这意味着最终值的精度不仅提高了一倍,而且在数值较高时,精度可能会提高数千倍。从单精度浮点数到双精度浮点数,可表示的最大值也大大增加。

为了避免远离世界原点时出现模型吸附(model snapping)问题,Godot 的 3D 渲染引擎将在启用大世界坐标时提高渲染的精度。出于性能原因,着色器不使用双精度浮点数,但会使用 替代解决方案 来模拟双精度,以便使用单精度浮点数进行渲染。

备注

只有确实需要大世界坐标时才启用它,因为启用大世界坐标会对性能和内存占用带来负面影响,这种负面影响在32位CPU上更加明显。

此功能专为中端/高端桌面平台量身定制。大世界坐标在低端移动设备上可能表现不佳,除非你采取措施通过其他方式来减少 CPU 使用率(例如减少每秒的物理循环 physics tick)。

在低端平台上,可以使用原点移位方法来实现大型世界,而无需使用双精度物理和渲染。原点移位适用于单精度浮点数,但它会给游戏逻辑带来更多复杂性,尤其是在多人游戏中。因此,本页不会详细介绍原点移位。

大世界坐标的目标群体是谁?

大世界坐标的典型使用场景是3D太空类型或者星球规模的模拟游戏。延伸来讲,如果一些游戏需要在支持*非常*快速的运动的同时还要支持非常慢并且精确的运动,那可能需要使用大世界坐标。

从另一方面来看,只有在确实需要大世界坐标才启用它是至关重要的(从性能方面考虑)。以下情况通常**不**适合使用大世界坐标:

  • 2D 游戏,因为 2D 游戏中的精度问题通常不太明显。

  • 那些世界尺寸是小型或者中型的游戏。

  • 游戏的世界很大,但分为不同的级别,通过加载序列来实现场景的切换和加载。你可以将每个级别的内容存放在其世界原点的周围,这样就可以在不产生性能损耗的情况下避免精度问题。

  • 可步行区域 不超过8192×8192米(以世界原点为中心)的开放世界游戏。如上表所示,即使是第一人称游戏,精度水平在该范围内也可以接受。

如果对是否使用大世界坐标存在疑虑 ,那你可能并不需要在你的项目中使用大世界坐标。实际上,大多数现代的3A开放世界主题的游戏并没有使用大世界坐标系统,这些游戏仍然依靠单精度浮点类型来处理游戏中的渲染和物理。

启用大世界坐标

此过程需要重新编译编辑器以及你打算使用的所有导出模板的二进制文件。如果只打算在发布模式下导出项目,则可以跳过调试导出模板的编译。无论如何,你都需要编译一个编辑器构建版,这样就可以可以测试你的精确大世界,而不必每次都导出项目。

请参阅 编译 部分来了解每个目标平台的编译指令。编译编辑器和导出模板时,需要添加 precision=double SCons 选项。

生成的二进制文件将以 .double 后缀命名,以将其与单精度二进制文件(缺少任何精度后缀)区分开来。你可以随后在“导出”对话框的项目导出预设中将二进制文件指定为自定义导出模板。

单双精度构建之间的的兼容性

当使用 ResourceSaver 单例保存 可执行文件 资源时,如果该资源是使用双精度数字的构建方式保存的,则会在文件中存储一个特殊标志。因此,当你切换到双精度构建并保存资源时,磁盘上的所有可执行文件资源都将发生变化。

单精度和双精度构建都支持在使用此特殊标志的资源上使用 ResourceLoader 单例。这意味着单精度构建可以加载使用双精度构建的资源,反之亦然。基于文本的资源不存储双精度标志,因为它们不需要这种标志来正确读取。

已知的不兼容

  • 在网络多人游戏中,服务器和所有客户端都应使用相同的构建类型,以确保客户端之间的精度保持一致。使用不同的构建类型 可能 也有效,但也可能会出现各种问题。

  • 在双精度版本中,GDExtension API 以不兼容的方式发生变化。这意味着扩展 必须 重新构建,才能使用双精度版本。在扩展开发者端,当使用 precision=double 构建 GDExtension 时,会启用 REAL_T_IS_DOUBLE 定义。在单精度构建中,real_t 可用作 float 的别名,在双精度构建中,可用作 double 的别名。

限制

由于 3D 渲染着色器实际上并不使用双精度浮点,因此在 3D 渲染精度方面存在一些限制:

  • 使用 skip_vertex_transformworld_vertex_coords 的着色器不会从精度提高中受益。

  • 三平面映射 不会从精度提高中受益。当远离世界原点时,使用三平面映射的材质将表现出可见的抖动。

  • In double-precision builds, world space coordinates in a shader fragment() function can't be reconstructed from view space, for example:

    vec3 world = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
    

    Instead, calculate the world space coordinates in the vertex() function and pass them using a varying, for example:

    varying vec3 world;
    void vertex() {
        world = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    }
    

当启用大世界坐标时,目前 2D 渲染还无法从提高精度中受益。这可能会导致在远离世界原点(从典型缩放级别的几百万像素开始)时发生可见的模型吸附(model snapping)。不过,二维物理计算仍将从提高精度中受益。