EGE基础入门篇(九):双缓冲与手动渲染

EGE专栏:EGE专栏

上一篇:EGE基础入门篇(八):清屏与重绘

下一篇:

一、双缓冲机制

  双缓冲是一种基本的动画技术,主要用于解决单缓冲清除图像图像绘制过程所带来的窗口闪烁问题。

  双缓冲指的是使用两块用于画面显示的帧缓冲区的一种绘图技术,只使用一块帧缓冲区的则称为单缓冲
在这里插入图片描述

1. 单缓冲绘图

  单缓冲是指程序只使用一块帧缓冲来进行绘图显示。这时帧缓冲既要被系统读取并显示屏幕上,又要作为程序绘图的目标。
在这里插入图片描述

1.1 单缓冲绘图的缺点

  程序绘图需要一定的时间,系统读取帧缓冲和程序绘图的时间并不是同步的,也就是说程序并不确切知道系统什么时候要来读取帧缓冲
  这样一来就有个很大的问题,如果在程序还没有将下一帧图像绘制完成的时候,系统就来读帧缓存,就会出现如下这种情况:

  “哟,大哥!您怎么来了?”
  “这不是到点了吗,我来收画了!快把你的画拿来,我临摹一份,拿出去给上级看看!”
  “哎哟,我这的时钟不准,忽快忽慢的。这不,我看到外边的景物变了,想对照着重画一幅呢。没想到这么巧,刚画了一半,您就来了!”
  “外边的人都等不及了,那就这样,这次画的就算了,把上次画的那份拿来吧,样子都差不多,那份也行。”
  “哎呀!您不是不知道,我家里就只有这么一块宝贝画板。要重新画,只能之前画的擦掉了,不然看着乱糟糟的。”
  “就你小子事多,怎么这么抠门,画板也不舍得多买一块!唉,算了,直接把你这份给我吧,没画完就没画完吧,总好过没得交差,不然上头怪罪下来,非把你给辞了不可!这次我先帮你担着,下次要给我好好表现!”
  “大哥放心,下次一定,下次一定!”
  话毕,大哥临摹了一幅半成品就急匆匆跑下一家去了。
  …
  看着墙上的时钟,大哥掐好了点出门,跑了几家后,看着手上拿着的几幅佳作,很是满意。不久后,大哥再次走到了那扇门前。“这次几幅画都很不错,上级肯定会多多夸奖我几句!上次就这小子的画出了问题,上级虽然嘴上没说什么,但心里肯定有点不愉快。这次可别给整我出什么幺蛾子了!”
  大哥怀着期待的心情推开了门,一进屋,那种似曾相识的感觉扑面而来。。。随后留下两人看着那块空白的画板大眼瞪小眼。
  …

  你:“什么垃*程序,窗口闪成这样!”,退出卸载删除一键三连

  系统通常会以每秒60次的频率读取窗口帧缓冲,然后显示到屏幕上。通常程序绘图和系统读取窗口帧缓冲的时间很难保持同步,很有可能窗口正在绘图的时候系统来读取帧缓存,此时就将不完整的一帧显示到了屏幕上
  如果这些没有绘制完成的相邻帧之间有很大的反差,那么人眼就会感觉到窗口闪烁。

1.2 系统读取帧缓冲

  由于系统读取帧缓冲和程序绘图的时间并不同步,所以程序的帧缓冲很有可能在绘图的任意阶段被系统读取。
  如下图所示,在一个绘图周期中分为绘图阶段绘图完成阶段。在绘图阶段程序尚未完成绘图,此时不应被系统读取,否则未完成的图形将会被显示到屏幕上。此时显示到屏幕上将是绘图过程中的任意一个图形,如果绘图过程图形的变化极大,那么极容易引起闪烁。绘图阶段占整个绘图周期的比例越大,闪烁发生的概率越高。
  在绘图完成阶段缓冲区中已是完整的一帧,此时允许被系统读取。
