帧率独立加速/减速?

时间:2017-05-14 03:57:01

标签: c++ calculus

我正在用C ++编写粒子模拟器。

我通过在每个时间步将它们的速度加到它们的位置来移动粒子。

时间步长的值是当前帧的百分比。所以全帧时间步长为1,半帧时间步长为.5,四分之一帧时间步长为.25等。总模拟步骤为frameCount / timeStep ...所以时间步长越小,总数越大模拟的步骤。

保持跨时间步长的基本动作非常简单。等式是:

position = position + velocity * timeStep; //10 full frames later, end position is always the same

然而,一旦我试图改变速度,我对目前对数学的理解也太复杂了。例如,如果我这样做:

velocity = velocity * .95f;
position = position + velocity * timeStep; //10 full frames later, end position dependent on time step

不同时间步长的结果不再相同。我知道这是因为如果我通过减少时间步长来增加计算的步数,我也会将速度降低很多倍,这将对粒子的最终位置产生很大影响。 / p>

如何在不同时间段内获得相同结果的方式来修改速度?

3 个答案:

答案 0 :(得分:2)

速度是随时间变化的。您已经在等式中正确计算出来了。

position = position + velocity * timeStep;

加速度是速度随时间的变化。所以你只需使用相同的方程,但相应地修改变量。也就是说,将位置改变为速度,将速度改变为加速度。时间步长保持不变。

velocity = velocity + acceleration * timeStep;

如果你想模拟摩擦力,那么你所做的就是将速度乘以某个恒定的摩擦值和时间步长,然后从加速度中减去它。但此值应仅用于框架,而不应存储在实际加速度值中。

float temp_accel = acceleration - friction * velocity * timeStep;

然后根据temp_accel修改你的速度。

velocity = velocity + temp_accel * timeStep;

如果您的加速度为零,那么您可以将其从等式中取出:

float temp_accel = -friction * velocity * timeStep;

答案 1 :(得分:2)

I noticed users are finding this Q&A and being misled,所以我想在这里发布以指出接受的答案不正确,并且没有给出与帧速率无关的结果。

我们可以通过简单的电子表格计算来证明这一点。设置三个具有不同时间步长的时间序列,并使用该答案中的公式:

[Velocity Cell] = [Previous Velocity Cell]
                + (Accel - Frict * [Previous Velocity Cell] * [Time Step]) * [Time Step]

[Position Cell] = [Previous Position Cell] + [Velocity Cell] * [Time Step]

Spreadsheet showing divergent trajectories

这三组结果将在很短的时间内出现明显差异(在这里我看到了1.6秒的仿真时间)

这是预料之中的,因为乘以时间步仅正确地说明了 linear 的变化。它隐含地假设变化率在我们模拟的时间间隔内保持恒定,因此该时间间隔内的总变化仅是当前速度乘以时间间隔的持续时间。

一旦允许变化率本身发生变化(即,由于重力或摩擦等加速度导致速度发生变化),就违反了这一假设。在较慢/较长时间步长上运行的代码将假定速度在整个持续时间内只有一个值,而在较快/较短时间步长上运行的代码将在同一时间间隔内看到多个不同的值,并自然得出不同的结果。结果。

I explore this problem in more detail in this answer here,展示了如何得出解释模拟间隔期间变化率的方程,但这是TL; DR:

如果要以恒定加速度独立于帧速率进行集成,请使用:

position += velocity * timeStep + 0.5 * acceleration * timeStep * timeStep;
velocity += acceleration * timeStep;

如果您想通过乘法拖动与帧速率无关地进行集成,请使用:

newVelocity = velocity * pow(fractionRemainingAfterOneSecond, timeStep);

position += (newVelocity - velocity) / naturalLog(fractionRemainingAfterOneSecond);

velocity = newVelocity;

如果您想要同时具有加速度和阻力的独立于帧率的集成...您将无法获得(抱歉)。我们没有积分的封闭式解决方案

Nasty integral

即使我上面给出的两个与帧率无关的解决方案也只有在具有无限精度实数的情况下才是真实的。在实际的代码中,每一步都会产生舍入误差,并且以不同的时间步长运行相同的模拟会导致舍入误差在不同的位置堆积。但是它们将比公认的答案强得多。


为完整起见,下面的比较显示了使用上述独立于帧率的公式模拟的轨迹,分别以60 FPS和10 FPS步进。我还提供了一种根本不使用累积增量的分析解决方案,以表明具有不同时间步长的模拟不仅彼此一致,而且还与基本事实(最大舍入误差)一致。 >

Acceleration Graph

Braking Graph

答案 2 :(得分:0)

接受的答案很好,但我想强调的是,你永远不能完全独立于模拟的步长。

运动方程(在某个时间点给出我们的位置和速度)是积分。模拟通过在小步骤上进行求和来近似积分。但是因为这些步骤是有限的而不是无穷小的,所以总会有错误。您可以越小的步骤,就越能减少错误。

对于视频游戏和UI动画,帧速率顺序的时间步长(例如每秒10-100步)通常足以使误差保持足够小以至于它对动画的外观没有不利影响或游戏的可玩性。

但是如果你以每秒10步和100步的速度运行相同的模拟,你可以得到不同的结果,因为后者比前者更接近。

缩小步骤也会增加计算量。有时候,你没有足够的马力来使用足够小的步骤来控制错误。对于这些情况,存在数值积分器,其提供比重复总和更好的近似。可能最着名的数值积分器是Runge-Kutta (RK4)。如果您发现必须将timeStep设置得不切实际,请尝试使用RK4。