画张图镇楼,顺带吐槽B站兴趣圈域名。。。

【帧率控制(表)】
从上次文章我们了解了同调弹幕所面临的技术问题,并介绍了两种针对性解决方案。今天将介绍第三种方案——通过改进帧率控制方法,将帧率恒定在60fps,消除累计误差,从而使同调弹幕成为可能。
那么,我们先来看一下传统的帧率控制方法。
对于STG这种按帧推进的游戏,要使游戏运行速度恒定,就必须使游戏帧率恒定。正常游戏帧率都是60fps。为了将游戏帧率稳定在这个数字,无非通过两种方法,一种是依靠垂直同步,一种是自己写代码通过定时器发出渲染信号。
首先来看垂直同步。这个词大家可能很熟悉,但是并不一定知道这是指什么和什么同步。其实显示器的刷新率和游戏的帧率不是一回事。游戏的帧率可以变化,而显示器的刷新率总是60fps。或者说,显示器刷新的时间点与游戏渲染画面的时间点并不吻合。所谓垂直同步,就是将游戏渲染画面的时间点与显示器刷新画面的时间点同步起来。当开启垂直同步时,渲染出来的画面不是马上显示到屏幕上,而是会等待显卡的同步信号,接收到同步信号后再显示到屏幕。这个同步信号的频率必然和显示器的刷新率相等,如果显示器刷新率是60fps的话,开启垂直同步后游戏的帧率也会是60fps。所以开着垂直同步的话,就直接可以控制游戏帧率了。不过上次文章也说了,显示器刷新率其实也并不是那么可靠的数字,不一定是精确的60fps,而且开着垂直同步有时还会有一些负作用,所以游戏往往还是需要配备一套不使用垂直同步的帧率控制方法。
如果不使用垂直同步的话,渲染出来的画面就不用等待同步信号而直接渲染至屏幕。那么要控制帧率的话,就必须通过定时器来控制渲染画面的时机。这个动作可以简单概括为“每隔16.667毫秒渲染一次画面”。理论上是非常简单,但实际上这个方法非常有讲究,包括精度问题、定时器的实现问题,不过这些不是今天的重点,我们先放一下。今天我们只谈一下传统的控制方法为什么不适合做同调弹幕。至于为什么不适合,其实原因很简单,因为这是一个增量式的控制。所谓“每隔16.667毫秒渲染一次画面”,也就是说从上一幅画面渲染开始,经过16.667毫秒后渲染下一幅画面,如果中间出现运算量过大,发生延时,则下一幅画面开始渲染的时间点就会推迟,之后的画面统一延后。这样的机制必然会形成累计误差,即使是很小的帧率波动,累计到后面,都足以破坏版面与BGM的同步性。
至于怎么避免累计误差,这个思路也很简单。之所以会有累计误差是因为渲染时机是相对上一帧画面延后16.667毫秒确定的,如果渲染信号不是相对于上一帧画面而是相对于第一帧画面,就可以消除累计误差。这个逻辑可以简单表述为,如果当前时间为16.667毫秒的整数倍,就进行画面渲染。
下图示意了传统帧率控制方法与改良后帧率控制方法的差异,其中横轴为时间轴,刻度表示渲染的时间点。当帧率稳定时,各渲染时间点间隔相等。当发生了一个延时或者卡顿时,传统方法会将之后的渲染画面相应延后,以保证当前帧率为60fps。而对于改进后的方法,整体的渲染时间点不受延时或卡顿影响,当发生延时或卡顿后,画面会以最大速度渲染,以追赶整体进度。

