3D物体拾取及XNA实现(转)
2010-11-21 23:37 Aga.J 阅读(886) 评论(0) 收藏 举报最近在做到和3D模型的拾取有关的东西,找到了这篇不错的文章,加上自己的修改,基本解决了鼠标点击3D模型获取3D模型的坐标点的问题。所以转过来记录下。
拾取原理
拾取主要用来表示能过鼠标在屏幕上单击来选中某个3D模型,然后就可以获取这个模型信息,同时也可以对这个模型进行编辑。
拾取算法的主要思想是:得到鼠标点击处的屏幕坐标,通过投影矩阵和观察矩阵把该坐标转换为通过视点和鼠标点击点的一条射入场景的光线,该光线如果与场景模型的三角形相交,则获取该相交三角形的信息。
拾取的具体过程如下:
1.使用获取鼠标当前状态。
2.把屏幕坐标转换为屏幕空间坐标。
屏幕中心是(0, 0),屏幕右下角是 (1*(屏幕宽度/屏幕高度), 1)。屏幕空间的x坐标这样计算: ((鼠标x坐标) / (屏幕宽度/2))-1.0f) *(屏幕宽度/屏幕高度)
屏幕空间的y坐标这样计算: (1.0f − ((鼠标y坐标) / (屏幕高度/ 2 ) )
3.计算摄像机视图中的宽度和高度的截距比。如下计算:
view ratio = tangent(camera field of view / 2 )
通常,你只需要一次,然后保存这个结果供以后使用。在摄像机视野改变的时候,你需要重新计算。
4.把屏幕空间坐标转换成摄像机空间坐标系近景裁剪平面中的一个点。
Near point = ((屏幕空间x坐标)*(近景裁剪平面的Z值)*(view ratio ),
(屏幕空间y坐标)*(近景裁剪平面的Z值)*(view ratio ),
(-近景裁剪平面的Z值) ) 
5.把屏幕空间坐标转换成摄像机空间坐标系远景裁剪平面中的一个点。
Far point = ((屏幕空间x坐标)* (远景裁剪平面的Z值)*(view ratio ),
(屏幕空间y坐标)*(远景裁剪平面的Z值 )*(view ratio),
(-远景裁剪平面的Z值) ) 
6.使用Invert 获得摄像机视图矩阵的一个Invert的拷贝。使用摄像机视图矩阵(Matrix)来把世界坐标转化成摄像机坐标,使用这个反转的矩阵可以把摄像机坐标转化成世界坐标。
Matrix invView = Matrix.Invert(view);
7.使用反转的视图矩阵(view Matrix ) 和Transform方法把远景点和近景点转换成世界空间坐标。
Vector3 worldSpaceNear = Vector3.Transform(cameraSpaceNear, invView);
Vector3 worldSpaceFar = Vector3.Transform(cameraSpaceFar, invView);
8.创建一个射线(Ray)类的对象,起点是近景点,指向远景点。
Ray pickRay = new Ray(worldSpaceNear, worldSpaceFar - worldSpaceNear);
9.对世界空间中的所有物体循环调用 Intersects 方法来检测 Ray 是否与其相交。如果相交,则检测是不是目前为止距玩家最近的物体,如果是,记录下这个物体以及距离值,替换掉之前找到的最近的物体的记录。当完成对所有物体的检测后,最后一次记录的那个物体就是玩家用鼠标点击的距离玩家最近的物体。
XNA实现
效果图如下:

 
主要方法如下:
 public class Game1:Game
   public class Game1:Game2
 {
    {3
 GraphicsDeviceManager graphi;
        GraphicsDeviceManager graphi;4
 model[] models;
        model[] models;5
 Texture2D texture;
        Texture2D texture;6

7
 Matrix view;
        Matrix view;8
 Matrix projection;
        Matrix projection;9

10
 int selectIndex = -1;
        int selectIndex = -1;11

12
 public Game1()
        public Game1()13
 {
        {14
 graphi = new GraphicsDeviceManager(this);
            graphi = new GraphicsDeviceManager(this);15
 Content.RootDirectory = "Content";
            Content.RootDirectory = "Content";16
 IsMouseVisible = true;
            IsMouseVisible = true;17
 }
        }18

19
 protected override void Initialize()
        protected override void Initialize()20
 {
        {21
 models = new model[4];
            models = new model[4];22
 models[0] = new model();
            models[0] = new model();23
 models[0].position = Vector3.Zero;
            models[0].position = Vector3.Zero;24

25

26
 models[1] = new model();
            models[1] = new model();27
 models[1].position =new Vector3(80,0,0);
            models[1].position =new Vector3(80,0,0);28

29
 models[2] = new model();
            models[2] = new model();30
 models[2].position = new Vector3(-80, 0, 0);
            models[2].position = new Vector3(-80, 0, 0);31

32
 models[3] = new model();
            models[3] = new model();33
 models[3].position = new Vector3(80, 80, 0);
            models[3].position = new Vector3(80, 80, 0);34

35
 //观察矩阵
            //观察矩阵36
 view = Matrix.CreateLookAt(new Vector3(0, 0, 300), Vector3.Forward, Vector3.Up);
            view = Matrix.CreateLookAt(new Vector3(0, 0, 300), Vector3.Forward, Vector3.Up);37
 //投影矩阵
            //投影矩阵38
 projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1, 10000);
            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1, 10000);39
 base.Initialize();
            base.Initialize();40
 }
        }41

42

43
 protected override void LoadContent()
        protected override void LoadContent()44
 {
        {45
 //载入模型文件
            //载入模型文件46
 models[0].mod = Content.Load<Model>("bsphere");
            models[0].mod = Content.Load<Model>("bsphere");47
 models[1].mod = Content.Load<Model>("cub");
            models[1].mod = Content.Load<Model>("cub");48
 models[2].mod = Content.Load<Model>("pyramid");
            models[2].mod = Content.Load<Model>("pyramid");49
 models[3].mod = Content.Load<Model>("teaport");
            models[3].mod = Content.Load<Model>("teaport");50

51
 //载入选中物体的贴图纹理
            //载入选中物体的贴图纹理52
 texture = Content.Load<Texture2D>("sp");
            texture = Content.Load<Texture2D>("sp");53
 base.LoadContent();
            base.LoadContent();54
 }
        }55

56
 /// <summary>
        /// <summary>57
 /// 更新
        /// 更新58
 /// </summary>
        /// </summary>59
 /// <param name="gameTime"></param>
        /// <param name="gameTime"></param>60
 protected override void Update(GameTime gameTime)
        protected override void Update(GameTime gameTime)61
 {
        {62
 CheckMousClick();
            CheckMousClick();63
 base.Update(gameTime);
            base.Update(gameTime);64
 }
        }65

66
 /// <summary>
        /// <summary>67
 /// 绘制
        /// 绘制68
 /// </summary>
        /// </summary>69
 /// <param name="gameTime"></param>
        /// <param name="gameTime"></param>70
 protected override void Draw(GameTime gameTime)
        protected override void Draw(GameTime gameTime)71
 {
        {72
 GraphicsDevice.Clear(Color.CornflowerBlue);
            GraphicsDevice.Clear(Color.CornflowerBlue);73

74
 for (int i = 0; i < models.Length;i++ )
            for (int i = 0; i < models.Length;i++ )75
 {
            {76
 foreach (ModelMesh mesh in models[i].mod.Meshes)
                foreach (ModelMesh mesh in models[i].mod.Meshes)77
 {
                {78
 foreach(BasicEffect effect in mesh.Effects)
                    foreach(BasicEffect effect in mesh.Effects)79
 {
                    {80
 effect.TextureEnabled = true;
                        effect.TextureEnabled = true;81
 //根据是否选中设置物体的贴图
                        //根据是否选中设置物体的贴图82
 if (i != selectIndex)
                        if (i != selectIndex)83
 {
                        {84
 //如果没有选中,不贴图
                            //如果没有选中,不贴图85
 effect.Texture = null;
                            effect.Texture = null;86
 }
                        }87
 else
                        else88
 effect.Texture = texture;
                            effect.Texture = texture;89

90
 effect.World = Matrix.CreateTranslation(models[i].position);
                        effect.World = Matrix.CreateTranslation(models[i].position);91
 effect.View = view;
                        effect.View = view;92
 effect.Projection = projection;
                        effect.Projection = projection;93

94
 effect.EnableDefaultLighting();
                        effect.EnableDefaultLighting();95
 effect.LightingEnabled = true;
                        effect.LightingEnabled = true;96
 }
                    }97

98
 mesh.Draw();
                    mesh.Draw();99
 }
                }100
 
                101
 }
            }102

103
 base.Draw(gameTime);
            base.Draw(gameTime);104
 }
        }105

106
 /// <summary>
        /// <summary>107
 /// 取得射线
        /// 取得射线108
 /// </summary>
        /// </summary>109
 /// <returns></returns>
        /// <returns></returns>110
 private Ray GetRay()
        private Ray GetRay()111
 {
        {112
 MouseState ms = Mouse.GetState();
            MouseState ms = Mouse.GetState();113
 Vector3 neerSource = new Vector3(ms.X, ms.Y, 0);
            Vector3 neerSource = new Vector3(ms.X, ms.Y, 0);114
 Vector3 farSource = new Vector3(ms.X, ms.Y, 1);
            Vector3 farSource = new Vector3(ms.X, ms.Y, 1);115

116
 Vector3 neerPosi = GraphicsDevice.Viewport.Unproject(neerSource, projection, view, Matrix.Identity);
            Vector3 neerPosi = GraphicsDevice.Viewport.Unproject(neerSource, projection, view, Matrix.Identity);117
 Vector3 farPosi = GraphicsDevice.Viewport.Unproject(farSource, projection, view, Matrix.Identity);
            Vector3 farPosi = GraphicsDevice.Viewport.Unproject(farSource, projection, view, Matrix.Identity);118
 Vector3 direction=farPosi-neerPosi;
            Vector3 direction=farPosi-neerPosi;119
 direction.Normalize();
            direction.Normalize();120
 return new Ray(neerPosi, direction);
            return new Ray(neerPosi, direction);121
 }
        }122

123
 /// <summary>
        /// <summary>124
 /// 鼠标单击
        /// 鼠标单击125
 /// </summary>
        /// </summary>126
 private void CheckMousClick()
        private void CheckMousClick()127
 {
        {128
 if (Mouse.GetState().LeftButton==ButtonState.Pressed)
            if (Mouse.GetState().LeftButton==ButtonState.Pressed)129
 {
            {130
 Ray ray = GetRay();
                Ray ray = GetRay();131
 for (int i=0;i<models.Length;i++)
                for (int i=0;i<models.Length;i++)132
 {
                {133
 BoundingSphere bs=models[i].mod.Meshes[0].BoundingSphere;
                    BoundingSphere bs=models[i].mod.Meshes[0].BoundingSphere;134
 bs.Center = models[i].position;
                    bs.Center = models[i].position;135
 Nullable<float> result = ray.Intersects(bs);
                    Nullable<float> result = ray.Intersects(bs);136
 if (result.HasValue)
                    if (result.HasValue)137
 {
                    {138
 selectIndex = i;
                        selectIndex = i;139
 }
                    }140
 }
                }141
 }
            }142
 }
        }143
 }
    }144

145
 /// <summary>
    /// <summary>146
 /// 自定义模型结构
    /// 自定义模型结构147
 /// </summary>
    /// </summary>148
 public struct  model
    public struct  model149
 {
    {150
 public Model mod;
        public Model mod;151
 public Texture2D text;
        public Texture2D text;152
 public Vector3 position;
        public Vector3 position;153
 }
    }最后附上原文链接:http://www.cnblogs.com/desmend/archive/2008/09/17/1292135.html
作者:Aga.J
出处:http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
个人学习笔记仅供本人记录知识所用,不属发表性文章。
 
                    
                 
    
 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号