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

XNA学习笔记4-model

阅读更多
      接着“XNA学习笔记2”中的内容管道说。图片文件、字体文件、.x/.fbx和.fx文件经过内容管道的导入编译等工作,最后通过ContentManager对象的Load方法得到texture、spritefont、model和effect对象。
      纹理(或shader)、模型和渲染的关系可以用一个形象的比喻。现在是一个立方体,立方体是模型;渲染就是对立方体的每个部分(这里就想做是每个面,稍后会介绍他的定义)如何添加纹理、颜色、阴影等效果;而纹理就是每个渲染用到的纹理。
      第二章讲到了简单2D的纹理是通过SpriteBatch类来处理,这个类仅处理2D的简单纹理,并且没有涉及到模型的概念。
      现在讨论一下3D的模型的一些知识。
myModel = Content.Load<Model>("tank");
      在大多数情况下,你可以旋转和移动模型的一部分,例如,移动人的手臂。要能实现这个功能,模型应该要被分成几个成员。对每个成员,你需要知道两件事:几何数据:你想知道顶点,顶点包含了组成模型成员的所有三角形顶点的信息,这些信息包括位置,颜色,法线等。成员与它们的父成员如何连接:以人的手臂为例,你要指定手臂是连接在肩膀上的。
      每个成员的几何数据以ModelMesh对象的形式存储,而ModelMesh对象是在Model的 ModelMeshCollection中,在这个对象你可以找到它的Meshes属性。成员的位置数据以Bone对象的形式存储,而Bone对象在Model的ModelBoneCollection中,在这个对象中你可以找到模型的Bones集合。每个ModelMesh对象包含指向Bone对象的引用,而一个Bone对象包含指向父Bone的引用,它必须连接到这个父Bone。通过这种方式,你可以将所有Bone对象连接到一起。
      ModelMeshes和Bones。一个ModelMesh包含模型的一个成员的几何信息,这个成员无法再分成更小的成员。例如,一台笔记本电脑不是一个好的ModelMesh,因为你想翻起/关闭液晶屏,打开/关闭DVD托盘。好的办法是对电脑底座使用一个ModelMesh,液晶屏使用第二个ModelMeshcreen,而DVD托盘使用第三个ModelMesh。因为所有的ModelMesh都需要他连接到一个Bone上,下一步你的三个ModelMeshes还需要三个Bone。你需要将连接到电脑底座的ModelMesh的Bone作为root Bone,因为电脑底座可以看成电脑的初始位置。连接到液晶屏ModelMesh的表示与底座连接的位置。同理连接DVD托盘的ModelMesh的Bone也要指向root Bone,表示托盘相对于底座的位置。
      ModelMeshes和ModelMeshParts。你已经为液晶屏定义了一个ModelMesh和对应的Bone,这样很完美,因为液晶屏不包含可动部分。但是,你可能还想使用一个固定纹理的effect绘制液晶屏的塑料外壳,使用另一个effect绘制LCD,例如,这个效果会从另一张纹理采样颜色或你还想添加一点反光效果。这就需要用到ModelMeshParts。每个ModelMesh可以包含多个ModelMeshParts,每个ModelMeshParts可以使用不同的纹理,材质或effect进行绘制。这意味着每个ModelMeshPart 都包含各自的几何数据和对应几何数据的effect。

实现model整体或局部的移动、旋转和缩放的功能
      将这部分之前先介绍两个函数:CopyAbsoluteBoneTransformsTo:将模型中的每个bone相对于root的变换信息存储在数组参数中;CopyBoneTransformsTo:将模型中的每个bone相对于其parent bone的变换信息存储在数组参数中。由于在对某个mesh或者part进行变换的时候,同时会对mesh对应的bone的child进行相同的变换。思考:以坦克的火炮为例,火炮的Bone矩阵包含一个诸如(0,0,-2)的偏移量:相对于它的父:炮塔向前移动2个单位。如果你只是简单地将火炮的世界矩阵设置为火炮ModelMesh的矩阵,这会导致火炮ModelMesh被绘制到相对于3D空间的初始位置(0,0,0)偏离(0,0,-2)的地方。但这不是你想要的结果!你实际是想将火炮放置到相对于它的parent:炮塔偏离(0,0,-2)的位置。这个时候会发现CopyAbsoluteBoneTransformsTo方法的重要作用。
      你可以简单地使用XNA框架提供的基本方法创建一个变换矩阵:
