📄 专业tc--图形篇6.htm
字号:
point)<BR>{kk1}<BR>if (m_bMouseCaptured)
{kk1}<BR>ASSERT(m_pCapturedSprite);<BR>CRect rcOld,
rcNew;<BR>m_pCapturedSprite->GetRect(&rcOld);<BR>m_pCapturedSprite->SetPosition(point.x
- m_ptOffset.x, <BR>point.y -
m_ptOffset.y);<BR>m_pCapturedSprite->GetRect(&rcNew);<BR>// Add the
changed regions to the view's dirty
list.<BR>AddDirtyRegion(&rcOld);<BR>AddDirtyRegion(&rcNew);<BR>// Render
and draw the
changes.<BR>RenderAndDrawDirtyList();<BR>}<BR>}<BR>子图形的位置被保存在一个CRect对象中。移动子图形,并获得其新的位置。通过调用AddDirtyRegion,将新旧矩形添加到重画列表中。一旦这些区域被添加到列表中,将产生对RenderAndDrawDirtyList的一个调用从而画出改变后的区域。AddDirtyRegion和RenderAndDraWDirtyList都是COSBView中的新函数。<BR>COSBView类具有一个新的对象来保存重画列表和几个新的成员函数。下面是COSBVIEW.
H的新添加的部分:<BR>class COSBView : public
CScrollView<BR>{kk1}<BR><BR>public:<BR>virtual void Render(CRect* pClipRect =
NULL) {kk1}return;}<BR>void AddDirtyRegion(CRect* pRect);<BR>void
RenderAndDrawDirtyList();<BR><BR>private:<BR>CObList m_DirtyList; // Dirty
regions<BR><BR>void EmptyDirtyList();<BR>};<BR><BR>#ifndef _DEBUG // Debug
version in osbview.cpp<BR>inline CDocument* COSBView::GetDocument()<BR>{kk1}
return (CDocument*) m_pDocument;
}<BR>注意重画列表是作为由Microsoft基类库(MFC)所提供的一个CObList对象实现的。CObList类保留了一个对象指针的列表,而其中每一个对象又都是从CObject类导出的。此表在这用于保存指向CRect对象(重画区域在这里的实现)的指针,而通过创建一个由CObList导出而只处理CRect对象的类,从而使实现更多了一些类安全性。采用这种方法要求进行类型强制,因此对新手来说也更多了一些危险性。现在来看一看AddDirtyRegion函数,这样可以了解列表的使用方法:<BR>void
COSBView::AddDirtyRegion(CRect* prcNew)<BR>{kk1}<BR>// Get the rectangle
currently at the top of the list.<BR>POSITION pos =
m_DirtyList.GetHeadPosition();<BR>if (pos) {kk1}<BR>CRect* prcTop =
CRect*)m_DirtyList.GetNext(pos);<BR>CRect rcTest;<BR>// If the new one
intersects the top one merge them.<BR>if (rcTest.IntersectRect(prcTop, prcNew))
{kk1}<BR>prcTop->UnionRect(prcTop, prcNew);<BR>return;<BR>}<BR>}<BR>// List
is empty, or there was no intersection.<BR>CRect* prc = new CRect;<BR>*prc =
*prcNew; // Copy the data.<BR>// Add a new rectangle to the list. .
m_DirtyList.AddHead((CObject*)prc);<BR>}<BR>记住在这里使用的唯一优化是如果一个新添加的矩形和重画列表中位于其上面的那个矩形之间存在重叠,则进行合并。代码首先获得位于列表顶部的矩形——注意CRect*强制,如果列表是空的,那么新的矩形被简单地添加到列表的顶部,如果在列表的顶部<BR>已经存在有一个矩形,则检测该矩形是否和新矩形之间有相交。如果有,则简单地进行扩大以包括新的矩形。如果没有发现有交叉,则将新矩形添加到列表的顶部。<BR>现在,让我们再来看一看<BR>RenderAndDrawDirtyList函数:<BR>void
COSBView::RenderAndDrawDirtyList()<BR>{kk1}<BR>POSITION pos =
m_DirtyList.GetHeadPosition();<BR>// Render all the dirty regions.<BR>while
(pos) {kk1}<BR>// Get the next region.<BR>CRect* pRect =
(CRect*)m_DirtyList.GetNext(pos);<BR>// Render it.<BR>Render(pRect);<BR>}<BR>//
Draw all the dirty regions to the screen.<BR>while (!m_DirtyList.IsEmpty())
{kk1}<BR>// Get the next region.<BR>CRect* pRect =
(CRect*)m_DirtyList.RemoveHead();<BR>Draw(NULL, pRect);<BR>// Done with
it.<BR>delete
pRect;<BR>}<BR>}<BR>对重画列表中的每一个矩形,将它作为一个剪切区域的矩形传递给Render函数,进行函数调用。然后再遍历一次重画列表,这次是通过调用一次Draw函数,将被构图的缓冲区区域拷贝到屏幕上。<BR>为了能够将这个函数包含在COSBView类中,作者还增加了一个模拟Render函数。这是一个虚拟函数,由用户提供一种实现来覆盖它。下面是在头文件中实际定义的模拟函数:<BR>virtual
void Render(CRect* pClipRect = NULL)
{kk1}return;} <BR>现在来看一看CAnimView中真正的Render函数:<BR>void
CAnimView::Render(CRect* pClipRect)<BR>{kk1}<BR>CAnimDoc* pDoc =
GetDocument();<BR>CRect rcDraw;<BR><BR>// Get the background DIB and render
it.<BR>CDIB* pDIB = pDoc->GetBackground();<BR>if (pDIB)
{kk1}<BR>pDIB->GetRect(&rcDraw);<BR>// If a clipping rectangle was
supplied, use it.<BR>if (pClipRect) {kk1}<BR>rcDraw.IntersectRect(pClipRect,
&rcDraw);<BR>}<BR>// Draw the image of the DIB to the off-screen
buffer.<BR>ASSERT(m_pDIB);<BR>pDIB->CopyBits(m_pDIB, <BR>rcDraw.left,<BR>rcDraw.top, <BR>rcDraw.right
- rcDraw.left,<BR>rcDraw.bottom -
rcDraw.top,<BR>rcDraw.left, <BR>rcDraw.top);<BR>}<BR><BR>// Get the sprite
and render it.<BR>CSprite* pSprite = pDoc->GetSprite();<BR>if (pSprite)
{kk1}<BR>pSprite->Render(m_pDIB,
&rcDraw);<BR>}<BR>}<BR>在这个函数中没什么新东西,但作者想再显示一次Render,这样用户可以看到剪切矩形是如何被用来限制缓冲区的受影响区域的。<BR><BR><BR>这一章里,我们简单介绍了计算机动画制作的基础知识,并通过上述几个例程讲解了如何在Windows
下实现简单动画过程。实际上要想做出真正精美的动画,或是进行游戏创作还需要借助应用软件和图形处理工具。<BR>象电视电影一样,有很多动画软件将动画过程分解为不同的图片,再把它们连起来播放,达到动画效果。在这里,我们介绍一下,如何方便的制作出三维动画。在三维图像处理软件3D
MAX中提供了将操作过程连接城动画的功能,利用这一点,我们可以很容易的制作出逼真的三维动画。<BR>这一节我们制作一个简单的动画,一只茶壶从飘着雪花的雪山下飞出。由于具体的制作步骤依存放于历程中,具体的制作过程也在教程中说明。这里我们只是对教程中录制动画时帧数的选取作以补充说明。由于3D
MAX自动会将指定帧数到调整前帧数或是动画的开始(0帧)之间物体的移动、旋转、变形记录下来。所以一开始我们将帧数改为30,然后移动壶从山底到山顶,这一过程就表现为从0帧到30帧壶从山底飞出。而此后将帧数改为80,将壶拉近屏幕并旋转,就停止录制。我们可以看到飞出山顶的壶又飞近屏幕并转了近180度,在原地停留了一会又重新从山底飞出。<BR><BR>总的来说,动画制作的方法有很多种,而且功能更强的工具软件也一定会层出不穷。只要你有好的创意,就一定会制作出优秀的动画。<BR><BR>DirectDraw是微软新近发行的DirectX3软件开发工具箱(SDK)中的一部份。对那些不甚了解的人来说,DirectX3SDK是原来称为游戏SDK的最新版本。和在游戏SDK中一样,DirectX3SDK包含了一组动态链接库,用来图形加速,3─D图形服务,声音加速,扩展连接,游戏杆操纵以及CD─ROM自动化。<BR>关于DirectX3SDK可以有很多的话题讨论,本篇只涉及如何用DirectDraw来编写游戏中的图形部份。尽管需要一些有关OLE和构件模型(COM)接口的基础知识,整个过程还是相当简单的。而且,所需的有关OLE和COM接口的知识也将在此讨论到。<BR>在这里,我们只是讨论如何使用最基本的DirectDraw函数。所举的例子仅涉及到全屏和翻页,对于如何在窗口中使用DirectDraw,在3维图中使用文字,使用视频剪接或是用Direct3D生成表面不作过多介绍。<BR>DirectX3SDK的基本环境<BR>DirectX3SDK可以在Windows95或WindowsNT4.0中使用。基于本文的目的,假定使用的环境是IBM─PC和Windows95。另外,还需要安装DirectX3SDK,以便编译连接应用。<BR>同时,使用的C或C++编译器应能生成32位的应用。您也可以使用其他的语言,但这里并不想涉及。当然您还应当具有Windows的编程技术。如果使用的是C编译器,则还必须包含Win32SDK。Win32SDK包含了生成可执行程序时的一些库。<BR>DirectDraw,OLE,和COM接口<BR>DirectDraw是围绕着OLE和COM接口来设计的。如果您不熟悉OLE编程,那将很难开始用DirectDraw编程,这是最基本的一点。尽管在很多书上解释了很多概念,介绍OLE和COM接口是如何工作的,但要用好DirectDraw其实只要了解有限的一些。<BR>首先看看OLE和COM的定义。OLE是微软为在不同的进程和机器间共享信息和服务而引进的基于对象的技术。COM指的是构件对象模型,在OLE编程中,它是接口模型。<BR>DirectDraw创建了函数来负责增加引用计数并返回指向接口的指针。但您仍要对没一个隐式创建的接口指针执行Release操作。如果没有维护好对象的引用计数,将很容易引起内存泄露。在后面编码的例子中,将介绍如何进行。<BR>下面看一个使用IDirectDraw接口方法的C程序行。<BR>ddrval=lpDD->lpVtbl->SetDisplayMode(lpDD,ScreenX,ScreenY,ScreenBpp);<BR>程序用了SetDisplayMode方法来设置显示模式,返回值表示成功或失败。在此重要的一点是如何取得指向方法的指针,不能直接访问IDirectDraw接口方法。当一对象实例化时,它生成一个虚函数表,称为vtable,它包含了指向该对象接口方法的所有指针。在上例中,指向DirectDraw对象的指针(lpDD)指向了指向vtable的指针(lpVtbl),vtable中包含了对象所有方法的指针,具体地说,本例中指向了SetDisplayMode方法。应用和方法接口间的联系可以清楚地看成这样:<BR>下一个例子看看如何用C++来做:<BR>ddrval=lpDD->SetDisplayMode(ScreenX,ScreenY,ScreenBpp);<BR>注意vtable不再显式地使用,C++自动地将对象(lpDD)作为第一个参数。不需要this指针,因为C++用指向当前对象的指针(本例中为lpDD)隐式地执行该方法。<BR><BR><BR>在使用DirextDraw时,需要首先创建一个对象DirectDraw的实体,该对象实体代表了微机显示适配器。然后,使用接口所提供的方法来操作该对象实体,使之完成有关命令和任务。接着,你还需要创建一个或多个DirectDraw-surface对象的实体,以便能在图形表面(Surface)上展示你的游戏画面。<BR>下面,在例程DDEX1中展示如何使用Directx3SDK来DirectDraw对象实体,如何创建一个带有后台缓冲区的基本表面(Surface),以及如何弹出表面(Surface)。<BR>注意:所有的例程都是用C++写成的,如果你的编辑器是C,你需要在文件中作出某些改动(至少,你要加入Vtable和指向各种接口方法的this指针)。<BR>DirectDraw初始化:<BR>DirectDraw
初始化代码写在dolnit函数中。<BR>/**CreatethemainDirectDrawobject.*/<BR>ddrval=DirectDrawCreate(NULL,&lpDD,NULL);<BR>if(ddrval==DD_OK)<BR>{kk1}<BR>//Getexclusivemode.<BR>ddrval=lpdd->SetCooperativeLevel(hwnd,<BR>DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN);<BR>if(ddrval==DD_OK)<BR>{kk1}<BR>//Createtheprimarysurfacewith1backbuffer.<BR>ddsd.dwSize=sizeof(ddsd);<BR>ddsd.dwFlags=DDSD_CAPS\DSD_BACKBUFFERCOUNT;<BR>ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|<BR>DDSCAPS_FLIP|<BR>DDSCAPS_COMPLEX;<BR>ddsd.dwBackBufferCount=1;<BR>ddrval=lpDD->CreateSurfae(&ddsd,&lpDDSPrimary,<BR>NULL);<BR>if(ddrval==DD_OK)<BR>{kk1}<BR>//Cetapointertothebackbuffer.<BR>ddscaps.dwCaps=DDSCAPS_BACKBUFFER;<BR>ddrval=lpDDSPrimary->GetAttachedSurface(&ddscaps,&lpDDSBack);<BR>if(ddrval==DD_OK)<BR>{kk1}<BR>//Drawsometext.<BR>if(lpDDSPrimary->GetDC(&hdc)==DD_OK)<BR>{kk1}<BR>SetBkColor(hdc,RGB(0,0,255));<BR>SetTextColor(hdc,RGB(255,255,0));<BR>TextOut(hdc,0,0,sxFrontMsg,lstrlen(szFrontMsg));<BR>lpDDSPrimary->ReleaseDC(hdc);<BR>}<BR>if(lpDDSBack->GetDC(&hdc)==DD_OK)<BR>{kk1}<BR>SetBkColor(hdc,RGB(0,0,255));<BR>SetTextColor(hdc,RGB(255,255,0)):<BR>TexOut(hdc,0,0,szBackMsg,lstrlen(szBackMsg));<BR>lpDDSBack->ReleaseDC(hdc);<BR>}<BR>//Createatimertoflopthepages.<BR>if(SetTimer(hwnd,TIMER_ID,TIMER_RATE,NULL))<BR>{kk1}<BR>returnTRUE;<BR>}}}}}<BR>wsprintf(buf,"DirectDrawInitFailed(%08lx)\n",ddrval);<BR>以下针对初始化DirectDraw对象和准备表面(Surface)集的各个步骤分别进行讨论。<BR><BR>为了创建一个DirecDraw对象实体,你应该在程序中使用DirectDrawCreateAPI函数(注意:这里我所说的是应该,而不是必须),这是因为使用OLE中的CoCreatelnstance函数也能创建一个DirectDraw对象实体,但这不在我们的讨论范围之中)。DirectDrawCreate采用全球统一的标准,它代表显示设备,这些显示设备在大多数情况下被定为NULL(即:系统使用缺省的显示设备)。当DirectDraw对象实体创建好后,就会有一个指针指向该对象实体。而且,在调色板中有三分之一的指针指向NULL(这样做的目的是为了今后的扩展)。<BR>接下来的例子说明如何创建一个DirectDraw对象,并判别该对象是否创建成功:<BR>ddrval=directDrawCreat(NULL,&lpDD,NULL);<BR>if(ddrval==DD_OK)<BR>{kk1}<BR>//lpDDisavalidDirectDrawobject.<BR>}<BR>else<BR>{kk1}<BR>//DirectDrawobjectcouldnotbecreated.<BR>}<BR>使用IDirectDraw2和IDirectDrawSurface2接口<BR>在你读本文的其他部份时,你会注意到所有的例程都使用的是IDirectDraw和IDirectDrawSurface的老版本接口。这是因为DirectX3SDK所给出的例程还没有来及使用IDirectDraw和IDirectDrawSurface更新后的接口。你可以通过调用IDirectDraw::QueryInterface方法来得到IDirectDraw2和IDirectDrawSurface2接口。<BR>下面的代码给出如何得到IDirectDraw2接口:<BR>//CreateanIDirectDraw2interface.<BR>LPDIRECTDRAWlpDD;<BR>ddrval=DirectDrawCreate(NULL,&lpDD,NULL);<BR>if(ddrval!=DD_OK)<BR>return;<BR>ddrval=lpDD->SetCooperativeLevel(hwnd,DDSCL_NORMAL);<BR>if(ddrval!=DD_OK)<BR>return;<BR>ddrval=lpDD->QueryInterfave(IID_IDirectDraw2,(lPVOID*)&lpDD2);<BR>if(ddrval!=DD_OK)<BR>return;<BR>下面的代码给出如何得到IDirectDrawSurface2接口:<BR>LPDIRECTDRAWSURFACElpSurf;<BR>LPDIRECTDRAWSURFACElpSurf2;<BR>//Createsurfaces.<BR>memset(&ddsd,0,sizeof(ddsd));<BR>ddsd.dwSize=sizeof(ddsd);<BR>ddsd.dwFlags=DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT;<BR>ddsd.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN|<BR>DDSCAPS_SYSTEMMEMORY;<BR>ddsd.dwWidth=10;<BR>ddsd.dwHeight=10;<BR>ddrval=lpDD2->CreateSurface(&ddsd,&lpSurf,NULL);<BR>if(ddrval!=DD_OK)<BR>return;<BR>ddrval=lpSurf->QueryInterface(<BR>IID_IDirectDrawSurface2,(LPVOID*(&lpSurf2);<BR>if(ddrval!=DD_OK)<BR>return;<BR>
<P align=center><FONT color=#00ff00><A
href="http://tc2001.myrice.com/index.htm"><FONT
size=5><B>回首页</B></FONT></A></FONT> <A
href="http://tc2001.myrice.com/base7.htm#"><B><FONT color=#ff0000
size=5> TOP</FONT></B></A> <A href="http://tc2001.myrice.com/base1.htm"><FONT
size=5>上一页</FONT></A> <A href="http://tc2001.myrice.com/base3.htm"><FONT
color=#00ff00 size=5>下一页</FONT></A></P>
<P>
</P></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -