
unity 动画帧速率
Ever wonder if it’s possible to make Unity follow a precise frame rate and potentially even follow an external clock source (commonly known as genlock)? This post will discuss how Unity natively maintains frame rates and how user code can be added to tightly control it. This can be vital in an environment like a broadcast studio where synchronization between Unity and other equipment is crucial.
有没有想过是否有可能使Unity遵循精确的帧速率,甚至可能遵循外部时钟源(通常称为 genlock )? 这篇文章将讨论Unity如何本地保持帧速率,以及如何添加用户代码以对其进行严格控制。 这在广播工作室等环境中至关重要,在该环境中,Unity与其他设备之间的同步至关重要。
Normally, out of the box, a Unity project will attempt to run your project as fast as possible. Frames will be rendered as quickly as they can while generally being limited by your display device’s refresh rate (see V Sync Count). The simplest way to start controlling frame rate is to explicitly set the QualitySettings.vSyncCount so that rendering will occur at an interval related to the display device’s refresh rate (e.g., for a 60Hz display, setting vSyncCount=2 will cause Unity to render at 30fps in sync with the display). This may not give granular enough control, however, as you are limited to submultiples of the display refresh rate.
通常,开箱即用,Unity项目将尝试尽快运行您的项目。 帧将尽快渲染,而通常受显示设备的刷新率限制(请参见 V Sync Count )。 开始控制帧频的最简单方法是显式设置 QualitySettings.vSyncCount, 以便在与显示设备的刷新率相关的时间间隔进行渲染(例如,对于60Hz显示器,设置vSyncCount = 2将导致Unity以30fps的速度渲染与显示同步)。 但是,这可能无法提供足够的精细控制,因为您只能使用显示刷新率的整数倍。
The next simplest solution would be to set QualitySettings.vSyncCount=0 and use Application.targetFrameRate to target a frame rate independent of the display’s refresh rate. With this set, Unity will throttle back its rendering loop to approximately this rate (note that tearing may occur since Unity will no longer be rendering in sync with the display). This is done in a low-cost manner so as not to unnecessarily burn CPU resources. The downside is that this approach may not yield the required precision for every use case.
下一个最简单的解决方案是设置 QualitySettings.vSyncCount = 0并使用 Application.targetFrameRate 来定位独立于显示器刷新率的帧速率。 设置此设置后,Unity会将其渲染循环的速度降低到大约此速率(请注意, 由于Unity将不再与显示同步进行渲染,因此可能会发生 撕裂 )。 这是以低成本方式完成的,以免不必要地消耗CPU资源。 缺点是,这种方法可能无法为每个用例提供所需的精度。
Fear not, coroutines can help improve precision. Rather than rely on Unity’s built-in frame rate throttling, you can control it yourself via script code. In order to do so, you must let Unity try to run as fast as possible by setting QualitySettings.vSyncCount=0 and Application.targetFrameRate to a very high value, and then, using a WaitForEndOfFrame coroutine, slow it down to precisely the rate you are looking for by refusing to allow the next frame to start rendering until you say so. To do this precisely, we suggest you use a combination of Thread.Sleep to conservatively delay the Unity rendering loop without eating up CPU resources, and then for the last few milliseconds, spin the CPU while checking for exactly the right time to allow the next frame to start. If you are trying to coordinate Unity’s frame rate with an external clock (genlock) you should break out of this CPU spinning loop as soon as an external sync signal is received.
不用担心,协程可以帮助提高精度。 您可以依靠脚本代码自己控制它,而不必依靠Unity的内置帧率限制。 为此,您必须通过将 QualitySettings.vSyncCount = 0和 Application.targetFrameRate 设置 为一个很高的值来 让Unity尝试尽可能快地运行 ,然后使用 WaitForEndOfFrame 协程将其降低到精确的速率通过拒绝允许下一帧开始渲染来寻找,直到您这样说。 为了精确地做到这一点,我们建议您使用 Thread.Sleep 的组合 来保守地延迟Unity渲染循环而不会占用CPU资源,然后在最后几毫秒内旋转CPU,同时检查恰好合适的时间以允许下一次框架开始。 如果您尝试与外部时钟(同步锁相)协调Unity的帧速率,则应在收到外部同步信号后立即退出此CPU自旋循环。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
using System.Collections;
using System.Threading;
using UnityEngine;
public class ForceRenderRate : MonoBehaviour
{
public float Rate = 50.0f;
float currentFrameTime;
void Start()
{
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 9999;
currentFrameTime = Time.realtimeSinceStartup;
StartCoroutine("WaitForNextFrame");
}
IEnumerator WaitForNextFrame()
{
while (true)
{
yield return new WaitForEndOfFrame();
currentFrameTime += 1.0f / Rate;
var t = Time.realtimeSinceStartup;
var sleepTime = currentFrameTime - t - 0.01f;
if (sleepTime > 0)
Thread.Sleep((int)(sleepTime * 1000));
while (t < currentFrameTime)
t = Time.realtimeSinceStartup;
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
using System . Collections ;
using System . Threading ;
using UnityEngine ;
public class ForceRenderRate : MonoBehaviour
{
public float Rate = 50.0f ;
float currentFrameTime ;
void Start ( )
{
QualitySettings . vSyncCount = 0 ;
Application . targetFrameRate = 9999 ;
currentFrameTime = Time . realtimeSinceStartup ;
StartCoroutine ( "WaitForNextFrame" ) ;
}
IEnumerator WaitForNextFrame ( )
{
while ( true )
{
yield return new WaitForEndOfFrame ( ) ;
currentFrameTime += 1.0f / Rate ;
var t = Time . realtimeSinceStartup ;
var sleepTime = currentFrameTime - t - 0.01f ;
if ( sleepTime > 0 )
Thread . Sleep ( ( int ) ( sleepTime * 1000 ) ) ;
while ( t < currentFrameTime )
t = Time . realtimeSinceStartup ;
}
}
}
|
Finally, on a related topic, if trying to coordinate time with an external clock source, you may also want Unity’s internal game time to advance at the same pace (rather than follow the CPUs clock which may drift over time relative to the external source). This is can be achieved by setting Time.captureFramerate to the same rate as the external clock. For example, if your external genlock signal is operating at 60fps, setting captureFramerate to 60 instructs Unity to allow exactly 1/60th of a second of game time to elapse between frame renders regardless of exactly how much real time has passed. As of Unity 2019.2 beta, it is possible to set a floating point capture frame rate by setting Time.captureDeltaTime. For older Unity versions, if your external signal is not operating at an integer based rate (like say 59.94), you can vary captureFramerate at every frame rendering to achieve the desired average rate for the advancement of time.
最后,在一个相关主题上,如果尝试与外部时钟源协调时间,那么您可能还希望Unity的内部游戏时间以相同的速度前进(而不是跟随可能随着时间而变化的CPU时钟)。 。 这可以通过将 Time.captureFramerate 设置 为与外部时钟相同的速率 来实现 。 例如,如果您的外部同步锁相信号以60fps运行,则将captureFramerate设置为60会指示Unity允许在帧渲染之间准确地经过游戏时间的1/60秒,而不管实际经过了多少时间。 从 Unity 2019.2 beta版开始 ,可以通过设置 Time.captureDeltaTime 来设置浮点捕获帧速率 。 对于较旧的Unity版本,如果您的外部信号不是以基于整数的速率运行(例如59.94),则可以在每个帧渲染时更改captureFramerate,以达到所需的平均速率以延长时间。

A sample proof-of-concept Unity project exploring the above topic is available at our GitHub project page. Specifically, precise control of frame rate using the WaitForEndFrame coroutine is given in ForceRenderRate.cs. A more complex example which emulates an external genlock can be found in GenLockedRecorder.cs. Although Unity does not natively support any vendor-specific genock implementation, the latter is a good starting point for integration with a third-party SDK offering this feature.
可以在我们的 GitHub项目页面上找到 探讨上述主题的示例概念证明Unity项目 。 具体来说, 在 ForceRenderRate.cs中 提供了 使用 WaitForEndFrame 协程 精确控制帧速率的 功能 。 可以在 GenLockedRecorder.cs中 找到一个更复杂的模拟外部同步锁相的 示例 。 尽管Unity本身不支持任何特定于供应商的genock实施,但后者是与提供此功能的第三方SDK集成的良好起点。
Please note that all above techniques yield the most stable frame rates when part of a Unity standalone player build. They are functional nonetheless when in Play Mode inside the Unity Editor but you may experience momentary fluctuations from time to time.
请注意,在Unity独立播放器构建的一部分中,上述所有技术都会产生最稳定的帧速率。 虽然它们在Unity编辑器中的“播放模式”下仍可运行,但您可能会时不时地遇到波动。
翻译自: https://blogs.unity3d.com/2019/06/03/precise-framerates-in-unity/
unity 动画帧速率