Matrix.CreateTranslation 
Matrix.CreateScale 
Matrix.CreateRotationX-Y-Z 
第一个方法创建一个平移矩阵,你可以定义将模型沿着X、Y和Z方向移动的距离。第二个方法让你可以缩放模型,第三个方法返回绕X、Y和Z轴旋转的矩阵。
     
myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
foreach (ModelMesh mesh in myModel.Meshes) 
{
    foreach (BasicEffect effect in mesh.Effects) 
    { 
        effect.EnableDefaultLighting(); 
        effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix; 
        effect.View = fpsCam.GetViewMatrix(); 
        effect.Projection = fpsCam.GetProjectionMatrix(); 
    }
    mesh.Draw(); 
} 
只需要对上面的方法进行组合(*)实现一些复杂的变换。
      矩阵乘法的顺序。在矩阵数学中,将矩阵M1乘以矩阵M2的结果通常与将M2乘以M1的结果是不同的。有两个规则(或者说是技巧)你必须记得:在矩阵乘法中,M1*M2就是“M1在M2之后”的意思;使用I.S.R.O.T.作为矩阵组合的顺序通常就是你想要的结果,此处I.S.R.O.T. 分别代表Identity,Scale,Revolve,Orbit,Translate。

碰撞检测
      你想检测两个模型是否发生碰撞。如果在场景中有许多模型,你将无法进行一个精确的逐三角形的碰撞检测。你想使用一个快速检测方法并在以后进行一个更好的碰撞检测。当进行碰撞检测时,你会发现常常需要在速度和精确度之间进行衡量。在大多数情况中,你会进行组合检测。在第一轮检测中,使用快速检测遍历所有对象,之后对快速检测中可能发生碰撞的物体进行精确检测。有两个方法处理两个模型间的碰撞检测。快速检测方法是找到模型的全局包围球,并通过调用一个包围球的Intersect 方法检测是否相交。你可以改进这个方法以增加精确度。由几个ModelMeshes组成的模型存储了模型不同部分的几何信息。每个ModelMeshes都能生成各自的包围球,所以你可以检测第一个模型和第二个模型的每个包围球。显然,这样做提高了精度但计算量也随之加大。
      快速检测。这个方法使用整个模型的包围球。如果这两个模型的包围球相交,则它们可能发生了碰撞。在某些情况下,这个方法会导致糟糕的结果,例如在滑板游戏中,当你想检测两个玩家的滑板的碰撞时,滑板的包围球相对于滑板本身大得多,滑板的体积不到包围球体积的百分之一,这个方法将会对全局包围球中的所有物体(可能包含其他滑板!)进行检测。但是,这个方法在进行第一次检测时用得还是很多的,因为它的速度足够快。

      你可以访问模型中的每个ModelMesh的包围球。使用CreateMerged方法,你可以将这个包围球组合起来,获取包围整个模型的包围球。但是,因为每个ModelMesh的包围球是相对于Bone矩阵定义的,所以你需要进行转换。你将创建一个方法将一个素材加载到一个模型变量中,初始化它的Bone矩阵数组,并将全局包围球保存到模型的Tag属性中。
private Model LoadModelWithBoundingSphere(ref Matrix[] modelTransforms, 
    string asset, ContentManager content) 
{
    Model newModel = content.Load<Model>(asset); 
    modelTransforms = new Matrix[newModel.Bones.Count]; 
    newModel.CopyAbsoluteBoneTransformsTo(modelTransforms);   
    BoundingSphere completeBoundingSphere = new BoundingSphere();     
    foreach (ModelMesh mesh in newModel.Meshes)
    {
        BoundingSphere origMeshSphere = mesh.BoundingSphere; 
        BoundingSphere transMeshSphere = XNAUtils.TransformBoundingSphere(
origMeshSphere, modelTransforms[mesh.ParentBone.Index]); 
        completeBoundingSphere = BoundingSphere.CreateMerged(
completeBoundingSphere, transMeshSphere); 
    }    
    newModel.Tag = completeBoundingSphere;     
    return newModel; 
}
      要检测两个模型间的碰撞,你需要通过世界矩阵变换每个包围球并检查变换过的包围球是否相交。