在换个角度讲,传统方法是在保障当前帧率为60fps,而改良方法是在保障平均帧率为60fps。因此可以通过改良后的方法来消除累计误差,为同调弹幕提供技术支持。因为这种方法依据的时间不是上一帧的相对时间,而是绝对时间,所以我称它为绝对帧率控制法。
不过,这种方法存在一个比较显而易见的问题。如果游戏发生了持续的延时或者说掉帧,那么之后游戏会持续地对画面进行极速渲染以追赶整体进度。举例来说,Boss某张符卡堆了10000颗子弹,游戏帧率掉到了30fps,持续了十几秒,这张符卡一过,全屏消弹后,游戏会为了追赶整体进度而持续极速渲染,为此游戏可能会持续几秒钟运行在几百fps。这样给玩家的体验就是游戏先掉帧掉了十几秒,然后又加速运行了若干秒。如果是传统的帧率控制方法,可能仅仅只是掉帧这个缺陷而已,而改良后的方法又多出了加速运行的问题,反而使事情更糟。为了防止这种情况发生,必须为这种帧率控制方法配套一种主动丢帧策略。当游戏卡了超过指定帧数后,主动放弃追赶整体进度。或者说,设置一个追赶最大值,当游戏掉帧后,全速渲染追赶整体进度时只能追赶指定帧数,如果超过这个指定值后,则放任其掉帧,延后游戏进度。
关于这个指定值,如果设得太大会存在游戏加速问题,如果设得太小则会存在累计误差问题。极端情况,当其取为无穷大时,则效果和没有这个值一样,游戏会无限追赶整体进度;当其取0时,则效果和传统帧率控制方法一样,游戏在卡顿后不会追赶整体进度,从而形成累计误差。具体取多少合适,需要根据实际情况,根据个人喜好来定。这里提供一个经验数据,从《东方百花宴》到现在的《弹幕音乐绘》,我这里这个值设的都是10帧(166.667毫秒),也就是说,游戏会把10帧以内的瞬间延时给消化掉,10帧以内的瞬间延时都不会造成累计误差,但是更大程度的延时或卡顿就会造成累计误差了。从实际效果看,10帧这个容忍量大大提高了同调弹幕的可行性。
这种帧率控制方法是我3年前制作《东方百花宴》的时候研究出来的,但是并不一定是我率先使用的。东方STG的资深玩家一定知道有个叫VP补丁的东西,可以用于消除东方早期几部作品的操作延时并稳定帧率。从VP补丁的现象看,有两点与这种帧率控制方法吻合,一个是帧率持续稳定在60fps无波动,另一个是使用了伪全屏。因此VP很可能就是使用了这种帧率控制方法。关于为什么要使用伪全屏,这牵涉到取消垂直同步后的画面缺陷,称为画面撕裂现象。至于什么是画面撕裂,其原因是什么,怎么解决,这些问题将在下次文章介绍。
总结一下,加上上次文章所介绍了两种方法,总共讲解了三种支持同调弹幕的技术方案,分别为《Akashicverse》使用的“按帧推进和按时间推进相结合的版面控制逻辑”、《IKUSAAAAAAAN!》的“BGM重定位法”以及上文介绍的“绝对帧率控制法”。其中第一种方法由于会影响到Replay存储功能,有些得不偿失;第二种方法可以在任何情况下将BGM与版面强制同步,其缺点在于同步时BGM会发生卡顿现象;第三种方法可以消除小范围帧率波动带来的累计误差,其缺点在于不能使用垂直同步,画面会有一些缺陷。值得注意的是,第二种方法和第三种方法其实并不矛盾,可以共用。现在我这里还没有把BGM重定位法完全调通,不过说不定在《弹幕音乐绘》剧情篇完成时就会同时使用这两种方法,为同调弹幕加上双保险。
最后还是要说一句,无论用什么技术手段来保证同步,都无法挽回画面持续卡顿及持续掉帧引起的后果。即使是《IKUSAAAAAAAN!》这种强制同步的方法,在持续掉帧时也会造成BGM断断续续,甚至支离破碎。因此在制作同调弹幕时,怎么合理控制弹量、合理布置特效,这依然是创作者必须经历的课题。
以下广告时间:
本系列过往文章将收录在弹幕音乐绘的哔哩哔哩兴趣圈中,欢迎关注
http://www.im9.com/community.html?community_id=12241
游戏《弹幕音乐绘》近期会开始刷存在感,以下是游戏宣传页面:
http://www.slimesmile.cc/
P.S. 近期B站圈子频繁抽风,如果之前的帖子不显示,可通过置顶索引贴来找到过往文章。

