XNA 3D模型入门讲解(一)

我又开始了,接下来开始我的 XNA 3D 模型入门讲解:

创建XNA Game项目(此步骤自行操作),然后添加一个 Game Component,命名为Camera,修改默认代码如下:

public class Camera : Microsoft.Xna.Framework.GameComponent
{
//Camera matrices
public Matrix view { get; protected set; }
public Matrix projection { get; protected set; }
public Camera(Game game, Vector3 pos, Vector3 target, Vector3 up)
: base(game)
{
  view = Matrix.CreateLookAt(pos, target, up);
  projection = Matrix.CreatePerspectiveFieldOfView(
  MathHelper.PiOver4,
  (float)Game.Window.ClientBounds.Width /(float)Game.Window.ClientBounds.Height,
  1, 3000);
}
public override void Initialize()
{
  base.Initialize();
}
public override void Update(GameTime gameTime)
{

base.Update(gameTime);
}
}
}

在Game类添加代码:

public Camera camera { get; protected set; }

在Initialize中添加代码(实例化摄像机对象,同时将组件添加到组件集合):

camera = new Camera(this, new Vector3(0, 0, 50),Vector3.Zero, Vector3.Up);
Components.Add(camera);

添加模型到项目:
在Content项目中添加一个Models文件夹,然后添加一个3D模型,如果你没有模型,可以下载源码,然后找到后缀名为.x的文件.

添加绘制3D模型的类:

class BasicModel
    {
        public Model model { get; protected set; }
        protected Matrix world = Matrix.Identity;

        public BasicModel(Model m)
        {
            model = m;
        }

        public virtual void Update()
        {
        }

        public void Draw(Camera camera)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);
            //遍历模型中的每个ModelMesh
            foreach (ModelMesh mesh in model.Meshes)
            {
                //遍历ModelMesh有对应该关系的BasicEffect对象
                foreach (BasicEffect be in mesh.Effects)
                {
                    be.EnableDefaultLighting();
                    be.Projection = camera.projection;
                    be.View = camera.view;
                    be.World = GetWorld() * mesh.ParentBone.Transform;
                }
                mesh.Draw();
            }
        }

        public virtual Matrix GetWorld()
        {
            return world;
        }
    }

代码讲解:
第一个变量是Model类型的。Model类用来代表内存中的3D模型,这就好比你之前用Texture2D类来代表内存中的2D图像。
第二个变量是Matrix,代表该特定模型的世界(world)。该矩阵代表在哪绘制,如何旋转和缩放模型等。
给BasicModel类添加一个构造方法。该构造方法只需要一个Model类型的参数,并把该参数赋给模型成员。
创建一个空的虚方法Update,它能够被BasicModel派生类重载,用于定制更新过程中应该执行的动作。

然后需要绘制3D模型,有点麻烦哦,看我慢慢道来:

    这些场景,以.X为后缀名,每一个都包含不止一个物体。这些物体,也称之为网格(meshes),存放在模型中。在XNA中,Model类用ModelMesh对象来表示这些网格。Model类的Meshes属性包含有一系列ModelMesh对象。
    网格包含有用于绘制该网格所需的材质、颜色和纹理等信息。网格的各个部分没有必要使用同样的颜色和纹理,你可以让一个网格包含多种材质。为了存放这些数据,网格由好几部分构成。ModelMesh类在一个名为MeshParts的
  属性里面存放了一系列的ModelMeshParts。每个MeshParts均包含绘制MeshPart所需的材质和绘制MeshPart所需的Effect对象。     默认情况下,Model的MeshPart所用到的Effect是BasicEffect类型的。BasicEffect派生自Effect,它给你提供一种不需要独自创建HLSL效果文件就能在3D绘制物体的一种方法。     最后,每一个ModelMesh都有一个变换(transformation),能移动网格到模型内合适的位置。     为了搞清这一切是怎么工作的,想象一辆带方向盘的汽车模型。用一个Model来代表整个场景(汽车和方向盘一起)。Model的Meshes属性可能有两个不同的ModelMesh对象,一个是汽车的、一个是方向盘的。每一个Meshes都有
 一个变换能把物体放在正确的位置,汽车有可能摆放在某个地方(比如说原点),方向盘相应地摆在某个合适的位置。除此之外,每个Meshes含有一种改变网格样子的材质(比如说汽车可能用到那些使它看起来呈现闪红的材质,方向盘可
能用到那些暗黑色的材质),也含有绘制ModelMesh所需的Effect。

绘制的代码见Draw方法中:

  前两行代码处理一些叫做骨骼(bones)的东西。骨骼用来把物体放置在模型空间内合适的位置。通常情况下,你的代码不会受到这些变换的影响,除非你的模型含有多个网格(飞船模型没有)。只是为了防止给定的模型有多个部分,你需要添加
前两行代码。   接着,代码遍历模型中的每个ModelMesh以及每个与ModelMesh有对应该关系的BasicEffect对象,应用默认的光照,设置被绘制的对象的Projection、View和World属性。摄像机使用投影和视图矩阵,世界矩阵设置对象的World属性。世界
矩阵乘以这里的变换(the transform),来确保ModelMesh放置在模型内合适的位置。

 添加模型管理器:

 public class ModelManager : DrawableGameComponent
    {
        //存放一系列BasicModel
        List<BasicModel> models = new List<BasicModel>();

        public ModelManager(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
        }

        public override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            models.Add(new SpinningEnemy(Game.Content.Load<Model>(@"models\spaceship")));
            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            foreach (BasicModel model in models)
            {
                model.Update();
            }
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            foreach (BasicModel bm in models)
            {
                bm.Draw(((Game1)Game).camera);
            }
            base.Draw(gameTime);
        }
    }

这里主要有两个重要部分:
一个是在LoadContent中加载刚才添加的模型,一个是在Update中遍历更新模型.

 

让模型转起来:
现在的模型似乎没有什么效果,和之前的二维图片没什么区别,接下来实现模型的转动.
添加一个新的类SpinningEnemy:

class SpinningEnemy : BasicModel
    {
        Matrix rotation = Matrix.Identity;
        public SpinningEnemy(Model m)
            : base(m)
        {
        }

        public override void Update()
        {
            rotation *= Matrix.CreateRotationY(MathHelper.Pi / 180);
        }

        public override Matrix GetWorld()
        {
            return world * rotation;
        }
    }

该类实现了BasicModel,这样就免去了重写Draw方法,该类的有了一个rotation旋转的变量;同时在Update中你将看到rotation变量在每一帧中以围绕Y轴多旋转1度的速度更新
(请记住角度是用弧度来表示的,π就是180°,所以π/ 180等于1度);类中还重写GetWorld方法,返回世界坐标和旋转的角度的乘积,也就是旋转后的位置.
最后在ModelManager中的LoadContent中修改创建BasicModel的代码为如下:

models.Add(new SpinningEnemy(Game.Content.Load<Model>(@"models\spaceship")));

 


这一部分主要讲解了在XNA中加载一个3D模型,同时实现了模型的旋转效果,在下一章节中将会讲解"3D中摄像机"的功能.

posted @ 2012-12-09 21:23  wangyafei_it  阅读(1428)  评论(0编辑  收藏  举报