在OpenGL中设置每秒最大帧数

12

有没有一种方法可以计算需要进行多少次更新才能达到所需的帧率,而不是针对特定的系统?我找到了 Windows 的解决方案,但我想知道在 OpenGL 中是否存在这样的东西。应该是一种计时器。

或者还有什么其他方法可以防止 FPS 大幅下降或上升?这次我正在测试绘制大量线条顶点,并使用 Fraps 可以看到帧率从 400 FPS 下降到 200 FPS,绘制明显变慢。

7个回答

13

你有两种不同的方式来解决这个问题:

  1. 假设你有一个名为maximum_fps的变量,其中包含你想要显示的最大帧数。

    然后你测量最后一帧花费的时间(用计时器即可)

    现在假设你说你想在你的应用程序中最多有60FPS。那么你希望所测量的时间不低于1/60。如果测量的时间较低,则调用sleep()来达到帧留下的时间。

  2. 或者你可以有一个名为tick的变量,它包含应用程序的当前“游戏时间”。使用相同的计时器,你将在应用程序的每个主循环中递增它。然后,在你的绘图例程中,你根据tick变量计算位置,因为它包含应用程序的当前时间。

    选项2的重大优点是,你的应用程序将更容易进行调试,因为你可以玩弄tick变量,随时向前和向后移动时间。这是一个巨大的优势。


9
第一个只是用于减慢FPS速度,依赖于睡眠间隔的准确性是危险的。 - young
在一些简单的情况下(包括我的当前情况),第一个解决方案已经足够好了,因为我只需要在运行应用程序时合并FPS,知道我的帧率从未低于200,就足够高效。因此,也许是一个危险的但可以在不进行太多更改的情况下轻松进入您的代码,这与第二个解决方案一样好。 - Raven
那么,您如何知道上一帧已经完成并且可以开始测量延迟时间呢? - Allahjane
@Allahjane 像往常一样:timer = time(); render_frame(); time_spent = time() - timer; - Gustavo Muenz
实际上我指的是OpenGL调用,它告诉你帧绘制已完成。 - Allahjane

10

规则1:不要让update()或loop()等函数依赖于被调用的频率。

你无法真正得到你想要的FPS。你可以尝试通过跳过一些昂贵的操作来提高它,或者通过调用sleep()等函数来减慢它。然而,即使使用这些技术,FPS也几乎总是与你想要的精确FPS不同。

解决这个问题的常见方法是使用从上次更新开始经过的时间。例如:

// Bad
void enemy::update()
{
  position.x += 10; // this enemy moving speed is totally up to FPS and you can't control it.
}

// Good
void enemy::update(elapsedTime)
{
  position.x += speedX * elapsedTime; // Now, you can control its speedX and it doesn't matter how often it gets called.
}

基本上你说的和爱迪生一样,但这段代码演示让每个人都清楚了。谢谢。 - Raven
不客气~ :) 是的,我的代码和 Edison 的第二个答案一样。只是想指出第一个代码的缺点。 - young

5

有没有办法计算需要进行多少次更新才能达到所需的帧率,而不是系统特定的?

没有。

无法精确计算需要调用多少次更新才能达到所需的帧率。

但是,您可以测量自上一帧以来经过了多长时间,根据它计算当前帧速率,将其与所需帧速率进行比较,然后引入一些睡眠来将当前帧速率降低到所需值。这不是一个精确的解决方案,但它会起作用。

我在Windows中找到了这个方法,但我想知道OpenGL本身是否存在类似的东西。应该是某种计时器。

OpenGL只关心渲染内容,与计时器无关。此外,使用Windows计时器并不是一个好主意。使用QueryPerformanceCounter、GetTickCount或SDL_GetTicks来测量经过了多长时间,并使用睡眠来达到所需的帧率。

还有什么其他方法可以防止FPS大幅度下降或上升吗?

通过睡眠来防止FPS上升。

至于防止FPS下降...

这是一个非常广泛的话题。让我们看看。大致上是这样的:使用顶点缓冲对象或显示列表,应用程序档案,不要使用过大的纹理,不要使用过多的透明度混合,避免“原始”的OpenGL(glVertex3f),不要渲染不可见的对象(即使没有绘制任何多边形,处理它们也需要时间),考虑学习BSP或OCTrees来呈现复杂场景,在参数曲面和曲线中,不要不必要地使用太多基元(如果您将使用一百万个多边形渲染圆圈,没有人会注意到差异),禁用垂直同步。简而言之-将渲染调用数量、渲染多边形数量、渲染像素数量、读取纹理像素数量减少到绝对可能的最小值,阅读来自NVidia的所有可用性能文档,您应该可以获得性能提升。

