剑三阵法有用么:关于脏矩形的那些事
来源:百度文库 编辑:偶看新闻 时间:2024/05/09 18:56:28
关于脏矩形的那些事
(2011-12-30 20:06:15) 转载▼标签:杂谈
分类: GameProgramming 很多人对脏矩形算法有所误解,其主要原因在于:(1) 认为脏矩形算法是2D像素游戏时代的古老产物,而现在多数游戏都跑在3D引擎上。
(2) 脏矩形算法一般都有底层框架或引擎支持,上层应用/游戏开发者不需要考虑脏矩形问题。
对于以上两个问题,我的回答:
(1) 在使用3D渲染2D的情况下,脏矩形算法依然有效(比如:通过OpenGL/Direct3D纹理贴图来实现2D图像绘制,使用脏矩形技术可以大幅降低光栅化所需的时间。其实Android Surface就是这么做的,如:脏矩形链可以当成纹理扣出来等。)
(2) 虽然很多引擎本身支持脏矩形,但当你需要对引擎进行修改或扩展时,必须了解该引擎对脏矩形的支持程度,以及背后的运作机制。
声明:我这里只是提供大致思路(甚至自己都没有写过测试代码,主要是没时间...),如果所述有误,希望指正,谢谢。
在讲述脏矩形概念之前,先来看一下游戏图形渲染的基本流程:
while(1){
}
对于2D游戏或UI系统来说,渲染流程主要就分为以上2个阶段。(这里假设游戏是全屏独占的,对于非全屏独占情况类似,但底层实现有较大差别)
事实上,第一阶段和第二阶段均可以被脏矩形算法优化,并且脏矩形算法必须要求double buffer支持。
1. 使用脏矩形算法优化第二阶段:
翻转前后台缓冲区,有两种方法。一种采用把内存数据直接Blt到显存中,另一种则是采用页面翻转。
之所以不采用页面翻转的原因在于:不是所有适配器支持页面翻转,且页面翻转需要渲染代码直接对VGA硬件显存读写,而VGA硬件总线一般会比较慢。
如果我们把物体绘制到后台缓冲(内存),然后再通过脏矩形Blt到显存,则可以大幅降低对VGA内存的访问。
算法流程(假设背景不变):
设置前台缓冲区背景(因为背景不变)
DirtyList初始化为空
while(1)
{
}
以上算法的缺点是,每次必须清空整个后台缓冲区然后重新绘制背景和所有物体。
但我们已假定背景不变,其实只要重画所有物体即可(不需要重新画背景。注:这里并不关心物体本身是否是脏的),于是得到进一步的优化:
设置前台缓冲区背景
设置后台缓冲区背景
DirtyList初始化为空
while(1)
{
}
以上算法,在Android里的实现方式如下:
public void run() {
}
注1:一般来说,游戏的背景不会一成不变,但很少每一帧都在变,在这种我们只要在背景变化前重设脏矩形区域为整个屏幕区域即可。
注2:把所有物体的矩形加入dirtyRect,重建dirtyRect 这一步也可以放到下一帧的逻辑代码前面。(因为只有在逻辑代码里修改物体状态才会导致脏矩形改变)
注3:Android里Holder.lockCanvas(dirtyRect)的意思是说,后面的绘图操作使用dirtyRect作为clip区域,并且在最后swap前后台缓冲时只刷新dirtyRect区域,并且刷新完了之后下一帧lock到的Canvas上的buffer数据除了dirtyRect以外都是上一帧画完时的数据。(dirtyRect因为本来就要重画,所以这块区域Android不会保存)
2. 使用脏矩形算法优化第一阶段:
在(1.)中的算法必须要重画所有物体,并且每帧的脏区的总和涉及了至少所有的物体(节省了第二阶段的时间以及第一阶段中绘制背景的时间)。而事实上游戏中的物体大多都是不变的,比如:TileGame中的树木、房子等,
在这种情况下,我们希望这些物体只绘制到后台缓存一次,以后除非物体状态被改变(或者与该物体重叠的区域上的物体状态被改变),否则就不再把这些物体再次绘制到后台缓存上。
由于一个物体是否要绘制到后台缓存取决于逻辑代码(物体状态),比如:游戏的上层逻辑 (游戏开发者)调用引擎的API接口去控制物体的位置、大小、动画帧索引,那么游戏的下层逻辑(引擎)就需要在这些API的实现里标记这些物体是脏的(MarkDirty)。
MarkDirty()函数:
注意:物体状态的改变主要分两种:一种只是Sprite本身图片改变,如:按钮被按下(image),另一种是位置、大小或可见性等属性改变(resize),这两种最好分开处理。
public void run() {