private bool CoarseCheck(Model model1, Matrix world1, Model model2, Matrix world2)
{
    BoundingSphere origSphere1 = (BoundingSphere)model1.Tag; 
    BoundingSphere sphere1= XNAUtils.TransformBoundingSphere(origSphere1, world1); 
    BoundingSphere origSphere2= (BoundingSphere)model2.Tag; 
    BoundingSphere sphere2 = XNAUtils.TransformBoundingSphere(origSphere2,world2); 
    bool collision = sphere1.Intersects(sphere2); 
    return collision; 
}
      精确检测。每个模型是由多个成员组成的,几何数据是存储在模型的Meshes集合中的。每个 ModelMesh都能生成自己的包围球。这些小的包围球的总体积比模型的全局包围球小得多,分别对每个小的包围球进行碰撞检测,通常会返回更好的结果。

      你首先将collision变量设置为false,除非检测到碰撞那么这个变量将保持为false。因为你需要将每个小包围球移动到正确的位置,所以需要绝对Bone矩阵。要对第一个模型的每个ModelMesh进行这个检测,你需要将包围球移动到绝对位置。要实现这一步,你需要考虑模型包围球的位置和模型在3D世界中的位置。然后你遍历第二个模型的所有部分并将这些包围球变换到绝对位置。对于第一个模型的每个包围球,你检查是否与第二个模型的任意一个包围球是否发生碰撞,如果是,则将collision变量设置为true。
private bool FinerCheck(Model model1, Matrix world1, Model model2, Matrix world2)
{
    //if (CoarseCheck(model1, world1, model2, world2) == false) 
    //    return false; 
    Matrix[] model1Transforms = new Matrix[model1.Bones.Count]; 
    model1.CopyAbsoluteBoneTransformsTo(model1Transforms);
    BoundingSphere[] model1Spheres = new BoundingSphere[model1.Meshes.Count]; 
    for (int i=0; i<model1.Meshes.Count; i++) 
    {
        ModelMesh mesh = model1.Meshes[i]; 
        BoundingSphere origSphere = mesh.BoundingSphere; 
        Matrix trans = model1Transforms[mesh.ParentBone.Index]* world1; 
        BoundingSphere transSphere = XNAUtils.TransformBoundingSphere(
origSphere,trans); 
        model1Spheres[i] = transSphere; 
    }
    
    Matrix[] model2Transforms = new Matrix[model2.Bones.Count];
    model2.CopyAbsoluteBoneTransformsTo(model2Transforms); 
    BoundingSphere[] model2Spheres= new BoundingSphere[model2.Meshes.Count]; 
    
    for (int i = 0; i < model1.Meshes.Count;i++)
    {
        ModelMesh mesh = model2.Meshes[i]; 
        BoundingSphere origSphere = mesh.BoundingSphere;
        Matrix trans = model2Transforms[mesh.ParentBone.Index] * world2; 
        BoundingSphere transSphere = XNAUtils.TransformBoundingSphere(
origSphere, trans); 
        model2Spheres[i]= transSphere; 
    }
    
    bool collision = false; 
    for (int i=0; i<model1Spheres.Length;i++) 
        for (int j = 0; j < model2Spheres.Length; j++) 
            if (model1Spheres[i].Intersects(model2Spheres[j]))
                return true; 
    return collision; 
}
      一般情况下,你首先进行快速检测,如果这次检测返回false,那就没必要进行精确检测。如果快速检测显示可能发生碰撞,那么还要进行更加精确的检测。
      对小而快的对象使用Ray-Traced进行碰撞检测。大部分的碰撞检测方法只在两个物体发生物理碰撞时才检测。但是,如果有一个小物体快速地穿过另一个物体,你的程序的更新速度就有可能跟不上而无法检测到碰撞。举一个具体的例子,比如一枚子弹打穿一个瓶子。子弹以5000km/h 的速度射向一个瓶子,而瓶子的宽度只有15cm。XNA程序每秒更新60次,所以每次更新子弹会前进23米的距离。这样的话,在调用Update方法时几乎没有可能检测到子弹和瓶子的碰撞,即使上一帧子弹的确穿过了瓶子。你可以在子弹的上一个位置和当前位置之间创建一个Ray。然后通过调用Ray或包围球的Intersect方法检测Ray是否和包围球发生碰撞。如果发生碰撞,这个方法返回碰撞点与Ray的终点间的距离(你可以使用子弹的前一个位置)。
