[Unity Mirror] Snapshot Interpolation(快照插值)

81 篇文章 30 订阅

英文原文:

https://gafferongames.com/post/snapshot_interpolation/

简介

嗨,我是格伦菲埃勒,欢迎来到网络化物理学(Networked Physics)。

在上一篇文章中,我们使用确定性LockStep联网了物理仿真。现在,在本文中,我们将通过完全不同的技术网络与完全不同的技术进行相同的模拟:快照插值。

背景介绍

  虽然确定性LockStep在带宽方面非常有效,但并不总是能够使你的模拟具有确定性。跨平台的浮点确定性是很难的。

  此外,随着玩家的数量增加,确定性LockStep变得有问题:您无法模拟帧N,直到您收到该帧的所有玩家的输入,所以玩家最终会等待最滞后的玩家。因此,我最多推荐了2-4名玩家的确定性LockStep。

  因此,如果您的模拟不是确定性的,或者您想要更高的玩家计数,那么您需要一个不同的技术。快照插值很好地适合这种情况。在许多方面,与确定性LockStep的极性相反:不是运行两个模拟,一个在左边,一个在右边,并使用完美的确定性和同步输入保持同步,快照插值根本不在右边运行任何模拟!

快照

  相反,我们从左边的模拟中捕捉所有相关状态的快照,并将其传送到右边,然后在右边我们使用这些快照来重建模拟的视觉近似,所有这些都不需要运行模拟本身。

  作为第一遍,让我们把渲染每个立方体所需的状态送过去:

    struct CubeState
    {
        bool interacting;
        vec3f position;
        quat4f orientation;
    };

  我相信你现在已经明白了,这种技术的代价是增加带宽的使用。大大增加了带宽的使用。请抓紧你们的脖子,因为快照包含了整个模拟的视觉状态。通过一点数学运算,我们可以看到每个立方体序列化为225比特或28.1字节。由于我们的模拟中有900个立方体,这意味着每个快照大约是25千字节。这可真够大的!

  在这一点上,我希望每个人都能放松,深呼吸,想象我们生活在这样一个世界里,我真的可以在互联网上以每秒60次的速度发送一个这么大的数据包,而不会出现一切爆炸。想象一下,我有FIOS(我有),或者我坐在骨干网的链接上,与另一台也在骨干网上的电脑相连。想象我住在韩国。做任何你需要做的事情,以暂停怀疑,但最重要的是,不要担心,因为我将用整个下一篇文章告诉你如何优化快照带宽。

  当我们在数据包中发送快照数据时,我们包含在顶部16位序列号。此序列号以零启动,并随着每个分组增加。我们使用此序列号接收以确定数据包中的快照是否比最近收到的快照更新。如果它更旧,那就扔掉了。

  每帧我们只渲染右侧收到的最新快照:

snapshot

  但仔细观察,即使我们尽可能快地发送数据(每帧一个数据包),你仍然可以看到右侧的缺口。这是因为互联网不能保证每秒钟发送60次的数据包以1/60秒的时间间隔到达。数据包是抖动的。有些帧你会收到两个快照数据包。其他帧你没有收到。

抖动和撞击

  当你第一次开始联网时,这实际上是一件非常普遍的事情。你开始在局域网上玩你的游戏,并注意到你可以非常快地发出数据包(60pps),而且大多数时候你的游戏看起来很好,因为在局域网上,这些数据包实际上倾向于以相同的速度到达,然后你开始尝试在无线或互联网上玩你的游戏,你开始看到故障。不要担心。有一些方法可以处理这个问题!

  首先,让我们来看看我们通过这种天真的方法发送了多少带宽。每个数据包是25312.5字节加上IP + UDP标题的28个字节,以及序列号的2个字节。这是每包的25342.5字节,每秒60个报文,这给出了每秒1520550个字节或11.6兆位/秒。现在有肯定有互联网连接可以支持那种交通量…但是自从,让我们说实话,我们并没有真正获得大量的爆破数据包,每秒60次,所有的抖动,让我们拉回来一点并每秒发送10个快照:

snapshot

  你可以看到上面的情况。右边的情况不是很好,但至少我们已经将带宽减少了6倍,达到了2兆/秒左右。我们肯定在朝着正确的方向前进。

线性插值

  现在说说快照的技巧。我们所做的不是立即渲染收到的快照数据,而是将快照在一个插值缓冲器中缓冲很短的时间。这个插值缓冲器在一段时间内保留快照,这样你不仅有你想渲染的快照,而且从统计学上讲,你也很可能有下一个快照。然后,当右边的时间向前移动时,我们在两个稍有延迟的快照的位置和方向之间进行插值,提供平滑运动的假象。实际上,我们已经用少量的附加延迟换取了平稳性。

  你可能会惊讶于用线性插值@10pps看起来有多好。

snapshot

  但仔细观察,你可以看到右侧的一些伪影。第一个是当玩家的立方体在空中盘旋时的微妙的位置抖动。这是你的大脑在位置插值的采样点检测到的一阶不连续。另一个神器发生在一群立方体在katamari球中时,你可以看到一种 “脉冲”,随着旋转速度的增加和减少。出现这种情况是因为附加的立方体在围绕玩家立方体旋转的两个样本点之间进行了线性插值,当它们在一个圆上的两个点之间采取最短的线性路径时,有效地插值通过玩家立方体。