【帧率控制(表)】
从上次文章我们了解了同调弹幕所面临的技术问题,并介绍了两种针对性解决方案。今天将介绍第三种方案——通过改进帧率控制方法,将帧率恒定在60fps,消除累计误差,从而使同调弹幕成为可能。
那么,我们先来看一下传统的帧率控制方法。
对于STG这种按帧推进的游戏,要使游戏运行速度恒定,就必须使游戏帧率恒定。正常游戏帧率都是60fps。为了将游戏帧率稳定在这个数字,无非通过两种方法,一种是依靠垂直同步,一种是自己写代码通过定时器发出渲染信号。
首先来看垂直同步。这个词大家可能很熟悉,但是并不一定知道这是指什么和什么同步。其实显示器的刷新率和游戏的帧率不是一回事。游戏的帧率可以变化,而显示器的刷新率总是60fps。或者说,显示器刷新的时间点与游戏渲染画面的时间点并不吻合。所谓垂直同步,就是将游戏渲染画面的时间点与显示器刷新画面的时间点同步起来。当开启垂直同步时,渲染出来的画面不是马上显示到屏幕上,而是会等待显卡的同步信号,接收到同步信号后再显示到屏幕。这个同步信号的频率必然和显示器的刷新率相等,如果显示器刷新率是60fps的话,开启垂直同步后游戏的帧率也会是60fps。所以开着垂直同步的话,就直接可以控制游戏帧率了。不过上次文章也说了,显示器刷新率其实也并不是那么可靠的数字,不一定是精确的60fps,而且开着垂直同步有时还会有一些负作用,所以游戏往往还是需要配备一套不使用垂直同步的帧率控制方法。
如果不使用垂直同步的话,渲染出来的画面就不用等待同步信号而直接渲染至屏幕。那么要控制帧率的话,就必须通过定时器来控制渲染画面的时机。这个动作可以简单概括为“每隔16.667毫秒渲染一次画面”。理论上是非常简单,但实际上这个方法非常有讲究,包括精度问题、定时器的实现问题,不过这些不是今天的重点,我们先放一下。今天我们只谈一下传统的控制方法为什么不适合做同调弹幕。至于为什么不适合,其实原因很简单,因为这是一个增量式的控制。所谓“每隔16.667毫秒渲染一次画面”,也就是说从上一幅画面渲染开始,经过16.667毫秒后渲染下一幅画面,如果中间出现运算量过大,发生延时,则下一幅画面开始渲染的时间点就会推迟,之后的画面统一延后。这样的机制必然会形成累计误差,即使是很小的帧率波动,累计到后面,都足以破坏版面与BGM的同步性。
至于怎么避免累计误差,这个思路也很简单。之所以会有累计误差是因为渲染时机是相对上一帧画面延后16.667毫秒确定的,如果渲染信号不是相对于上一帧画面而是相对于第一帧画面,就可以消除累计误差。这个逻辑可以简单表述为,如果当前时间为16.667毫秒的整数倍,就进行画面渲染。
下图示意了传统帧率控制方法与改良后帧率控制方法的差异,其中横轴为时间轴,刻度表示渲染的时间点。当帧率稳定时,各渲染时间点间隔相等。当发生了一个延时或者卡顿时,传统方法会将之后的渲染画面相应延后,以保证当前帧率为60fps。而对于改进后的方法,整体的渲染时间点不受延时或卡顿影响,当发生延时或卡顿后,画面会以最大速度渲染,以追赶整体进度。

