【问题标题】:Rendering video in game engine with dynamic FPS使用动态 FPS 在游戏引擎中渲染视频
【发布时间】:2020-02-16 15:35:51
【问题描述】:

我正在尝试在我的游戏引擎中渲染预先导出的视频,但在尝试安排帧时偶然发现了问题。即,虽然视频以固定 FPS 导出,但引擎具有动态帧速率(可能高于或低于视频)。在尝试选择在当前引擎帧中渲染哪个视频帧时,我的算法不断遇到边缘情况,这会破坏视频的速度或视频帧的分布。

我现在知道,可能没有一种算法可以在所有情况下都完美运行,但有三种情况可能在运行时发生并且应该被算法覆盖:

  1. 引擎中的任意稳定帧速率,误差范围约为 5%。 (例如,如果平均帧时间为 33.333 毫秒,则所有帧持续时间都将在 31.6666 毫秒 - 35 毫秒范围内。)
  2. 任意半稳定帧速率,
  3. 稳定的帧率,可以在视频播放期间更改为不同的稳定帧率。

在这 3 种情况下,以下情况应该成立:

  1. 在任意时间段内观察时,视频呈现的速度与视频以其原始 FPS 呈现的速度相同。
  2. 当在任意时间段内观察时,引擎具有稳定的帧速率,帧分布应平衡。

考虑到这一切,我尝试了 2 种解决方案,即使采用了一些启发式方法也没有产生好的结果。

  1. 一个非常幼稚的解决方案,其中当前帧的增量时间乘以帧速率,结果被四舍五入,视频播放提前了那么多帧。虽然此解决方案在某些帧速率下完美运行,但在其他帧速率下速度差异太大。例如,如果视频为 60FPS,引擎渲染为 30FPS,那么引擎每帧都会将视频播放提前 2 帧,这是完美的。但是,即使在 ~39 FPS 的情况下,由于舍入效果,每个引擎帧中的播放仍然只会提前 2 帧,从而导致视频速度下降。
  2. 一种基于时间线的解决方案,我们在其中跟踪所有先前引擎帧的累积持续时间。将其与当前引擎帧的持续时间相加,然后将其四舍五入到视频帧持续时间的最接近的倍数,我们得到应该在引擎中渲染的近似视频帧。该解决方案完美地平衡了视频速度,但在许多边缘情况下,帧分布变得非常不平衡,从而导致视频卡顿。使用与之前相同的示例,视频为 60FPS,引擎为 30FPS,存在一种边缘情况,即持续时间约为 41.66666 毫秒的一帧将导致视频帧提前为 (1, 3, 1, 3, 1, 3....) 而不是 (2, 2, 2, 2, 2, 2...)。

我想我的问题是:已知算法和启发式算法是否存在问题,可以简化为这个问题?如果没有,是否有一个很好的启发式方法可以在我上面描述的 3 种情况下产生更好的结果?

【问题讨论】:

  • 我喜欢你的问题,但我没有资格回答它...只是考虑一下...我会考虑走捷径并将引擎帧率强制为与当我需要显示它时有问题的视频......如果您需要一直显示视频,这可能不会很有效。如果是全屏视频,我根本不会使用引擎。
  • @rfmodulator 不幸的是,正如我想象的那样,一旦用户将鼠标悬停在某些按钮上,视频就应该作为 UI 元素播放。至于引擎,我可能决定走那条路(在显示视频时强制保持恒定的 FPS),但我真的很想知道是否有一种很好的方法来解决这个问题而无需更改引擎。

标签: algorithm video time frame frame-rate


【解决方案1】:

我认为您的基于时间线的算法非常接近。我认为你只需要用数学方式定义你的欲望。

对于每个帧选择,我们可以考虑两种错误。让 w 为所选帧将显示的挂钟时间与根据视频帧速率应该显示的时间之间的差。令 d 为所选帧的放映时间与之前放映的帧之间的误差。

定义这些误差的相对权重AB,然后选择最小化Aw2 + Bd2

【讨论】:

  • 我无法从数学上定义问题和目标,您的回答对我帮助很大。但是,您对第二个错误的定义在应该间歇性跳过帧的边缘情况下仍然存在不平衡的空间。我已经更改了第二个定义,以包括所选帧的显示时间与所有其他先前显示的帧之间的差异中的错误。现在算法以适当的方式处理边缘情况,并且展开公式(如果我们不想单独权衡每个差异)使我不必为每帧存储增量时间。
【解决方案2】:

您可以采用第二种方法并应用一些简单的平滑,例如通过最后两个值的平均值提前你的帧,总是向下舍入并向前传播误差,即当总和是奇数并且你向下舍入得到一个整数时,你将下一个总和加 1 以平衡它。

所以 1, 3, 1, 3, 1, 3... 会产生 2, 2, 2, 2, 2... 而 2, 3, 2, 3, 2, 3 会产生 2, 3, 2、3、2...等

伪代码:

int prevValue = 0
int error = 0;
bool firstValue = true;
int getNextValue(int value)
{
    if(firstValue)
    {
        prevValue = value;
        firstValue = false;
    }
    int sum = value + prevValue + error;
    error = sum % 2; 
    prevValue = value;
    return sum / 2; // integer division
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-07
    • 1970-01-01
    相关资源
    最近更新 更多