在这里插入图片描述

  在绘图阶段被读取时,图形变化过程中的任意一个图形都有可能被显示到屏幕上,而且顺序是不定的。在屏幕上图形不断的变化中,人眼就能明显地感觉到闪烁。

在这里插入图片描述

2. 双缓冲绘图

  双缓冲绘图使用两块帧缓冲,一块帧缓冲用于显示,另一块帧缓冲用于绘图。

用于显示的帧缓冲称为屏幕缓冲区(on-screen buffer),也叫前台缓冲区(front buffer)
用于绘图的帧缓冲称为离屏缓冲区(off-screen buffer),也叫后台缓冲区(back buffer)

在这里插入图片描述

2.1 双缓冲绘图流程

  系统会不断地从前台缓冲区(屏幕缓冲区)中读取图像数据,程序绘图则需要保证前台缓冲区的数据都是完整的一帧,只在后台缓冲区(离屏缓冲区)上绘图
  当绘图完成后,才会交换两个缓冲区中的数据,使得前台缓冲区中的图像数据变成和原来后台缓冲区中的一致。这样就完成了一帧的绘图操作并平滑地显示在了屏幕上。

  这个交换缓冲区数据操作,通常是复制或者直接交换两个缓冲区首地址指针,取决于各平台的实现。在windows平台中通常是通过复制操作来实现。
  参见 Front, Back, and Other BuffersBuffer Functions

在这里插入图片描述
  由于系统只从前台缓冲区中读取图像数据,而我们是在后台缓冲区上绘制,绘制完成后才快速交换数据。所以绘图过程中的不完整帧,不会被系统读取到,也就不会被显示到屏幕上,这样就避免了闪烁
在这里插入图片描述

2.2 缓冲区之间的数据交换操作 : SwapBuffers()

  上面提到,当在后台缓冲区绘制完一帧后,需要将这一帧的图像数据交换到前台缓冲区中。
  这个操作通常称为交换缓冲区(Swap Buffers)。这里的交换并不是真的就是两个缓冲区互换,而是有两种不同的实现方式:

  一种是通过数据复制来实现,直接将后台缓冲区里的数据复制到前台缓冲区上,这也是windows上的常用实现。(参见 Front, Back, and Other BuffersBuffer Functions),当然,这种方式无疑会有不小的开销。

  另一种是通过交换缓冲区的指针或引用来实现,而这一种实现也被称作页交换。交换仅仅只需要改变一个指针的操作,非常高效。

在这里插入图片描述
  这两种不同的实现方式各有利弊,至于使用哪种实现,各平台有自己的考量。这里仅列出《游戏编程模式》中的解释:

参考链接:《游戏编程模式》双缓冲模式

请添加图片描述

二、EGE中的双缓冲机制与手动渲染模式

1. EGE双缓冲

  除了窗口本身的帧缓冲外,EGE还额外创建了一块帧缓冲,构成双缓冲
  EGE以内部的帧缓冲作为后台缓冲区,绘图都是在这块缓冲区中进行,绘图完成后,调用特定的函数即可将后台缓冲区中的数据复制到前台缓冲区中,完成图像显示。
在这里插入图片描述

2. 如何触发SwapBuffers操作?

  EGE中的 SwapBuffers 操作是通过复制缓冲区实现,而不是交换缓冲区指针。

  EGE触发SwapBuffers有两种方式,一种是靠内部50ms定时器触发,另一种是调用EGE延时及阻塞类函数时触发。

  EGE延时及阻塞类函数包括 getch(), getkey(), getmouse(),delay_fps(),delay_ms() 等,这些函数的调用会触发SwapBuffers操作,前提是调用了EGE的绘图函数,让其检测有绘图操作。

  由内部50ms定时器触发的SwapBuffers操作,这个是十分不建议的。因为双缓冲本身就是为了防止因未完成的图形内容显示到屏幕上造成的闪烁,如果绘图未完成时定时器触发了SwapBuffers操作,那么就会使得未完成的图形内容在屏幕上显示,无法达到避免闪烁的效果。这样也仅仅是将闪烁频率从60Hz降到了20Hz而已,只能用于不断叠加绘制的绘图中,一旦清屏重绘,闪烁将十分明显。
  内部50ms定时器是默认开启的,那么通过什么设置可以关闭定时器触发SwapBuffers操作呢?这就是接下来的手动渲染模式

