ITPub博客

首页 > Linux操作系统 > Linux操作系统 > RacingGame学习笔记4——基础图形部分3

RacingGame学习笔记4——基础图形部分3

原创 Linux操作系统 作者:思月行云 时间:2010-10-13 17:40:59 0 删除 编辑

文件五:LineManager2D.cs

看完了几个2D的辅助绘制类后,让我们开始分析Graphics目录中的几个3D辅助绘制类。我们将依次浏览LineManager2D、LineManager3D、PlaneRenderer、Material、Model以及MeshRenderManager类。让我们从最容易理解的LineManager2D开始。

这个类的任务就是在屏幕中画2D线。如果查找对这个类的引用的话,最终你会发现当前的代码中并没有用到该类。Benjamin在Professional XNA Game Programing一书中写到了这个类以及LineManager3D类。他说写这两个类的目的是为了在导入2D或3D资源时绘制辅助线以便更好的观察和定位。(比如增强透视感。)而当前的源代码中是没有包好测试时的代码的。尽管如此,我们从这两个类中可以学到在XNA中绘制辅助线的方法,还是很有好处的。

由于当前的XNAFramework中对画线并没有提供直接的支持,(当然这样做也是有理由的。)我们通常只能通过两种方式画线,第一种是采用XNA的2D图形接口,也就是使用SpriteBatch和Texture2D。使用一个长条形的贴图,实际绘制时计算目标矩形的长宽和位置以及旋转量,(如果是3D直线的话就要用变换矩形变换到平面上。)然后按计算的结果在屏幕中绘制出来。另一种方式就是这里采用的,使用XNA的3D接口,创建顶点缓冲区,设置管道线参数,然后调用Device.DrawUserPrimitives来绘制。这两种方法在外观风格和效率上都有一定区别,实际使用中可以适当选择一种方法。

让我们来看类中的几个成员。首先,Variables区间中定义了一个名称为Line的结构,跟前一节LensFlare中的FlareData类似,在这里用来储存将要绘制的直线的位置,颜色信息。同时,在Line的定义的上方,声明了一个List实例lines。再看看一个公有函数AddLine。该函数根据输入的直线信息创建Line类的新实例,并添加到lines中。(函数中最初判断已有的直线数量是否已经达到允许的最大数量了,之所以有最大数量的限制是因为顶点缓冲区的大小是已经确定了的。)以上便是注册一条2D直线的全过程,下面我们来看看绘制部分。

定位到Render函数。函数的第一部分是检查是否要更新顶点缓冲区。如果需要,调用UpdateVertexBuffer函数。这个函数的作用便是将我们抽象出来的直线信息转换为顶点缓冲区中的实际顶点了。让我们看看这个函数中的代码。

我们主要关注UpdateVertexBuffer函数中的那个for循环。循环遍历lines中的每一条直线。在循环中,将当前直线的位置和颜色信息填充到相应的顶点缓冲区的下标位置中。注意顶点的格式是VertexPosition。循环结束后,重新设置了numOfPrimitives和buildVertexBuffer的值,表示更新的完成。

接着回到Render函数中。只在numOfPrimitives(也就是已添加的直线数量)大于零的时候进行绘制。绘制的第一步实际上是设置管道线参数。可以看见程序中将一些常用的设置封装到了BaseGame类中。接下来有这样的一条语句:

ShaderEffect.lineRendering.Render(

       "LineRendering2D",

       delegate

       {

              BaseGame.Device.VertexDeclaration = decl;

              BaseGame.Device.DrawUserPrimitives(

              PrimitiveType.LineList, lineVertices, 0, numOfPrimitives );

       } );

