- 浏览: 390384 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
yanmie:
您的文章已被我们收录地址:http://www.airmyth ...
学习一下 Pixel Bender -
chaimzane:
我现在自己都有点看不懂了 实际上很简单, LocaleModu ...
Flex 优化技巧 -- 全局CSS样式模块实现RSLS 方式加载 -
muqingren:
以我现在对flex的了解,没看懂你说的........... ...
Flex 优化技巧 -- 全局CSS样式模块实现RSLS 方式加载 -
cony138:
卤煮碉堡了啊
A*寻路 -- 更加真实 的路径(一) -
jack_ye:
[/flash][/flash][/fl[u][u]ash][ ...
Alternativa3D 8 基础理论 / 概念
转载: http://dev.gameres.com/articles.asp?style=a&page=1 作者: Kylinx
终极优化你的游戏 —— 使用脏矩形技术
很久以来由于工作上的繁忙没有写新东西了 ~hoho~
本文基于 2D 表现的游戏,在当今 3D 大行其道的时代,说 2D 是否显得格格不入?这个问题我不作讨论,因为本人从事的一直都是 2D 游戏的开发,所以如果你认为讨论 2D 技术是一个过时的东西就此打住。 废话不多说,下面进入正题。
优化一直是我在程序中追求的东西之一,想想让自己的游戏在一个古董机器能流畅的运行或者说在当今的机器上, CPU 占用率和内存占用率都很低的情况。(毕竟我非常讨厌一个游戏独占了我所有的 CPU 资源)。
如果从图形接口上作优化,常用的就是使用 3D 加速和 CPU 的特殊指令(虽然说 DirectDraw 能够使用 2D 硬件加速,但大部分机器支持的仅仅是简单的加速,比如带 ColorKey 的支持,连一些稍微高级一点的东西,比如 Alpha 混合,带 Alpha 通道的纹理(表面)都不支持,需要自己写,优化起来还是使用 CPU 的特殊指令)。虽然说使用 3D 加速非常简单,但是它的缺点也非常明显:对硬件有苛刻的要求,如果仅仅是做 2D 游戏不推荐使用(新手作为练习写 DEMO 而使用倒还可以 , 我也这样使用过,呵呵 ) 。使用特殊的 CPU 指令最常见的就是使用 MMX 指令了,现在想找到一块装了 Windows95 以上但不支持 MMX 的 CPU 都有难度 ~ 自己花了大半年的时间用 MMX 高速实现了 D3D 对 2D 贴图的各种特效(带通道或者不带通道的纹理,带 BlendColor, 带缩放旋转,做加减法的 Alpha 混合之类的)之后,虽然发现可以不使用 D3D 的东西,但是如果画面的东西很多的话,在一些内存带宽不高的机器上的速度还是不够理想,所以还是需要更多的优化。这时候我想起了 DirtyRect 。
什么是脏矩形?简单的说,就是游戏每次画面的刷新只更新需要更新的那一块区域。 Windows 本身就是最好的例子。或者说 Flash 控件,也正是利用了脏矩形技术,所以他的效率才如此的高。传统的游戏循环如下:
while( 游戏没有结束 ) { if( 有Windows消息 ) { 处理Windows消息 } else if( 需要渲染 ) { 清除游戏屏幕的缓冲区 把游戏中的物体画到缓冲区里面 把缓冲区更新到游戏窗口上 锁定游戏速度,Sleep一段时间,限制FPS } }
从上面的伪代码可以看出,每次游戏都要做清除缓冲区 - 〉渲染游戏的物体 - 〉更新到窗口,而基本上我们写游戏至少要保证最低每秒钟要刷新 24 帧以上(一般都在 30 )。所以上面的代码每秒钟要至少 24 次以上,画面东西越多,耗费的 CPU 越多。
不过我们也可以自然的想到,每次那么多东西不一定都需要更新的,比如一个动画,一般都有一个延迟,比如间隔 200 毫秒更新一次,那么在这段时间是不需要重新画的,只有更新了帧以后,表示这个动画所在的范围已经“脏”了,需要重新画,这个时候才需要画这个动画。而这段时间之内我们可以节约大量的 CPU 时间,很自然,积少成多,总体下来这个数值是非常可观的。再举一个例子,一个静止的游戏物体,(比如一棵树)是永远都不需要更新的,除非这个树的位置或者他的属性发生了变化。这样下来我们首先想到的是,每次我们都省略清除后台缓冲这个步骤,这个非常重要,因为上一次画下来的东西都在这个缓冲区里面,如果清除之后就什么都没有啦 ~~
搞明白了这个原理以后,下面来看看具体实现过程中遇到的问题:
游戏中的物体不会是相互没有遮挡的,所以如果遇到遮挡的问题怎么办?
如果游戏中有 100 个物体,里面的物体相互遮挡关系总有一个顺序,为了简化问题,只考虑两个物体遮挡的情况,多个物体的遮挡可以根据这个来衍生。
考虑上图,物体 B 遮挡了物体 A, 也就是说渲染顺序是先画 A 再画 B ,这个顺序由各自定义,(我自己就喜欢用一棵渲染树来排序,当然如果你用连表或者其他数据结构来实现也没有问题。)如果物体 A 的整个区域都需要更新,那么对于 B 物体,需要更新的部分也就只有 A 与 B 的交集部分(图中的蓝色区域 ) ,在画 B 的时候,我们设置目标裁减区域 (也就是屏幕缓冲的裁减区域)为这个交集部分,则 B 在渲染的时候,相当于整个缓冲区大小就只有蓝色区域那么大,那么裁减函数将会把 B 的数据区裁减到相应的位置(你实现的图形函数中不会没有做裁减的工作吧???如果没有实现,你就不用看了,直接 return 算了,不然下面的东西你肯定不明白我说什么)。怎么样, B 物体相当于只画了蓝色区域这一部分的东西,比整个区域来说节约了不少时间吧?
不知道上面说的你明白了没有,如果没有明白请多看几遍,直到弄明白之后再往下看,不然千万不要往下看。
上面的例子大家肯定会问一个问题,我如何控制 B 只画蓝色区域的部分呢?这个问题我暂时不说,等到把所有的遮挡情况说完了再说。继续看另外的遮挡情况
上面 6 个物体 A,B,C,D,E,X 。 X 是我们的游戏背景颜色,假设画的顺序是 EADCB, 如果 E 需要重新画,那很显然, A,B,C,D 不需要做什么如果 A,D 都需要重新画,那显然 A,D 只需要各画一次。而 B 需要更新的,不是需要更新 BD 相交的区域,而是 AB 相交的大区域,也就是说小区域该忽略掉,如果 B 需要重新画, A,D,C 需要重新画吗?也许有人会说, B 画的次序是在最后的,所以前面的就不需要画了,对么?答案是错的,需要重新画,因为背景缓冲区我们一般情况下 不去清除它,所以谈不上画的顺序了。也就是说, A 与 B 相交的部分, A 在下次画的时候也需要更新, D 也同样 ( 想通了吗?再举一个例子,如果 B 含有大量的透明色,如果 B 需要更新的话,那么 B 的区域首先要涂上 X 作为背景,不然 B 非透明色如果变成了透明色的话,那 B 在重新画的时候,由于透明色不需要画,那么 B 上一次留下来的颜色就残留在 X 上面,看起来当然不对啦,同理对于 A,D 也一样处理)。
上面的理论部分不知道听明白了没有,如果不明白的话自己花一点点时间去想象看。假如明白了的话,下面继续更加深入的问题。
从上面的理论解说部分可以看出,脏矩形的选取和优化是关键。怎样得到最优化的脏矩形表,就成为了这个技术优化的核心部分。
为了简单起见,这里使用的是一个链表来管理所有的渲染物体。
为了实现我们所设计的东西,我设计了一个非常简单的类:
class CRenderObject { public: virtual ~CRenderObject(){} virtual void OnRender( GraphicsDevice*pDevice ) = 0; //所有物体都在这里渲染 virtual void OnUpdate( float TimeStamp ) = 0;//物体更新,比如动画帧更新拉之类的,在这里面可以设置DirtyRect标志之类的 virtual bool IsDirty( ) = 0;//是否有脏矩形 virtual bool GetBoundsRect(RECT*pRect) =0;//得到该物体的范围 virtual int GetDirtyRects ( RECT*pRectBuffer ) = 0;//该物体的脏矩形个数,填充到pRectBuffer里面,返回填充了多少个 ...其他函数 };
我们还需要一个简单的能管理脏矩形和渲染物体的类
class CRenderObjectManager { pulibc: void RemoveRenderObject( CRenderObject*pObject );//删除一个渲染物体 void AddRenderObject( CRenderObject*pObject );//添加一个渲染物体 void Render( GraphicsDevice*pDevice );//渲染所有的物体 void Update( );//更新所有物体 .....其他函数 protected: std::list< CRenderObject* > m_RenderObjects; int m_nCurrentDirtyRectCount;//当前脏矩形数量 struct DirtyRect { RECT Range; //脏矩形范围 int AreaSize; //脏矩形大小,用来排序 }; BOOL m_bHoleDirty;//是否全部脏了 DirtyRect m_DirtyRects[128];//屏幕上最多的脏矩形数量,如果大于这个数量则认为屏幕所有范围都脏了 }; void CRenderObjectManager::Update() { m_bHoleDirty = false; m_nCurrentDirtyRectCount = 0; static RECT DirtyRectBuffer[128]; float TimeStamp = GetElapsedTime(); for(std::list< CRenderObject* >::iterator it = m_RenderObjects.begin(); it != m_RenderObjects.end(); it++) { CRenderObject*pObject = *it; pObject->OnUpdate( TimeStamp ); if(m_bHoleDirty == false && pObject->IsDirty() ) { int Count = pObject->GetDirtyRects(DirtyRectBuffer); for( i =0; i<Count;i++) { 对于该物体的每一个脏矩形DirtyRectBuffer[i] 如果DirtyRectBuffer[i] 没有在任何一个已有的脏矩形范围内 那么把这个脏矩形根据从大到小的顺序添加到脏矩形范围内,否则忽略这个脏矩形 如果脏矩形数量已经大于设定的最大脏矩形范围,设置所有所有屏幕都脏了的标志, } } } 如果屏幕所有都脏了,填充背景颜色 否则为每一个脏矩形填充背景颜色 } void CRenderObjectManager::Render( GraphicsDevice* pGraphics) { for(std::list< CRenderObject* >::iterator it = m_RenderObjects.begin(); it != m_RenderObjects.end(); it++) { CRenderObject*pObject = *it; if(如果屏幕都脏了的标志已经设定) { RECT rcBoundsRect = { 0, 0, 0, 0 }; if( pObject->GetBoundsRect( rcBoundsRect ) ) { //设置屏幕裁减区域 pGraphics->SetClipper( &rcBoundsRect ); } pObject->OnRender( pGraphics ); } else { RECT rcBoundsRect = { 0, 0, 0, 0 }; if( pObject->GetBoundsRect( rcBoundsRect ) ) { //如果该物体的范围与脏矩形缓冲区的任何一个脏矩形有交集的话 for( int i=0; i<m_nCurrentDirtyRectCount; i++ ) { RECT rcIntersect; if( ::IntersectRect( &rcIntersect, &m_DirtyRects[i].Range, &rcBoundsRect ) ) { //只画交集的部分 pGraphics-> SetClipper ( &m_DirtyRects[i].Range ); pObject->OnRender( pGraphics ); } } } } } }
好了,核心代码的伪代码就在这里,不知道大家看明白没有,当然我在这里上面实现的这种方法有一个缺陷,最坏情况下一个也许会导致重新画很多次,如图的情况
假设 A 是渲染物体, B,C,D,E 是由大到小的脏矩形范围,那么很显然,重叠的部分就被反复画。。。这是在分割脏矩形导致的问题,这样画下来,如果 A 物体是采用了叠加混合到背景的算法的话,问题就出来了,重画的部分会变得非常亮。所以脏矩形的分割就显得非常重要,也就是说把这些脏矩形还要分割为互相独立的互不相交的矩形,至于分割算法嘛,嘿嘿,各位还是动一下脑筋思考思考吧:)这个也是最值得优化的地方了,哈哈。实在想不出的话,最简单的方法就是把彼此相交 的脏矩形都做一个合并,得到更大的脏矩形,虽然没有相交的区域了,但是也许这个脏矩形会变得比较大了哦:)
最后,大家一定关心的是我会不会提供源代码,很抱歉的说,不能。我在我的引擎中实现的不是以简单的链表去做的,用的是一棵比较复杂的渲染树,牵扯到的东西就比较多了,所以不方便提供代码,不过可以给一个演示吧:)再说大家如果真的明白了我所说的,那就可以自己动手写一下嘛,不要怕失败 /P^_^,
好啦,关于脏矩形的技术就介绍到这里啦,用好这个技术你会发现你的游戏会在配置极低的机器上也能运行如飞的:)这种技术如果能用在现在市面上的那么多的游戏中的话,就不必为一个小游戏就强占了您100%的 CPU 资源而烦恼拉:)
如果您有更好的方法或者指出其中的不完善的地方还请您不吝赐教,大家多多交流:)
关于测试的 Demo
该 Demo 渲染部分由 Kylinx 花了近半年的时间,全部采用 MMX 写成,已经成功实现 d3d 中对 2d 纹理的操作,速度非常快
关于 Settings.ini
EnableDirtyRect = 1 // 是否允许脏矩形技术, 0= 关闭, 1= 开启
LockFPS = 1 // 是否锁定 FPS , 0= 关闭, 1= 开启
哈,这个 Demo 在不锁定 FPS, 脏技术开启的的情况下,我的 Duron1.8G CPU,FPS 达到 31500 左右!(没错,是三万一千五百)这个数字吓人吧?如果脏技术未打开,只能在 150 左右,相差 200 倍阿!!!
如果 LockFPS 开启,在我机器上( 512M DDR) 跑 30 个 DEMO,CPU 占用还是为 0 ,哈哈!
例子下载: http://dev.gameres.com/Program/Visual/2D/DirtyRectDemo.rar
发表评论
-
Android 中调用 其他 app 的 url 协议
2014-09-05 00:17 1577// var url:String = "i ... -
如何为独立游戏筹集资金
2014-04-22 09:28 0from: http://bbs.9ria.com/thre ... -
blog
2014-01-21 13:37 880http://www.catalinzima.com/ -
在cocos2d-x中实践数据驱动的游戏开发
2014-01-21 09:33 3337from: http://elvisco.de/2013/0 ... -
Inside the AS3 Date class: Timezones and Daylight Saving Time
2014-01-17 13:54 1217from: http://www.computus.org/ ... -
Packed SWF. How to unpack?
2013-10-29 19:23 1585from:http://blog.codestage.ru/ ... -
Stage3D Upload Speed Tester
2013-08-11 04:23 1783from: http://jacksondunstan.co ... -
Speed Up Alpha Textures With Stage3D By 4x
2013-07-26 13:44 2145from: http://jacksondunstan.c ... -
Stage3D optimisation
2013-07-26 13:07 1274from: http://blog.bwhiting.co ... -
Make an AS3 Flash Component with Custom UI Controls
2013-06-28 22:24 2185from: http://studio.barliesqu ... -
234234234234
2013-06-25 21:17 0http://howtonode.org/how-to-ins ... -
AdobeMax: Deep Dive into Flash Player Rendering
2013-06-21 10:41 1540from http://www.developria.com ... -
Why Starling (or any other 2D framework on top of Stage3D)?
2013-06-18 11:00 1087from: http://blog.kaourantin.n ... -
Updated ‘Elastic Racetrack’ for Flash 9 and AVM2
2013-06-18 10:10 1209from: http://www.craftymind.co ... -
HDR&ToneMapping
2013-06-06 09:43 1148from: http://blog.csdn.net/cca ... -
HDR渲染器的实现(基于OpenGL)
2013-06-06 09:37 1927from: http://dev.gameres.com/P ... -
Alternativa3D 8 基础理论 / 概念
2013-06-05 16:31 4450from: http://blog.csdn.net/lim ... -
3D图形技术概念和渲染管线的处理
2013-05-31 01:32 7039from: http://psv.tgbus.com/ne ... -
深度测试与alpha混合
2013-05-31 01:22 1918from: http://www.opengpu.org/f ... -
深入探索3D拾取技术
2013-05-30 23:46 1156from: http://blog.csdn.net/pop ...
相关推荐
脏矩形技术是一种在计算机图形学中用于优化2D图形更新的策略,特别是在游戏和GUI应用中。这种技术的核心思想是避免对整个屏幕或者不必要区域进行无谓的重绘,而是只更新屏幕上的“脏”或已改变的矩形区域。在标题...
本代码主要利用MATLAB工具实现MATLAB——实现注释矩形框,简单明了,易于理解
脏矩形技术是一种在计算机图形学和游戏开发中常见的优化技术,主要应用于更新屏幕上的显示内容。这种技术的目的是减少不必要的重绘操作,提高效率,特别是在处理大量动态元素时。以下是对脏矩形技术的详细解释: 脏...
脏矩形技术是一种在计算机图形学中用于优化屏幕更新的技术,尤其在游戏开发和GUI设计中广泛应用。这种技术的核心思想是识别并仅重绘屏幕上发生改变的区域,而不是每次都需要刷新整个屏幕,从而大大提高了效率。 脏...
在安卓(Android)平台上开发游戏或者涉及到物体动态交互的应用时,多矩形碰撞检测是一个重要的技术环节。这个压缩包文件“安卓Android源码——(多矩形碰撞).zip”很可能包含了一个示例项目,用于演示如何在Android...
脏矩形渲染是一种更高级的优化技术,尤其适用于多窗口系统和动态界面。它基于这样一个观察:并非屏幕上的每个像素每次都需要更新。当内容发生变化时,只记录和更新实际改变的区域(即“脏矩形”)。然后,在下一次...
在安卓(Android)开发中,实现游戏或者交互式应用时,多矩形碰撞检测是一个重要的技术环节。这个压缩包文件“安卓Android源码——(多矩形碰撞).rar”很可能包含了一个示例项目,用于演示如何在Android环境中进行多...
脏矩形技术是一种在计算机图形学和用户界面设计中广泛使用的优化策略,它主要用于减少屏幕重绘的计算量,提高程序性能。这个"dirtyrect.rar_DEMO_脏矩形"压缩包包含了一个演示脏矩形技术的应用源代码,让我们深入...
【标题】"极简击船小游戏——java核心技术hit the dots界面版" 提供了一个学习Java核心技术的机会,特别是针对游戏开发中的用户界面实现。这个小游戏基于《java核心技术》一书中的"击沉dotcoms"概念,作者为了练习和...
标题“code.rar_排样_排样优化_矩形 排样 优化_矩形 排样优化_矩形件排样”和描述“优化排样 矩阵 矩形件排样优化的十字线法”揭示了这个压缩包文件的内容是关于矩形件的排样优化算法,具体采用十字线法。...
这份“安卓Android源码——(矩形碰撞).zip”压缩包很可能是提供了一个简单的示例,教你如何在Android平台上实现两个矩形之间的碰撞检测。以下是对这个主题的详细说明: 一、矩形碰撞检测基础 矩形碰撞检测主要用于...
综上所述,这个“三消游戏——PC版”项目展示了Unity在开发2D游戏方面的强大功能,从游戏逻辑到视觉表现,再到最终的打包发布,Unity都提供了全面的工具和技术支持。对于希望学习游戏开发的爱好者,这是一个极好的...
本代码主要利用MATLAB工具实现MATLAB——使用rectangle命令创建二维矩形或椭圆区域,简单明了,易于理解
《滚屏动作游戏——太空保卫战》是一款基于Android平台的经典游戏,它为初学者提供了学习Android游戏开发的宝贵资源。这款游戏的设计理念是让玩家扮演太空守卫者,抵御一波又一波的外星入侵,通过滚动屏幕来操控飞机...
《Java小游戏——雷电》是基于Java编程语言开发的一款经典飞行射击类游戏,深受玩家喜爱。作为初涉游戏开发的尝试,它展示了Java在游戏开发领域的应用潜力。下面将详细介绍这款游戏中涉及的主要Java技术和相关知识点...
在提供的压缩包中,"MATLAB——求解矩形域内的热方程"可能是包含MATLAB代码的文件,它可能展示了上述过程的一个具体实现。代码可能包含了定义问题、离散化、建立矩阵、时间迭代和结果可视化等部分,每个部分都对应着...
安卓Android源码——(矩形碰撞).rar
【Java小游戏——坦克大战】是基于Java编程语言开发的一款简单但富有挑战性的游戏。它展现了Java在游戏开发中的应用,让玩家能够通过控制自己的坦克与多个敌方坦克进行战斗。游戏中,玩家需灵活操作,发射子弹,上下...