3. EGE手动渲染模式

  EGE通过设置手动渲染模式,直接禁用由内部定时器触发的 SwapBuffers 操作。这样后台缓冲区和前台缓冲区的交换操作,只能由用户调用延时及阻塞类函数来触发,不会有在绘图过程中莫名触发交换缓冲区的情况发生。
  用户就可以在完成一帧的绘制后手动调用延时及阻塞类函数来进行缓冲区交换,完成双缓冲消除闪烁的功能。

  当程序有清屏重绘的操作时,就需要设置手动渲染模式,来避免图形闪烁。
  设置为手动渲染模式后,通过调用常用的delay_fps()或delay_ms(), getch()等来触发SwapBuffers

下面是简单的一个设置示例:

#include <graphics.h>

int main()
{
	//创建窗口,设置手动渲染模式
	initgraph(640,480, INIT_RENDERMANUAL);
	
	//帧循环,通过delay_fps()触发缓冲区交换
	for ( ; is_run(); delay_fps(60)) {
		//清屏
		cleardevice();

		//绘图,中途可使用getch()暂停,或者调用delay_ms(0)触发缓冲区交换
	}
	
	closegraph();
	return 0;
}

  在初始化参数中传入INIT_RENDERMANUAL便能简单地完成手动渲染模式的设置。下面则更为详细地介绍EGE中的自动渲染模式手动渲染模式。

3.1 EGE中的渲染模式

渲染模式 分为 自动渲染手动渲染默认为自动渲染

3.1.1 自动渲染模式

  自动渲染模式,即调用绘图类的函数后,由库决定何时刷新(50ms定时),把绘画的内容显示到窗口上。

  优点绘画后显示
  缺点:少量绘制后便会刷新一次窗口,当绘制复杂时,多次刷新比较费时,造成绘制慢。因为不确定刷新时间点,可能在没有绘制完一帧时就显示内容,容易出现窗口闪烁现象。

3.1.2 手动渲染模式

  手动渲染模式,即需要自己调用 delay_fps, delay_ms, getch()带有等待特性并具有刷新功能的函数来刷新窗口,加快绘制速度,避免闪烁。

  优点:减少刷新次数,加快绘制。并且只在调用某些延时类函数时刷新,避免显示不完整的一帧,减少闪烁。
  缺点:不调用延时函数时不会刷新窗口,如果整个循环没有调用带刷新功能延时类函数,将看不到绘制内容。

  绘制时推荐一直使用手动渲染模式,这将提高绘制效率并将减少闪烁情况。 当有刷新窗口、查看显示内容的需要时,可以插入delay_ms(0) 来手动刷新,或者加入 getch() 暂停来查看。

  如果你的程序有清屏之类窗口颜色变化极大的,那就必须设置为手动渲染模式,否则画面容易出现问题。

3.2 渲染模式的设置

3.2.1 在窗口初始化时设置

  调用initgraph() 初始化图形环境时,在初始化模式参数中传入INIT_RENDERMANUAL即可设置成 手动渲染模式, 默认为 自动渲染模式

initgraph(640, 480, INIT_RENDERMANUAL);

  初始化模式参数也可在 setinitmode() 中设置。

setinitmode(0, 0, INIT_RENDERMANUAL);
initgraph(640, 480);
3.2.2 setrendermode函数

  setrendermode(), 用于改变渲染模式,需要在初始化窗口 initgraph() 之后调用

void setrendermode(rendermode_e mode);

参数 mode,
值有两个可选:自动模式用于简单静态绘图,手动模式用于制作动画或者游戏

  • RENDER_AUTO (自动渲染,默认模式)

  • RENDER_MANUAL (手动渲染)


EGE专栏:EGE专栏

上一篇:EGE基础入门篇(八):清屏与重绘

下一篇:

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依稀_yixy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值