Hermite 插值

  我发现这些伪影是不可接受的,但我不想提高数据包发送速率来修复它们。让我们看看我们可以做些什么来让它在相同的发送速率下看起来更好。我们可以尝试的一件事是升级到更精确的位置插值方案,一种在位置样本之间进行插值的方案,同时考虑每个样本点的线性速度。

  这可以通过 Hermite 样条(发音为“air-mitt”)来完成

  与其他具有间接影响曲线的控制点的样条曲线不同,Hermite 样条曲线保证通过起点和终点,同时匹配起点和终点速度。这意味着速度在采样点上是平滑的,并且katamari 球中的立方体倾向于围绕立方体旋转,而不是以速度通过它进行插值。

snapshot

  在上面你可以看到位置@10pps 的 Hermite 插值。带宽略有增加,因为我们需要在快照中包含每个立方体的线速度,但我们能够在相同的发送速率下显着提高质量。我再也看不到任何伪影。回去把这个和原始的、非插值的10pps版本进行比较。我们能够在如此低的发送率下以这种质量水平重建模拟,这真的很令人惊讶。

  作为一个旁观者,我发现没有必要对方向四元数进行高阶插值以获得平滑插值。这很好,因为我做了很多研究,在采样点上用指定的角速度在方向四元数之间精确插值,这似乎很困难。要达到一个可接受的结果,只需要从线性插值+归一化(nlerp)切换到球形线性插值(slerp),以确保方向插值的角速度不变。

  我相信这是因为模拟中的立方体在空中的角速度大多是恒定的,而大的角速度变化只在碰撞发生时不连续发生。这也可能是因为方向在空中时往往变化缓慢,而位置则相对于屏幕上受影响的像素数量变化迅速。无论怎样,似乎slerp已经足够好了,这很好,因为它意味着我们不需要在快照中发送角速度。

处理真实世界条件

  现在我们必须处理丢包问题。在上一篇文章中讨论了 UDP 与 TCP 之后,我相信您会明白为什么我们永远不会考虑通过 TCP 发送快照。

  快照对时间至关重要,但与确定性锁步快照中的输入不同,它不需要可靠。如果快照丢失,我们可以跳过它并在插值缓冲区中插入更新的快照。我们永远不想停下来等待丢失的快照数据包被重新发送。这就是为什么您应该始终使用 UDP 发送快照的原因。

  我会让你知道一个秘密。上面的线性和 Hermite 插值视频不仅以每秒 10 个数据包的发送速率录制,而且还以 5% 的丢包率和 +/- 2 帧抖动 @ 60fps 录制。我如何处理这些视频的数据包丢失和抖动是通过简单地确保快照在插值缓冲区中保留适当的时间,然后再进行插值。

  我的经验法则是插值缓冲区应该有足够的延迟,这样我就可以连续丢失两个数据包,并且仍然有一些东西可以插值。通过实验,我发现在 2-5% 的丢包率下效果最佳的延迟量是数据包发送速率的 3 倍。在每秒 10 个数据包的情况下,这是 300 毫秒。我还需要一些额外的延迟来处理抖动,根据我的经验,抖动通常只有一到两帧 @ 60fps,所以上面的插值视频是以 350 毫秒的延迟录制的。

  增加350毫秒的延迟似乎是个大问题。而且确实如此。但是,如果你试图吝啬,你最终会在每次数据包丢失时搭上1/10秒的时间。在其他领域(如FPS、飞行模拟器、赛车游戏等),人们经常使用的一种技术是使用外推法来隐藏插值缓冲器所增加的延迟。但是根据我的经验,外推法对刚体的效果并不好,因为它们的运动是非线性的,而且是不可预测的。在这里,你可以看到200ms的外推法,将整体延迟从350ms减少到只有150ms。

snapshot

  问题是它不是很好。原因是外推对物理模拟一无所知。外推不知道与地板的碰撞,因此立方体向下外推穿过地板,然后弹回正确。预测不知道将玩家立方体保持在空中的弹簧力,因此立方体最初向上移动的速度比它应该的要慢,并且必须捕捉才能赶上。它也不知道任何关于碰撞以及碰撞响应如何工作的知识,因此在地板上滚动的立方体和其他立方体也会被错误预测。最后,如果您观察 katamari 球,您会看到外推法预测连接的立方体在应该与玩家立方体一起旋转时继续沿其切线速度移动。

总结

  可以想象,您可以花费大量时间来提高这种推断的质量并使其了解立方体的各种运动模式。你可以拿走每个立方体,并确保至少立方体不会穿过地板。您可以使用立方体之间的边界球添加一些近似的碰撞检测或响应。你甚至可以把片球中的立方体拿来让它们预测运动,然后随着玩家立方体旋转。

  但是,即使你做了这一切,仍然会有错误的预测,因为你根本无法准确地用近似值来匹配物理模拟。如果你的模拟主要是线性运动,例如快速移动的飞机、船、太空船–你可能会发现,简单的外推法在短时间内(50-250ms左右)效果很好,但根据我的经验,一旦物体开始与其他非静止物体碰撞,外推法就开始失效了。

  我们怎样才能减少插值所增加的延迟量?350毫秒似乎仍然是不可接受的,我们不能用外推法来减少这个延迟而不增加很多的不准确度。解决办法很简单:提高发送速率 如果我们每秒钟发送30个快照,我们可以在150ms的延迟下获得相同数量的丢包保护。每秒60个数据包只需要85ms。

  为了提高发送率,我们将需要一些相当好的带宽优化。但是不要担心,我们可以做很多事情来优化带宽。以至于有太多的东西要放在这篇文章里,我不得不插入一个额外的计划外的文章,只是为了涵盖所有的内容!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值