`
andyjackson
  • 浏览: 58353 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

XNA学习笔记5-顶点绘制

阅读更多
      上一节讨论到了model的组成,一个模型使用无数个ModelMeshPart组成,通过bone将这么messpart按照树形结构组合在一起。
      Primitives (图元)是可以被XNA绘制的最基本的对象,最常被使用的图元是三角形。你想定义一个3D几何结构并绘制到屏幕上,这个结构可以是由三角形,线或点组成。ModelContent中存储几何结构的是一个个MeshContent,该对象中存储了其几何结构。我个人认为,model的几何组成最终是有IndexBuffer这个索引集合和vertextBuffer共同组成。因此,除了可以利用其他软件,通过内容管道处理加载得到model,形成3D图形之外,同样可以在程序中,自己通过Device的DrawUserPrimitives(刻画图元),直接往设备中输入3D结构所需的数据,刻画在屏幕上,本节就主要讲述一下通过代码来描绘3D图形。
      VertexPositionColor。定义顶点的坐标和颜色,将它们存储在一个VertexPositionColor数组中,然后利用DrawUserPrimitives方法进行绘制。
    vertices = new VertexPositionColor[3]; 
    vertices[0] = new VertexPositionColor(new Vector3(-5, 1, 1), Color.Red); 
    vertices[1] = new VertexPositionColor(new Vector3(-5, 5, 1), Color.Green); 
    vertices[2] = new VertexPositionColor(new Vector3(-3, 1, 1), Color.Blue);
DrawUserPrimitives的第一个参数是PrimitiveType的枚举类型,用来表示如何进行刻画这些点。
  • TriangleList:要绘制的每个三角形的每个顶点。
  • TriangleStrip:相对于TriangleList可以节省相邻三角形公用的顶点,只需要n+2长度的数组。但是,当使用TriangleStrip以逆时针方向定义三角形时,不可能遵循剔除规则。要解决这个问题,对于TriangleStrip 来说剔除有些特别,必须按照顺时针、逆时针交替循环的方式定义。
  • LineList:用于绘制多条线段。
  • TriangleFan:用扇形结构绘制多个三角形,适合所有三角形公用同一个顶点的情况。
      VertexPositionTexture。指定顶点坐标和对应纹理的位置,存储在VertexPositionTexture的数组中,并通过DrawUserPrimitives方法进行绘制。
    vertices = new VertexPositionTexture[9]; 
    int i = 0;
    vertices[i++] = new VertexPositionTexture(
         new Vector3(4, 1, -5), new Vector2(0.25f, 0.5f)); 
    vertices[i++] = new VertexPositionTexture(
         new Vector3(7, 5, -5), new Vector2(0.5f, 0)); 
    vertices[i++] = new VertexPositionTexture(
         new Vector3(10, 1, -5), new Vector2(1, 1));     
    myVertexDeclaration=new VertexDeclaration(
         device, VertexPositionTexture.VertexElements);
.......
    basicEffect.Begin(); 
    foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) 
    {
         pass.Begin();
         device.VertexDeclaration=myVertexDeclaration; 
         device.DrawUserPrimitives<VertexPositionTexture>(
              PrimitiveType.TriangleList, vertices, 0, 3); 
         pass.End(); 
     }
     basicEffect.End();  
除了三角形的绘制模式以外,还有纹理取样方式。当指定纹理坐标时,要记住纹理的左上为坐标(0,0),右上为(1,0)。这意味着第二个坐标值表示垂直位置,所以右下坐标为(1,1)。但这并没有限定纹理坐标的值必须在[0,1]范围内,例如设定为(1.5f, –0.5f)也可以。在这种情况中,你需要通过设置U和V(U是第一个纹理坐标,V是第二个)的纹理模式让XNA知道如何处理这样的坐标。
device.SamplerStates[0].AddressU = TextureAddressMode.Clamp; 
device.SamplerStates[0].AddressV = TextureAddressMode.Clamp; 
TextureAddressMode有多种取值:
  • Clamp。这个寻址模式让所有的纹理坐标截取到[0,1]范围内,所有小于0的坐标截取到0,大于1的截取到1。
  • Wrap。显卡会从坐标加上或减去1直到坐标仍回到[0,1]范围。
  • Mirror。当坐标值小于0时,将值不断地以比他大的最小整数做轴对称变换;当坐标值大于1时,将值不断地以比他小的最大整数做轴对称变换。直到坐标值在[0,1]的范围。
  • MirrorOnce。这个模式将[-1,1] 区域内的纹理坐标镜像到[0,1]区间,而所有超出[-1,1]区间的坐标会截取到–1(小于–1的值)或1(大于1的值)。

      使用索引移除冗余顶点。通常绘制图形的数组中,有许多冗余顶点数据,在GPU与CPU之间传递,很浪费时间。好的办法是将独立顶点存储在数组中并将这个数组传递到显卡。然后在绘制图形数组中,存储顶点数组的索引,这可以节省显存和带宽。
/*indices为索引数组*/
device.DrawUserIndexedPrimitives<VertexPositionColor>(
PrimitiveType.TriangleList, vertices, 0, 9, indices, 0, 8); 
使用索引不见得都能优化性能,所以在使用索引前,你应该首先不使用索引绘制三角形。有些情况中使用索引反而会使性能降低。例如,有五个三角形并不共享一个顶点。不使用索引,你需要使用15个顶点,而使用索引,你仍要定义15个顶点,因为这些顶点是独立的!而且还要定义15个索引,每个索引对应一个顶点,这种情况下,传递到显卡的数据反而变多了!作为一个规律,你可以将(独立顶点的数量) 除以(三角形的数量)。如果没有共享顶点,那么这个值是3,如果有共享,这个值会小于3。这个值越小,,使用索引提升的性能就越多,表示使用索引可以极大地提升性能。
      DrawPrimitives每次当你调用DrawUserPrimitives方法时,顶点都会从系统内存传递到显卡中。通常,大部分数据没有变化,这意味着每帧重复传递了相同的数据。今天的显卡有容量很大而且很快的显存,所以你可以将顶点数据存储在显存中加速程序。从显存中将顶点数据传递到显卡速度要快得多,因为这些数据只在同一板卡的不同芯片间传输。同样索引数据也能获得加速。通过创建顶点数组的VertexBuffer,你可以将顶点数据复制到显存中。将顶点存储到显存后,就可以调用DrawPrimitives方法 (代替DrawUserPrimitives方法),这个方法从更快的显存中获取顶点。
    vertBuffer = new VertexBuffer(device, 
        VertexPositionTexture.SizeInBytes * vertices.Length,
        BufferUsage.WriteOnly);
    vertBuffer.SetData(vertices, 0, vertices.Length); 
........
    device.Vertices[0].SetSource(vertBuffer, 0,
        VertexPositionTexture.SizeInBytes); 
    device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);
VertexBuffer的构造函数可以在最后一个参数中指定一些有用的可选择的标志,显卡的驱动会使用这个标志决定哪种内存是最快的。下面是可以使用的BufferUsages:
  • BufferUsage. None:允许从VertexBuffer中读写。
  • BufferUsage. Points:表示VertexBuffer中的顶点数据是用来绘制点和精灵的。这个与性能无关。
  • BufferUsage. WriteOnly:不从VertexBuffer读取数据。当使用VertexBuffer时,可以将顶点数据放在快得多的显存中。这样,当你想处理顶点数据时,比起将顶点数据在系统内存中存储一个副本,调用VertexBuffers的GetData方法往往更好。
      如果你还想将索引存储在显卡中,你应在创建VertexBuffer之后再创建一个IndexBuffer,创建IndexBuffer时,你需要指定索引的类型,类型可以是ints或shorts,还要指定索引的多少。一些低端显卡只支持16-bit的索引,如果你使用32-bit的整数型索引会出错。要解决这个问题你应将索引存储在一个short数组中,指定创建一个short类型的索引缓冲。如果你的数组包含不超过32,768的索引,你应该使用shorts而不是ints。这样索引缓冲可以节省一半内存。
    indexBuffer = new IndexBuffer(device, typeof(int), 
        indices.Length, BufferUsage.WriteOnly); 
    indexBuffer.SetData<int>(indices); 
    //.....
    device.Vertices[0].SetSource(
        vertBuffer, 0, VertexPositionTexture.SizeInBytes); 
    device.Indices = indexBuffer; 
    device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 5, 0, 2); 
      如果你计划频繁更新顶点数据,应该使用DynamicVertexBuffer而不是VertexBuffer。 这会让数据不是存储在最快的显存中,而是更容易处理的某些内存中。所以,这样做会让性能有一点降低,但在VertexBuffer中频繁地改变数据会让性能得到极大地提高。
      因为当你想频繁更新数据时才会使用DynamicVertexBuffer,所以你会经常使用SetData 方法。因此DynamicVertexBuffer的SetData方法还接受一个额外的参数SetDataOptions。这个参数让你可以指定一些选项可以提高程序的速度。默认情况下,当你想覆盖显存中的内容时,因为不支持同时读写,显卡无法从显存中读取数据。当你把大量数据写至内存时会导致显卡的绘制过程中止,这是因为显卡要等待你的复制过程结束。但是,有两个方法可以让你确保显卡不会等待复制操作结束。你可以使用SetDataOptions 参数,下面是可选项:
  • SetDataOptions. None:这个选项可以完全控制覆盖VertexBuffer 哪一部分。但是,如前面解释的那样,这会导致性能降低。如果显卡绘制的数据是从VertexBuffer 之前内容提取的,那么显卡必须停止绘制直到较慢的复制过程结束。
  • SetDataOptions. Discard:使用这个选项表示你不再需要VertexBuffer的之前的内容。这时数据存储在显存的一个新的位置。在写入过程发生时,显卡还能继续使用老的数据。一旦写入过程完成,显卡可以使用新的数据绘制而抛弃老数据。简而言之,显卡无需等待,但你必须重写所有数据。(在Xbox平台上无法使用这个选项,你只能调用DrawUserIndexedPrimitive绘制频繁更新的顶点。)
  • SetDataOptions . NoOverwrite:这个选项很强大但比较危险。你必须保证正在覆盖的VertexBuffer部分没有被渲染过程使用。这样,你可以覆盖VertexBuffer 的特定部位,此时显卡无需等待复制过程结束,因为你正在覆盖的部分不参与绘制过程。这比使用Discard选项快,因为你无需保留在内存中保留一块新的部分。
下面,讨论通过顶点绘制的一些其他的话题。
开启Backface Culling(背面剔除)
      你定义三角形顶点的方式告知XNA你想让哪一面朝向相机。当绘制一个实心对象时,人们可以清楚地说出三角形的哪个面在物体的内部还是外部。要绘制必须的三角形,你可以要求XNA只绘制朝向相机的那个面,其他三角形在物体的内部被前面隐藏!但是对计算机来说事情不是那么简单。对你定义的每个三角形,你需要说明三角形是在外面还是里面,这可以通过沿逆时针方向或顺时针方向定义三角形的顶点做到这点。当定义顶点时,你需要考虑相机的位置。要绘制三角形,顶点的顺序必须以相机看来按顺时针方向递增。你可以想象一条从相机出发指向三角形中心的射线,如图下所示。如果顶点的顺序(从相机看过来!)是绕着这个中心点顺时针旋转的(左图),XNA就认为这个三角形是面向相机的,这样,三角形才会被绘制。
      下图的左图显示了一根从相机出发的射线并与两个三角形相交,圆点表示射线和前面的三角形的交点。当你沿着前面的三角形的顶点增加的顺序(从0到1到2)前进时,会发现绕着圆点做顺时针旋转,这时XNA会绘制这个三角形。现在看一下射线与后面的三角形的交点,当你沿着顶点增加的顺序(从6到7到8)前进时,会绕逆时针旋转,这时XNA会将这个三角形剔除,这样处理很好,因为这个三角形位于立方体的后方,被前表面隐藏了。图5-12的右图显示了同样的立方体,但旋转了180度,这样前表面和后表面就互换了位置,相机仍位于页面的同一侧。现在如果顺着顶点6到7到8,将绕视线做顺时针旋转,这次,显卡会绘制这个三角形并剔除另一个!这正是我们想要的结果。通过这种方式,显卡知道应该剔除哪些三角形,剔除那些不朝向相机的三角形可以极大地提高程序的性能,这也是默认激活的。
      虽然当绘制由实心表面组成的对象时使用剔除可以带来极大地好处,但有时你也需要将提出关闭。例如,你想创建一个只由两个三角形组成的长方形墙,从外部看效果不错,但当你进入到建筑物内部,这面墙会被剔除,导致你可以看穿这堵墙!简单的方法是使用下列代码将剔除关闭:
device.RenderState.CullMode = CullMode.None; 

计算顶点缓冲中所有顶点的法线
      当绘制自定义的结构时,你会发现光照不正确。这是因为你没有指定正确的法线向量,显卡要求每个顶点都有法线信息,这样它才可以决定每个三角形获得多少光照。如果每个顶点只被一个三角形使用,你只需找到三角形的法线向量(换句话说,这个向量垂直于三角形)并将这个向量作为三个顶点的法线向量。但是在一个结构中,所有顶点被几个三角形共享。要获取平滑的效果,每个顶点需要存储周围三角形所有法线的平均值。
      一般情况通过叉乘计算三角形的法线,步骤如下:
  • 对于结构中的每个三角形,计算法线向量。
  • 将这个向量添加到三角形的三个顶点的法线中。对所有三角形进行这个操作后,执行以下操作:
  • 归一化结构中的每个顶点的法线向量。














  • 大小: 12.3 KB
  • 大小: 36.8 KB
  • 大小: 41 KB
分享到:
评论
1 楼 w657284862 2011-10-17  
博主看的那本书啊,能告知一下嘛。你都理解很好嘛。

相关推荐

    XNA4.0学习指南--源代码

    5. **SoundEffect和Music**:XNA提供了处理音频的类,如SoundEffect用于短促音效,Song用于背景音乐。开发者可以轻松地播放、暂停、停止和调整音量。 6. **Model Class**:用于加载和管理3D模型。XNA支持Collada (....

    XNA Collision Series 1 - 2D Rectangle Collision

    XNA Tutorial Collision Series 1 - 2D Rectangle Collision

    XNA学习资料-XNA入门指南2.0

    5. **游戏对象和精灵(Sprite)**:XNA中的Sprite类是处理2D图形的核心,可以用来显示图片、动画等。开发者需要学会如何创建和管理游戏对象。 6. **声音和音乐**:XNA提供了处理音频的能力,可以播放音乐和音效,包括...

    XNA学习资料,XNA

    5. **Game类和GameComponents**:介绍XNA中的Game类,它是所有XNA游戏的基础,以及GameComponent类,它是实现游戏逻辑和渲染的基石。 6. **图形渲染**:讲解如何使用SpriteBatch和Texture2D进行2D图形绘制,包括...

    XNA入门指南-第一章

    #### 三、学习XNA所需技能 - **C#编程基础**:虽然本书不是编程入门书籍,但对于C#有一定的了解是非常必要的。C#是XNA开发的主要语言,熟悉C#语法和编程技巧对于理解XNA框架至关重要。 - **数学基础知识**:游戏...

    XNA学习指南_中文_附源代码[XNA学习必备]

    **XNA学习指南详解** XNA,全称为Xbox Next Generation APIs,是由微软推出的一款用于游戏开发的框架,特别适合初学者和有经验的游戏开发者。它提供了完整的工具集,包括编程环境、图形库和音频处理功能,使得创建...

    XNA代码绘制3D对象

    本文将深入探讨如何使用XNA框架通过纯代码来绘制3D对象,从而帮助你理解3D图形编程的基本原理。 首先,XNA框架基于DirectX,它提供了丰富的图形处理功能。要开始绘制3D对象,你需要了解几个关键概念: 1. **坐标...

    2D XNA Primitive Shapes Library-开源

    开发团队已经将项目的维护移至Bitbucket平台(https://bitbucket.org/C3/2d-xna-primitives),这意味着社区可以更方便地参与贡献,修复问题,或者添加新功能,进一步增强了这个库的生命力和适应性。 在实际应用中...

    XNA游戏开发->国际象棋源码

    XNA利用C#编程语言,结合DirectX图形库,使得游戏开发更加容易上手,尤其对于初学者来说是个很好的学习平台。 【国际象棋】是一种双人对弈的策略棋类游戏,起源于6世纪的印度,后来在欧洲发展并逐渐演变成现代的...

    xna program recipes

    通过以上知识点的学习,不仅可以掌握XNA的基本使用方法,还能深入了解游戏开发的核心技术,如2D/3D渲染、粒子系统、模型动画等。对于初学者而言,这些知识是非常宝贵的,可以帮助他们快速上手并逐步深入游戏开发领域...

    XNA代码绘制不同面得几何图形

    对于3D几何图形,XNA提供了`Model`和`Mesh`类,但如果我们想控制每个面的颜色,那么直接使用顶点数据和索引数据进行绘制更为灵活。我们可以创建一个`VertexBuffer`来存储顶点信息,包括位置、法线和颜色,然后创建一...

    xna编程资料,自己整理的 xna shader

    XNA Shader编程是现代游戏开发的重要组成部分,通过对HLSL的学习和实践,开发者可以掌握复杂的光照算法、纹理映射等技术,从而实现高质量的视觉效果。本文档提供了从基础知识到具体应用场景的全面指南,帮助读者快速...

    Windows Phone开发-XNA基础

    1. **易用性**:XNA采用C#语言和.NET框架进行开发,这让很多开发者能够快速上手,无需深入学习复杂的C/C++特性。 2. **跨平台性**:XNA具有良好的跨平台支持能力,开发者可以在不同平台上轻松移植游戏,无需大量修改...

    XNA 游戏编程学习总结2D与3D实现

    在“XNA游戏编程学习总结2D与3D实现-2D源码”中,我们可以深入探讨XNA在2D游戏开发中的应用。 1. **XNA框架基础** - XNA框架包含了Game类:它是所有游戏项目的基础,包含了游戏循环、渲染、更新等核心功能。 - ...

    Xna将图形绘制到WinForm控件

    标题中的“Xna将图形绘制到WinForm控件”指的是使用微软的XNA框架在Windows Forms应用程序中渲染图形。XNA是一个为游戏开发设计的框架,但也可以用于其他图形编程需求。这个主题涉及到的主要知识点包括: 1. **XNA...

    XNA文字绘制-附件资源

    XNA文字绘制-附件资源

    XNA4.0GameDevelopmentByExample-英文原版.zip

    2. **图形和渲染**:讲解如何使用XNA处理2D和3D图形,包括精灵、纹理、模型加载和绘制,以及基本的矩阵变换和光照效果。 3. **音频处理**:介绍如何播放音乐和音效,如使用SoundEffect和MusicPlayer类。 4. **输入...

Global site tag (gtag.js) - Google Analytics