有没有一种方法可以计算需要进行多少次更新才能达到所需的帧率,而不是针对特定的系统?我找到了 Windows 的解决方案,但我想知道在 OpenGL 中是否存在这样的东西。应该是一种计时器。
或者还有什么其他方法可以防止 FPS 大幅下降或上升?这次我正在测试绘制大量线条顶点,并使用 Fraps 可以看到帧率从 400 FPS 下降到 200 FPS,绘制明显变慢。
有没有一种方法可以计算需要进行多少次更新才能达到所需的帧率,而不是针对特定的系统?我找到了 Windows 的解决方案,但我想知道在 OpenGL 中是否存在这样的东西。应该是一种计时器。
或者还有什么其他方法可以防止 FPS 大幅下降或上升?这次我正在测试绘制大量线条顶点,并使用 Fraps 可以看到帧率从 400 FPS 下降到 200 FPS,绘制明显变慢。
你有两种不同的方式来解决这个问题:
假设你有一个名为maximum_fps
的变量,其中包含你想要显示的最大帧数。
然后你测量最后一帧花费的时间(用计时器即可)
现在假设你说你想在你的应用程序中最多有60FPS。那么你希望所测量的时间不低于1/60。如果测量的时间较低,则调用sleep()
来达到帧留下的时间。
或者你可以有一个名为tick
的变量,它包含应用程序的当前“游戏时间”。使用相同的计时器,你将在应用程序的每个主循环中递增它。然后,在你的绘图例程中,你根据tick
变量计算位置,因为它包含应用程序的当前时间。
选项2
的重大优点是,你的应用程序将更容易进行调试,因为你可以玩弄tick
变量,随时向前和向后移动时间。这是一个巨大的优势。
规则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.
}
有没有办法计算需要进行多少次更新才能达到所需的帧率,而不是系统特定的?
没有。
无法精确计算需要调用多少次更新才能达到所需的帧率。
但是,您可以测量自上一帧以来经过了多长时间,根据它计算当前帧速率,将其与所需帧速率进行比较,然后引入一些睡眠来将当前帧速率降低到所需值。这不是一个精确的解决方案,但它会起作用。
我在Windows中找到了这个方法,但我想知道OpenGL本身是否存在类似的东西。应该是某种计时器。
OpenGL只关心渲染内容,与计时器无关。此外,使用Windows计时器并不是一个好主意。使用QueryPerformanceCounter、GetTickCount或SDL_GetTicks来测量经过了多长时间,并使用睡眠来达到所需的帧率。
还有什么其他方法可以防止FPS大幅度下降或上升吗?
通过睡眠来防止FPS上升。
至于防止FPS下降...
这是一个非常广泛的话题。让我们看看。大致上是这样的:使用顶点缓冲对象或显示列表,应用程序档案,不要使用过大的纹理,不要使用过多的透明度混合,避免“原始”的OpenGL(glVertex3f),不要渲染不可见的对象(即使没有绘制任何多边形,处理它们也需要时间),考虑学习BSP或OCTrees来呈现复杂场景,在参数曲面和曲线中,不要不必要地使用太多基元(如果您将使用一百万个多边形渲染圆圈,没有人会注意到差异),禁用垂直同步。简而言之-将渲染调用数量、渲染多边形数量、渲染像素数量、读取纹理像素数量减少到绝对可能的最小值,阅读来自NVidia的所有可用性能文档,您应该可以获得性能提升。您绝对需要限制帧速率,这完全取决于渲染循环中发生的情况以及您的应用程序所做的事情。特别是涉及物理/网络方面的应用,或者如果您正在使用外部工具包(如Cairo, QPainter, Skia, AGG等)进行任何类型的图形处理,除非您希望结果不同步或100%的CPU使用率。
你问错了问题。你的显示器只能以60 fps(欧洲为50 fps,或者如果你是职业玩家可能为75 fps)的速度显示。
相反,你应该寻求将fps锁定在60或30。有一些OpenGL扩展可以让你这样做。然而,这些扩展不是跨平台的(幸运的是它们不是特定于视频卡的,否则会变得非常可怕)。
wglSwapIntervalEXT
glXSwapIntervalSGI
这些扩展与您的显示器v-sync密切相关。一旦启用,调用交换OpenGL后缓冲区的操作将被阻塞,直到监视器准备就绪。这就像在您的代码中放置一个睡眠以强制执行60 FPS(或30、15或其他数字,如果您不使用以60 Hz显示的显示器)。不同之处在于,“睡眠”始终是完美定时的,而不是基于上一帧所需时间的猜测。
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);
}
我也喜欢deft_code的答案,并且会考虑将他的建议添加到我的解决方案中。
我的答案的关键部分是:
如果您考虑减慢和加速帧,则必须仔细考虑在每种情况下是否指的是渲染或动画帧。在本示例中,简单动画的渲染节流与动画加速相结合,以处理可能出现缓慢动画的情况。
该示例是针对动画代码的,无论基准测试模式或固定FPS模式是否处于活动状态,都会以相同速度进行渲染。即使在更改之前触发的动画也保持恒定的速度。