在换个角度讲,传统方法是在保障当前帧率为60fps,而改良方法是在保障平均帧率为60fps。因此可以通过改良后的方法来消除累计误差,为同调弹幕提供技术支持。因为这种方法依据的时间不是上一帧的相对时间,而是绝对时间,所以我称它为绝对帧率控制法。
不过,这种方法存在一个比较显而易见的问题。如果游戏发生了持续的延时或者说掉帧,那么之后游戏会持续地对画面进行极速渲染以追赶整体进度。举例来说,Boss某张符卡堆了10000颗子弹,游戏帧率掉到了30fps,持续了十几秒,这张符卡一过,全屏消弹后,游戏会为了追赶整体进度而持续极速渲染,为此游戏可能会持续几秒钟运行在几百fps。这样给玩家的体验就是游戏先掉帧掉了十几秒,然后又加速运行了若干秒。如果是传统的帧率控制方法,可能仅仅只是掉帧这个缺陷而已,而改良后的方法又多出了加速运行的问题,反而使事情更糟。为了防止这种情况发生,必须为这种帧率控制方法配套一种主动丢帧策略。当游戏卡了超过指定帧数后,主动放弃追赶整体进度。或者说,设置一个追赶最大值,当游戏掉帧后,全速渲染追赶整体进度时只能追赶指定帧数,如果超过这个指定值后,则放任其掉帧,延后游戏进度。
关于这个指定值,如果设得太大会存在游戏加速问题,如果设得太小则会存在累计误差问题。极端情况,当其取为无穷大时,则效果和没有这个值一样,游戏会无限追赶整体进度;当其取0时,则效果和传统帧率控制方法一样,游戏在卡顿后不会追赶整体进度,从而形成累计误差。具体取多少合适,需要根据实际情况,根据个人喜好来定。这里提供一个经验数据,从《东方百花宴》到现在的《弹幕音乐绘》,我这里这个值设的都是10帧(166.667毫秒),也就是说,游戏会把10帧以内的瞬间延时给消化掉,10帧以内的瞬间延时都不会造成累计误差,但是更大程度的延时或卡顿就会造成累计误差了。从实际效果看,10帧这个容忍量大大提高了同调弹幕的可行性。
这种帧率控制方法是我3年前制作《东方百花宴》的时候研究出来的,但是并不一定是我率先使用的。东方STG的资深玩家一定知道有个叫VP补丁的东西,可以用于消除东方早期几部作品的操作延时并稳定帧率。从VP补丁的现象看,有两点与这种帧率控制方法吻合,一个是帧率持续稳定在60fps无波动,另一个是使用了伪全屏。因此VP很可能就是使用了这种帧率控制方法。关于为什么要使用伪全屏,这牵涉到取消垂直同步后的画面缺陷,称为画面撕裂现象。至于什么是画面撕裂,其原因是什么,怎么解决,这些问题将在下次文章介绍。
总结一下,加上上次文章所介绍了两种方法,总共讲解了三种支持同调弹幕的技术方案,分别为《Akashicverse》使用的“按帧推进和按时间推进相结合的版面控制逻辑”、《IKUSAAAAAAAN!》的“BGM重定位法”以及上文介绍的“绝对帧率控制法”。其中第一种方法由于会影响到Replay存储功能,有些得不偿失;第二种方法可以在任何情况下将BGM与版面强制同步,其缺点在于同步时BGM会发生卡顿现象;第三种方法可以消除小范围帧率波动带来的累计误差,其缺点在于不能使用垂直同步,画面会有一些缺陷。值得注意的是,第二种方法和第三种方法其实并不矛盾,可以共用。现在我这里还没有把BGM重定位法完全调通,不过说不定在《弹幕音乐绘》剧情篇完成时就会同时使用这两种方法,为同调弹幕加上双保险。
最后还是要说一句,无论用什么技术手段来保证同步,都无法挽回画面持续卡顿及持续掉帧引起的后果。即使是《IKUSAAAAAAAN!》这种强制同步的方法,在持续掉帧时也会造成BGM断断续续,甚至支离破碎。因此在制作同调弹幕时,怎么合理控制弹量、合理布置特效,这依然是创作者必须经历的课题。
以下广告时间:
本系列过往文章将收录在弹幕音乐绘的哔哩哔哩兴趣圈中,欢迎关注
http://www.im9.com/community.html?community_id=12241
游戏《弹幕音乐绘》近期会开始刷存在感,以下是游戏宣传页面:
http://www.slimesmile.cc/
P.S. 近期B站圈子频繁抽风,如果之前的帖子不显示,可通过置顶索引贴来找到过往文章。