private bool RayCollision(Model model, Matrix world, Vector3 lastPosition,
    Vector3 currentPosition) 
{
    BoundingSphere modelSpere = (BoundingSphere)model.Tag; 
    BoundingSphere transSphere = XNAUtils.TransformBoundingSphere(
modelSpere, world); 
    Vector3 direction = currentPosition - lastPosition; 
    float distanceCovered = direction.Length(); 
    direction.Normalize(); 
    
    Ray ray = new Ray(lastPosition, direction); 
    
    bool collision = false; 
    float? intersection = ray.Intersects(transSphere); 
    if (intersection != null) 
        if (intersection <= distanceCovered) 
            collision = true; 
    return collision; 
} 
      你同样可以通过在模型的不同ModelMesh上的小包围球上进行ray和包围球的碰撞检测提高精度。
      下一节,将讨论Effect的相关知识~~
  • 大小: 11.4 KB
  • 大小: 41 KB
分享到:
评论

相关推荐

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

    4. **Sprite Batch**:精灵批处理是XNA中绘制2D图像的关键工具。它允许一次性绘制大量2D图像,从而提高渲染效率,减少对GPU的频繁调用。 5. **SoundEffect和Music**:XNA提供了处理音频的类,如SoundEffect用于短促...

    XNA Collision Series 1 - 2D Rectangle Collision

    XNA Tutorial Collision Series 1 - 2D Rectangle Collision

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

    4. **数学基础**:理解向量、矩阵、颜色等基本数学概念是使用XNA进行游戏开发的必备知识。例如,向量用于表示位置、速度和方向,矩阵用于变换和投影。 5. **游戏对象和精灵(Sprite)**:XNA中的Sprite类是处理2D图形...

    XNA学习资料,XNA

    4. **C#基础知识**:由于XNA使用C#作为编程语言,这章可能会回顾一些C#的关键概念,如变量、控制流语句、类和对象等,以便于没有C#背景的读者快速上手。 5. **Game类和GameComponents**:介绍XNA中的Game类,它是...

    XNA入门指南-第一章

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

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

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

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

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

    2D XNA Primitive Shapes Library-开源

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

    XNA-3D文章.CHM

    学习资源\3D-xna游戏设计\XNA-3D文章.CHM

    XNA4.0学习指南

    XNA4.0学习指南

    XNA载入3D模型的例子ModelTest

    通过“ModelTest”示例,开发者可以学习到如何在XNA环境中正确地加载、处理和渲染3D模型,以及如何应用基础的光照和纹理效果。这是构建3D游戏的基础,也是进一步探索更高级技术,如骨骼动画、碰撞检测和物理模拟的...

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

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

    XNA3D游戏开发教程

    ### XNA 3D游戏开发教程知识点概览 #### 一、XNA 游戏开发简介 ...通过以上内容,读者可以对XNA游戏开发有一个较为全面的理解,掌握从环境搭建到游戏发布的整个流程,为进一步深入学习打下坚实的基础。

    XNA 4.0 学习指南

    ### XNA 4.0 学习指南 #### 一、XNA 4.0 新特性 XNA 4.0 是微软推出的一款强大的游戏开发框架,它支持跨平台的游戏开发,包括PC、Xbox 360 和 Windows Phone 7。在XNA 4.0中,微软对许多方面进行了改进,包括项目...

    XNA4.0GameDevelopmentByExample-英文原版.zip

    通过这本书的学习,读者不仅可以掌握XNA4.0的使用,还能了解游戏开发的基本流程和设计原则。虽然XNA框架已经不再被微软官方支持,但其原理仍然适用于许多现代游戏引擎,如Unity或Unreal Engine,因此这本书对于理解...

    Windows Phone开发-XNA基础

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

Global site tag (gtag.js) - Google Analytics