涉及到我们尚未分析的一个类——ShaderEffect。我们将在以后介绍Shaders目录的时候专门介绍。现在仅需要知道匿名方法(anonymous method)中的绘制过程会使用ShaderEffect中的lineRendering特效(effect)来进行。(如果你还不知道匿名方法的话,快查查MSDN吧。他可是C#2.0中一个激动人心的新特征。)在这里的匿名方法中,将刚才更新的顶点缓冲区传入Device.DrawUserPrimitives方法中。而这个匿名方法又作为ShaderEffect.lineRendering.Render的参数传入。该函数会在准备好lineRendering特效后运行匿名方法中的代码。接下来的绘制工作就都交给显卡的渲染管道线了。

 

文件六:LineManager3D.cs

这是LineManager2D的3D版本。两者异常相似。也就不再在此重复介绍了。

 

文件七:TangentVertex.cs

在介绍更多的3D内容之前,我们必须先介绍两个在飞车游戏中应用广泛的类型。其一就是当前将要叙述的TangentVertex类,其二便是要在下一个文件中介绍的对物体材质进行抽象的Material类。

在飞车的特效系统中,使用了当前游戏中十分流行的法向凹凸贴图(Normal Texture)。法向凹凸贴图是一种以较低运算代价换来较好视觉效果的渲染技术。读者或许知道物体上一点的漫反射光强是由该点的表面法向量和光源方向的点积决定的,而3D模型一般是由顶点信息组成的,对面中任意一点的法向量一般都是通过插值得到(Phong方法或是Gourand方法),所以不可能对法向量进行更细节的描述。但是如果用一张贴图来存储当前表面每个点上的实际法向量与插值得到的法向量的偏移的话,我们就可以在渲染时先依据该贴图计算出实际法向量,然后再用此实际法向量与光源方向点积计算漫反射光强时。

以上是对法向凹凸贴图原理的简单描述。当我们深入思考计算过程的时候,我们就会知道当前这个TangentVertex类存在的意义。

如上所述,这个凹凸贴图中每一点的像素值能够表示实际法向量与模型法向量的偏移,实际上,像素值中的三个通道储存的是一个指向实际法向的向量,但这个向量所在的坐标系显然必须入3D模型的表面方向有关。想像有一只铅笔,笔尾跟桌面接触,笔的方向坐标是相对桌面而言的。即使桌面在转动,由笔来表示的桌面实际法向量依然是正确的。所以如果要准确描述实际法向量的世界方向,还需要在3D模型的表面建立一个坐标系来定位贴图中的向量位置。这个坐标空间通常被切空间(tangent space)。

确定切空间至少需要两个正交向量。其中之一是模型顶点的法向量,另一个则是将会在该类中看到的模型顶点的切向量。这两者作为切空间坐标系的两个轴,而确定第三个坐标轴的向量则是由两者的叉积得到,通常被称为副法向量。(一般法向量作为Z轴,另两个坐标轴的确定和左右手系的选择要由小组中的3D美工、2D美工和技术美工共同协商决定。飞车游戏中模型的切向量表示Y轴,使用右手系。)不在模型顶点的表面点的切空间则由插值得到的法向量和切向量按同样的方法得到。

于是我们便可以理解整个渲染的过程:从模型的顶点信息中得到模型表面每一个点的切空间;从凹凸贴图中获得对应点的实际实际法向量在切空间中的坐标;将这个坐标转换到世界坐标系中;再与光源方向向量点积得到一个决定漫反射光强的值;最后与其他表面信息混合得出该点的实际颜色值。

理论上的过程清楚了之后,让我们看看这个过程中众多环节中的一环——顶点的定义。

在TangentVertex类的Variables区间的开头,便定义了模型顶点应包含的信息。他们是:

pos:顶点的3D位置。

uv:贴图坐标。

normal:顶点法向量。

tangent:顶点切向量。

除了格式的定义外,我们还需要将这个格式的定义表达成一种通用的形式,用来在整个XNA框架中使用。这便是接下来在Generate vertex declaration中定义的VertexDeclaration和VertexElements。在GenerateVertexElements()函数中,初始化了一个VertexElement的数组,数组按类中的声明顺序解释了各变量的含义。而VertexDeclaration则根据VertexElements构建,在渲染使用该定点定义的物体的时传给Device。另外,正如VertexElements定义处的注释中所说,调用Mesh.Clone方法也需要传入VertexElements参数。

这个文件是自定义顶点类型的一个很好的示例。我们将在接下来的很多文件中见到对该顶点类型的使用。

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/21146222/viewspace-675858/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2009-03-18

  • 博文量
    49
  • 访问量
    67014