我每秒要显示200帧。框架非常简单,黑色和白色,只有几行。计时器正在驱动动画。我们的目标是在大约时回放帧。 200 fps。
在Linux下我将定时器设置为5毫秒,我让它显示每一帧(即200fps)。工作正常,但在Win 7下失败。
在Win 7(同一台机器)下,我必须将定时器设置为20 ms,并让它每4帧显示一次(50 fps×4 = 200)。我通过反复试验找到了这些神奇的数字。
我应该怎样做才能保证(在合理范围内)动画将以适当的速度在用户的机器上播放?
例如,如果用户的机器只能执行30 fps或60 fps怎么办?
答案 0 :(得分:4)
简短的回答是,你不能(通常)。
为了获得最佳美学效果,大多数窗口系统默认启用“vsync”,这意味着屏幕重绘会以显示器的刷新率发生。在旧的CRT时代,你可能能够使用高端显示器获得75-90赫兹的频率,但是今天的液晶显示器可能会达到60 fps。
也就是说,有一些OpenGL扩展可以通过编程方式禁用VSync(不记得手头的扩展名),并且您可以在驱动程序级别频繁禁用它。但是,无论你做什么(除了自定义硬件),你都无法以200 fps的速度显示完整的帧。
现在,目前还不清楚你是否有需要以200 fps显示的预渲染图像,或者你是否从头开始渲染并希望达到200 fps。如果它是前者,一个好的选择可能是使用计时器来确定你应该显示哪个帧(每个60赫兹更新),并使用该值在两个预渲染帧之间进行线性插值。如果是后者,我只需使用计时器来控制运动(或场景中的任何动态)并根据时间渲染适当的场景。更快的硬件或禁用VSYNC将在相同的时间内为您提供更多的帧(因此更平滑的动画,模数撕裂等),但场景将以正确的速度展开。
希望这有帮助。如果您提供有关您的应用程序的更多信息以及200 fps要求的来源,我们可能会给您更好的建议。
答案 1 :(得分:2)
我已经读过,您的数据采样频率为200Hz,您希望以自然速度播放。即一秒钟的采样数据应在一秒钟内完成。
首先:忘记使用计时器来协调渲染,这不太可能正常工作。相反,您应该测量一个完整的渲染周期(包括v-sync)所花费的时间,并由此推进动画时间计数器。现在200Hz已经是一些非常好的时间分辨率,所以如果数据足够平滑,则根本不需要插值。这样的事情(Pseudocode):
objects[] # the objects, animated by the animation
animation[] # steps of the animation, sampled at 200Hz
ANIMATION_RATE = 1./200. # Of course this shouldn't be hardcoded,
# but loaded with the animation data
animationStep = 0
timeLastFrame = None
drawGL():
timeNow = now() # time in seconds with (at least) ms-accuracy
if timeLastFrame:
stepTime = timeNow - timeLastFrame
else:
stepTime = 0
animationStep = round(animationStep + stepTime * ANIMATION_RATE)
drawObjects(objects, animation[animationStep])
timeLastFrame = timeNow
可能是,您的渲染比屏幕刷新之间的时间快得多。在这种情况下,您可能还想渲染一些中间步骤,以获得某种运动模糊效果(您还可以使用动画数据来获取运动矢量,可以在着色器中使用它来创建矢量模糊效果),渲染循环将如下所示:
drawGL():
timeNow = now() # time in seconds with (at least) ms-accuracy
if timeLastFrame:
stepTime = timeNow - timeLastFrame
else:
stepTime = 0
timeRenderStart = now()
animationStep = round(animationStep + stepTime * ANIMATION_RATE)
drawObjects(objects, animation[animationStep])
glFinish() # don't call SwapBuffers
timeRender = now() - timeRenderStart
setup_GL_for_motion_blur()
intermediates = floor(stepTime / timeRender) - 1 # subtract one to get some margin
backstep = ANIMATION_RATE * (stepTime / intermediates)
if intermediates > 0:
for i in 0 to intermediates:
drawObjects(objects, animation[animationStep - i * backstep])
timeLastFrame = timeNow
答案 2 :(得分:0)
一种方法是在循环的每次迭代中休眠1ms并检查已经过了多长时间。
如果超过目标时间量(对于200 / ps,即1000/200 = 5ms),则绘制一个帧。否则,继续循环的下一次迭代。
E.g。一些伪代码:
target_time = 1000/200; // 200fps => 5ms target time.
timer = new timer(); // Define a timer by whatever method is permitted in your
// implementation.
while(){
if(timer.elapsed_time < target_time){
sleep(1);
continue;
}
timer.reset(); // Reset your timer to begin counting again.
do_your_draw_operations_here(); // Do some drawing.
}
此方法的优点是,如果用户的机器不能达到200fps,您仍然会尽可能快地绘制,并且永远不会调用睡眠。
答案 3 :(得分:0)
这里可能需要考虑两个完全独立的因素:
用户机器的速度有多快?由于机器在准备开始绘制下一帧时仍在处理最后一帧,因此可能无法达到目标帧速率。
您使用的计时器的分辨率是多少?我的印象(尽管我没有证据支持这一点)是Windows操作系统下的计时器提供的分辨率远远低于Linux下的计时器。所以你可能要求睡眠时间为(例如)5 mS,而睡眠时间为15 mS。
进一步测试应该可以帮助您确定这两种情况中的哪一种与您的情况更相关。
如果问题是缺乏处理能力,您可以选择显示中间帧(正如您现在所做的那样),或降低视觉效果(降低质量,降低分辨率或其他任何可能有助于提升速度的内容)。
如果问题是计时器分辨率,你可以查看备用计时器API(Windows API提供两种不同的计时器功能,每种都有不同的分辨率,也许你使用的是错误的),或者通过要求更短的时间来尝试和补偿切片(如Kdoto的建议)。但是,这样做实际上可能会降低性能,因为您现在正在进行比以前更多的处理 - 您可能会注意到此方法下的CPU使用率飙升。
修改强>
正如Drew Hall在他的回答中提到的,还有另一个完整的网站:你在代码中获得的刷新率可能与屏幕上显示的实际刷新率非常不同。然而,这是依赖于输出设备的,并且它的问题听起来像问题是在代码中,而不是在输出硬件中。