我发现即使是OGL也有自己的计时器,就像这样: glutTimerFunc(40, Timer, 0); 无论如何,当我谈到Windows的计时器时,我认为QueryPerformanceCounter和QueryPerformanceFrequency仅在Windows上可用。 - Raven
3
@Raven:“glutTimerFunc”不是OpenGL函数,glut不属于OpenGL的一部分。“仅适用于Windows”的确如此。如果您需要跨平台解决方案,请使用SDL_GetTicks。 - SigTerm
但是如果你正在使用glut(在我的情况下是freeglut),那么你仍然有跨平台的解决方案,对吗?当然,这时需要freeglut库,但是glut在那个文件中提供了有价值的函数(如计时器)... - Raven

1

您绝对需要限制帧速率,这完全取决于渲染循环中发生的情况以及您的应用程序所做的事情。特别是涉及物理/网络方面的应用,或者如果您正在使用外部工具包(如Cairo, QPainter, Skia, AGG等)进行任何类型的图形处理,除非您希望结果不同步或100%的CPU使用率。


1

你问错了问题。你的显示器只能以60 fps(欧洲为50 fps,或者如果你是职业玩家可能为75 fps)的速度显示。

相反,你应该寻求将fps锁定在60或30。有一些OpenGL扩展可以让你这样做。然而,这些扩展不是跨平台的(幸运的是它们不是特定于视频卡的,否则会变得非常可怕)。

这些扩展与您的显示器v-sync密切相关。一旦启用,调用交换OpenGL后缓冲区的操作将被阻塞,直到监视器准备就绪。这就像在您的代码中放置一个睡眠以强制执行60 FPS(或30、15或其他数字,如果您不使用以60 Hz显示的显示器)。不同之处在于,“睡眠”始终是完美定时的,而不是基于上一帧所需时间的猜测。


1
你正在问错问题。这是一个非常合理的游戏开发问题。“你的显示器只能显示”没错,但一个应用程序很容易就可以产生每秒600帧的高帧率。而且,非常高的帧率对于捕捉视频并在之后放慢播放速度非常有用。 - SigTerm
2
相反,你应该寻求将帧率锁定在60或30。如果你这样做,游戏在硬件不够强大的情况下将无法运行。一个正确完成的游戏应该能够在任何帧率下运行(从5到1000)。 "wglSwapIntervalEXT"与帧率几乎没有关系。 - SigTerm
我有点含糊不清。当然,如果系统无法达到每秒30帧的速度,你应该优雅地降级。但是,为了更好的用户体验,最好将帧率“锁定”在某个一致的值上,而不是在20-200之间跳来跳去。 - deft_code
1
@Caspin:“推动高帧率的有效原因”,我已经看到足够多的关于这个主题的激烈讨论,有两个主要的观点 - 通过更高的帧率,你可以获得更流畅的控制。即使fps高于显示器刷新率。另一个观点是为了制作视频游戏视频(比如使用Fraps)- 当帧率超过200时,你可以轻松地从中制作出很好的慢动作视频。 - SigTerm
1
-1是因为欧洲的显示器与世界其他地方的显示器完全相同。50/60帧率只存在于模拟显示器的黑暗时代,对于数字显示器来说不再适用。 - Clearer
显示剩余7条评论

1
这段代码大致可以完成工作。
static int redisplay_interval;

void timer(int) {
    glutPostRedisplay();
    glutTimerFunc(redisplay_interval, timer, 0);
}

void setFPS(int fps)
{
    redisplay_interval = 1000 / fps;
    glutTimerFunc(redisplay_interval, timer, 0);
}

0

这里有一个类似的问题,附带我的答案和实例

我也喜欢deft_code的答案,并且会考虑将他的建议添加到我的解决方案中。

我的答案的关键部分是:

如果您考虑减慢和加速帧,则必须仔细考虑在每种情况下是否指的是渲染或动画帧。在本示例中,简单动画的渲染节流与动画加速相结合,以处理可能出现缓慢动画的情况。

该示例是针对动画代码的,无论基准测试模式或固定FPS模式是否处于活动状态,都会以相同速度进行渲染。即使在更改之前触发的动画也保持恒定的速度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接