3D物体拾取及XNA实现(转)
2010-11-21 23:37 Aga.J 阅读(888) 评论(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:Game2
{3
GraphicsDeviceManager graphi;4
model[] models;5
Texture2D texture;6

7
Matrix view;8
Matrix projection;9

10
int selectIndex = -1;11

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

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

25

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

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

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

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

42

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

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

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

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

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

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

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

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

103
base.Draw(gameTime);104
}105

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

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

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

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

浙公网安备 33010602011771号