如何使用 requestAnimationFrame 锁定 FPS?
How to lock FPS with requestAnimationFrame?
我使用了 Paul Irish 的剧本
https://gist.github.com/paulirish/1579671
在 html 站点内创建动画循环。
尽管它在全屏模式下比在浏览器中更快 window,但它仍然有效。
此外,我观察到不同的速度取决于 canvas 大小和我使用的浏览器。
问:如何使用脚本保证稳定的帧率?
此处提供代码(WebGL 入门,第 1 章,作者 Brian Danchilla):
https://github.com/bdanchilla/beginningwebgl/blob/master/01/2D_movement.html
您无法直接强制执行稳定的帧速率。您的页面并不是用户平台上唯一 运行ning 的应用程序,而且平台功能差异很大。 requestAnimationFrame
运行 尽可能快,不超过目标设备上的显示更新间隔,但可能会慢得多,具体取决于可用 CPU、GPU、内存和其他限制。
这里的标准做法是测量自上一个动画帧以来经过的时间量,通常使用 Date.now(),并且每个帧将动画推进该时间量。对于人眼来说,这使得生成的动画 运行 具有一致的速度,即使帧速率变化很大。
例如,Shadertoy and GLSL Sandbox 运行 full-screen GLSL着色器等站点并传入一个名为time
(或iGlobalTime
)的制服,这是float
表示自着色器启动以来经过的秒数。此时间值会以不规则的间隔增加,具体取决于每个动画帧渲染所用的时间,但结果是浮点数似乎以每秒 1.0 的稳定数向上计数。这样,根据这个时间值的shader播放就可以显得一致了。
像这样的东西应该有用。如果两帧之间的时间差小于您的 FPS 限制,更新函数 returns 并等待下一帧。但这只会限制更新发生得太快;正如 emackey 所说,更新循环总是有可能 运行 更慢。
var updateId,
previousDelta = 0,
fpsLimit = 30;
function update(currentDelta) {
updateId = requestAnimationFrame(update);
var delta = currentDelta - previousDelta;
if (fpsLimit && delta < 1000 / fpsLimit) {
return;
}
/* your code here */
previousDelta = currentDelta;
}
为了美化@emackey 所说的话,
简短的回答是你不能。您可以要求计算机每一帧做无限量的工作。我不能保证在有限的时间内完成这项工作。
最重要的是,每台计算机的功率不同。便宜的集成 GPU 的功率比高端显卡低得多。 intel i3 比 i7 慢得多。
您还提到更改 canvas 大小。绘制 300x150 canvas 仅需 45000 像素的工作量。绘制 1920x1080 canvas 将需要 2,073,600 像素的工作量或 46 倍以上的工作量
您能做的最好的事情就是做尽可能少的工作,或者自动或根据用户选择删除慢速硬件上的功能。大多数游戏都是这样做的。它们是图形设置选项,用户可以在其中选择分辨率、纹理分辨率、anti-alising 级别和各种其他内容。
也就是说,您可以尝试进行计算,使应用中的事物相对于时间以一致的速度移动。在慢速机器或更大的机器上帧率可能会变慢 canvas 但每秒移动的距离将保持不变。
您可以使用传递给 requestAnimationFrame
的时间值来执行此操作
function render(time) {
// time is time in milliseconds since the page was loaded
...do work...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
比如这里是非帧率独立动画
function render(time) {
xPosition = xPosition + velocity;
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
这里是帧率独立动画
var then = 0;
function render(time) {
var timeInSeconds = time * 0.001;
var deltaTimeInSeconds = timeInSeconds - then;
then = timeInSeconds;
xPosition = xPosition + velocityInUnitsPerSecond * deltaTimeInSeconds;
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
注意:传入requestAnimationFrame的时间分辨率比Date.now()
高
我使用了 Paul Irish 的剧本 https://gist.github.com/paulirish/1579671 在 html 站点内创建动画循环。
尽管它在全屏模式下比在浏览器中更快 window,但它仍然有效。 此外,我观察到不同的速度取决于 canvas 大小和我使用的浏览器。
问:如何使用脚本保证稳定的帧率?
此处提供代码(WebGL 入门,第 1 章,作者 Brian Danchilla): https://github.com/bdanchilla/beginningwebgl/blob/master/01/2D_movement.html
您无法直接强制执行稳定的帧速率。您的页面并不是用户平台上唯一 运行ning 的应用程序,而且平台功能差异很大。 requestAnimationFrame
运行 尽可能快,不超过目标设备上的显示更新间隔,但可能会慢得多,具体取决于可用 CPU、GPU、内存和其他限制。
这里的标准做法是测量自上一个动画帧以来经过的时间量,通常使用 Date.now(),并且每个帧将动画推进该时间量。对于人眼来说,这使得生成的动画 运行 具有一致的速度,即使帧速率变化很大。
例如,Shadertoy and GLSL Sandbox 运行 full-screen GLSL着色器等站点并传入一个名为time
(或iGlobalTime
)的制服,这是float
表示自着色器启动以来经过的秒数。此时间值会以不规则的间隔增加,具体取决于每个动画帧渲染所用的时间,但结果是浮点数似乎以每秒 1.0 的稳定数向上计数。这样,根据这个时间值的shader播放就可以显得一致了。
像这样的东西应该有用。如果两帧之间的时间差小于您的 FPS 限制,更新函数 returns 并等待下一帧。但这只会限制更新发生得太快;正如 emackey 所说,更新循环总是有可能 运行 更慢。
var updateId,
previousDelta = 0,
fpsLimit = 30;
function update(currentDelta) {
updateId = requestAnimationFrame(update);
var delta = currentDelta - previousDelta;
if (fpsLimit && delta < 1000 / fpsLimit) {
return;
}
/* your code here */
previousDelta = currentDelta;
}
为了美化@emackey 所说的话,
简短的回答是你不能。您可以要求计算机每一帧做无限量的工作。我不能保证在有限的时间内完成这项工作。
最重要的是,每台计算机的功率不同。便宜的集成 GPU 的功率比高端显卡低得多。 intel i3 比 i7 慢得多。
您还提到更改 canvas 大小。绘制 300x150 canvas 仅需 45000 像素的工作量。绘制 1920x1080 canvas 将需要 2,073,600 像素的工作量或 46 倍以上的工作量
您能做的最好的事情就是做尽可能少的工作,或者自动或根据用户选择删除慢速硬件上的功能。大多数游戏都是这样做的。它们是图形设置选项,用户可以在其中选择分辨率、纹理分辨率、anti-alising 级别和各种其他内容。
也就是说,您可以尝试进行计算,使应用中的事物相对于时间以一致的速度移动。在慢速机器或更大的机器上帧率可能会变慢 canvas 但每秒移动的距离将保持不变。
您可以使用传递给 requestAnimationFrame
function render(time) {
// time is time in milliseconds since the page was loaded
...do work...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
比如这里是非帧率独立动画
function render(time) {
xPosition = xPosition + velocity;
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
这里是帧率独立动画
var then = 0;
function render(time) {
var timeInSeconds = time * 0.001;
var deltaTimeInSeconds = timeInSeconds - then;
then = timeInSeconds;
xPosition = xPosition + velocityInUnitsPerSecond * deltaTimeInSeconds;
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
注意:传入requestAnimationFrame的时间分辨率比Date.now()