这篇文章是帮助你理解xna底层是怎样工作以及它跟DirectX的关系,让你可以脱离Model、Game等等不灵活的类,完全从底层实现一些高级控制

 

驱动游戏运行的dll有两个:

 

Microsoft.Xna.Framework.dll

这是引擎的核心,从某程度上说是对DirectX的封装(也加了一些额外的东西),介绍几个重要的命名空间和类

——Microsoft.Xna.Framework空间

主要提供矩阵、向量等等常用数据结构

——Microsoft.Xna.Framework.GameServices空间

封装Xna服务,特别是跟微软Live服务相关的东西(账号注册、登陆都在这里)

——Microsoft.Xna.Framework.Graphics空间

重点要介绍的东西,这个空间是对Direct3D(包括D3DX,即Direct3D扩展)的封装

先看看几个核心类(也就是D3D系列)

————GraphicsDevice:绝对核心的类,控制显卡的类,在DirectX9对应IDirect3D9Device,掌管所有资源的创建(这也是为何很多类的构造函数要把Deivce作为参数),提供绘制(DrawPrimitive, DrawIndexedPrimitive),控制绘制属性(*State属性)

————Vertex系列:包括VertexBuffer(存储顶点信息的地方,顶点信息包含很多东西,可以是位置、颜色、贴图坐标、法线等等,必须在VertexDeclaration类说明包含哪些东西),IndexBuffer(顶点索引,请百度之),VertexDeclaration(说明VertexBuffer的结构)类

————Texture系列:包括Texture2D, Texture3D等

————Shader系列:包括PixelShader, VertexShader等

然后更高层的(D3DX) ,如果知道这些类是如何实现的,那么对D3D如何使用也就有一个基本了解了,具体可以看后面的例子(或请百度:P或msdn)

————Effect系列:对应D3DX的Effect系列

————SpriteBatch:对应D3DX的Sprite系列

————Model系列:对应D3DX的Mesh系列。

 

 

我们以TextureQuad例子说明Model类内部是怎么工作的(与DirectX是相似的)

http://msdn.microsoft.com/en-us/library/bb464051.aspx

这个例子在3D空间显示一个有贴图的正方形。这个例子不使用Model,SpriteBatch等辅助类,而直接用GraphicsDevice等核心类实现

先看看它的LoadContent函数

Code

 做了几件事:读贴图,创建BasicEffect,然后将View、Project矩阵和贴图等等传给BasicEffect

 

再看看Draw函数

Code

GraphicsDevice.Clear(Color.CornflowerBlue);

首先这行把屏幕清空,以便画新东西。

 

GraphicsDevice.VertexDeclaration = quadVertexDecl;

这个等下说


之后就是一个Effect循环了。

说到这里,到底BasicEffect到底是个什么东西?为什么要为它指定View、Project矩阵和贴图?

在你调用pass.Begin()的瞬间,实际上做了这些事情(不完全伪代码):

GraphicsDevice.PixelShader = pass.pixelShader;

GraphicsDevice.VertexShader = pass.pixelShader;

foreach(effect的每个参数)

     GraphicsDevice.Set*ShaderConstant(....) ;

这样,在你调用pass.End之前画任何东西,出来的贴图、光照还有镜头位置都会跟这个BasicEffect指定的一样。

然后

                GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(

                    PrimitiveType.TriangleList, quad.Vertices, 0, 4, quad.Indexes, 0, 2);

这段代码就如同Model.Draw()一样,将整个正方形的所有顶点画出来。因为有了BasicEffect的作用,所以画出来就有了贴图


 

那么我想做一个复杂点的效果,例如将这个quad变成反射镜,怎么做?

方法就是不用BasicEffect,自己写一个新的Effect。Effect是由.fx文件编译出来的东西,而BasicEffect只是预先定义好了.fx的代码内容。所以只要写一个新的.fx文件就可以做出这种效果

随便找一个.fx文件的例子,发现他们的函数通常要传入POSITION,NORMAL这些类型的参数,这些哪里来?都来自VertexBuffer的数据,然而DirectX如何分清楚那个数字代表POSITION哪个代表NORMAL呢?就靠VertexDeclaration(上面没讲的那行代码)来指定一个顶点由什么组成

至于什么是EffectTechnique, EffectPass, 以及shader的具体做法以后再讨论


顺便说一下D3D和D3DX的关系。D3D是核心的接口,真正跟显卡打交道的地方,通过Device管理IndexBuffer,VertexBuffer,Texture,Shader几大资源以及进行渲染。而显卡厂商要在驱动程序中对这个接口提供实现。

而D3DX是为方便而增加的库,提供了Sprite(画2D图)、Mesh(组合了IndexBuffer,VertexBuffer以及VertexStream,VertexDeclaration等概念)、Font(将字体变成贴图的工具)

也就是说,如果你够牛,你可以自行实现整个D3DX库,因为这个库跟硬件是无关的。


 

终于到下一个dll……

Microsoft.Xna.Framework.Game.dll

这是引擎的非核心内容,针对Windows平台而言主要封装了Windows窗口、Windows计时、DirectX管理、Xna服务管理等

 

由此可见,实际上Xna可以用于游戏以外的应用中,例如编写Windows程序,可以名副其实的当做.Net版本的DirectX使用(难怪微软停止了DirectX Managed的开发……)。因为平时我们使用Xna都是按照Microsoft.Xna.Framework.Game.dll中的程序模型(也就是Game类)来编写程序的,所以如何使用GraphicsDevice类以及窗口信息循环等等细节都隐藏了。

 

在我们创建Game类并执行Game.Run()后,通常我们需要在Game.Initialize里创建一个GraphicsDeviceManager(简称GDM)。Game会创建一个Window,然后GDM会从Game(注意GDM的构造函数有Game参数)获取这个Window的句柄,然后传给GraphicsDevice的构造函数,这样GraphicsDevice就构造成功,而且DirectX会在这个窗口上绘制(因为传了窗口的句柄)。

然后在窗口的消息循环中会调用我们在Game类中非常熟悉的Update和Draw函数。

你可以看看DXUT(包含在DirectX SDK中,但是这个不是DirectX的部分),你就发现Microsoft.Xna.Framework.Game.dll就相当于DXUT的C#版本,解决了DirectX跟Windows之间的交互问题。

 

理解了Game类的原理,就可以脱离这个dll,自己控制程序跟DirectX的交互。具体如何实现可以参考xna的Windows Form系列例子。

 

建议大家下载DirectX SDK(或者看msdn),将整个Direct3D库的构造跟Xna的Graphics空间对比一下,然后再对比一下DirectX程序和xna windows form例子,你会发现xna和DirectX是如此相似。

另外可以阅读OGRE的源码(我会考虑写一篇分析)

 

限于篇幅,更多请baidu或msdn~

posted on 2009-03-21 14:08  中大信息中心数媒部  阅读(2462)  评论(3)    收藏  举报