XNA 3D游戏开发入门基本——鼠标选择3D模型(3D物体的拾取)
来源:互联网 发布:js截取url后面的 编辑:程序博客网 时间:2024/04/20 19:39
拾取原理
拾取主要用来表示能过鼠标在屏幕上单击来选中某个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 是否与其相交。如果相交,则检测是不是目前为止距玩家最近的物体,如果是,记录下这个物体以及距离值,替换掉之前找到的最近的物体的记录。当完成对所有物体的检测后,最后一次记录的那个物体就是玩家用鼠标点击的距离玩家最近的物体。
实现
主要方法如下:
public
class
Game1:Game
{
GraphicsDeviceManager graphi;
model[] models;
Texture2D texture;
Matrix view;
Matrix projection;
int
selectIndex = -1;
public
Game1()
{
graphi =
new
GraphicsDeviceManager(
this
);
Content.RootDirectory =
"Content"
;
IsMouseVisible =
true
;
}
protected
override
void
Initialize()
{
models =
new
model[4];
models[0] =
new
model();
models[0].position = Vector3.Zero;
models[1] =
new
model();
models[1].position =
new
Vector3(80,0,0);
models[2] =
new
model();
models[2].position =
new
Vector3(-80, 0, 0);
models[3] =
new
model();
models[3].position =
new
Vector3(80, 80, 0);
//观察矩阵
view = Matrix.CreateLookAt(
new
Vector3(0, 0, 300), Vector3.Forward, Vector3.Up);
//投影矩阵
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1, 10000);
base
.Initialize();
}
protected
override
void
LoadContent()
{
//载入模型文件
models[0].mod = Content.Load<Model>(
"bsphere"
);
models[1].mod = Content.Load<Model>(
"cub"
);
models[2].mod = Content.Load<Model>(
"pyramid"
);
models[3].mod = Content.Load<Model>(
"teaport"
);
//载入选中物体的贴图纹理
texture = Content.Load<Texture2D>(
"sp"
);
base
.LoadContent();
}
/**/
/// <summary>
/// 更新
/// </summary>
/// <param name="gameTime"></param>
protected
override
void
Update(GameTime gameTime)
{
CheckMousClick();
base
.Update(gameTime);
}
/**/
/// <summary>
/// 绘制
/// </summary>
/// <param name="gameTime"></param>
protected
override
void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
for
(
int
i = 0; i < models.Length;i++ )
{
foreach
(ModelMesh mesh
in
models[i].mod.Meshes)
{
foreach
(BasicEffect effect
in
mesh.Effects)
{
effect.TextureEnabled =
true
;
//根据是否选中设置物体的贴图
if
(i != selectIndex)
{
//如果没有选中,不贴图
effect.Texture =
null
;
}
else
effect.Texture = texture;
effect.World = Matrix.CreateTranslation(models[i].position);
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
effect.LightingEnabled =
true
;
}
mesh.Draw();
}
}
base
.Draw(gameTime);
}
/**/
/// <summary>
/// 取得射线
/// </summary>
/// <returns></returns>
private
Ray GetRay()
{
MouseState ms = Mouse.GetState();
Vector3 neerSource =
new
Vector3(ms.X, ms.Y, 0);
Vector3 farSource =
new
Vector3(ms.X, ms.Y, 1);
Vector3 neerPosi = GraphicsDevice.Viewport.Unproject(neerSource, projection, view, Matrix.Identity);
Vector3 farPosi = GraphicsDevice.Viewport.Unproject(farSource, projection, view, Matrix.Identity);
Vector3 direction=farPosi-neerPosi;
direction.Normalize();
return
new
Ray(neerPosi, direction);
}
/**/
/// <summary>
/// 鼠标单击
/// </summary>
private
void
CheckMousClick()
{
if
(Mouse.GetState().LeftButton==ButtonState.Pressed)
{
Ray ray = GetRay();
for
(
int
i=0;i<models.Length;i++)
{
BoundingSphere bs=models[i].mod.Meshes[0].BoundingSphere;
bs.Center = models[i].position;
Nullable<
float
> result = ray.Intersects(bs);
if
(result.HasValue)
{
selectIndex = i;
}
}
}
}
}
/**/
/// <summary>
/// 自定义模型结构
/// </summary>
public
struct
model
{
public
Model mod;
public
Texture2D text;
public
Vector3 position;
}
其中用到了XNA中的Viewport.Unproject方法和Ray.Intersects方法,它们的内部结构分别如下:
public
Vector3 Unproject(Vector3 source, Matrix projection, Matrix view, Matrix world)
{
Vector3 position =
new
Vector3();
Matrix matrix = Matrix.Invert(Matrix.Multiply(Matrix.Multiply(world, view), projection));
position.X = (((source.X -
this
.X) / ((
float
)
this
.Width)) * 2f) - 1f;
position.Y = -((((source.Y -
this
.Y) / ((
float
)
this
.Height)) * 2f) - 1f);
position.Z = (source.Z -
this
.MinDepth) / (
this
.MaxDepth -
this
.MinDepth);
position = Vector3.Transform(position, matrix);
float
a = (((source.X * matrix.M14) + (source.Y * matrix.M24)) + (source.Z * matrix.M34)) + matrix.M44;
if
(!WithinEpsilon(a, 1f))
{
position = (Vector3) (position / a);
}
return
position;
}
public
void
Intersects(
ref
Ray ray,
out
float
? result)
{
result = 0;
float
num = 0f;
float
maxValue =
float
.MaxValue;
if
(Math.Abs(ray.Direction.X) < 1E-06f)
{
if
((ray.Position.X <
this
.Min.X) || (ray.Position.X >
this
.Max.X))
{
return
;
}
}
else
{
float
num11 = 1f / ray.Direction.X;
float
num8 = (
this
.Min.X - ray.Position.X) * num11;
float
num7 = (
this
.Max.X - ray.Position.X) * num11;
if
(num8 > num7)
{
float
num14 = num8;
num8 = num7;
num7 = num14;
}
num = MathHelper.Max(num8, num);
maxValue = MathHelper.Min(num7, maxValue);
if
(num > maxValue)
{
return
;
}
}
if
(Math.Abs(ray.Direction.Y) < 1E-06f)
{
if
((ray.Position.Y <
this
.Min.Y) || (ray.Position.Y >
this
.Max.Y))
{
return
;
}
}
else
{
float
num10 = 1f / ray.Direction.Y;
float
num6 = (
this
.Min.Y - ray.Position.Y) * num10;
float
num5 = (
this
.Max.Y - ray.Position.Y) * num10;
if
(num6 > num5)
{
float
num13 = num6;
num6 = num5;
num5 = num13;
}
num = MathHelper.Max(num6, num);
maxValue = MathHelper.Min(num5, maxValue);
if
(num > maxValue)
{
return
;
}
}
if
(Math.Abs(ray.Direction.Z) < 1E-06f)
{
if
((ray.Position.Z <
this
.Min.Z) || (ray.Position.Z >
this
.Max.Z))
{
return
;
}
}
else
{
float
num9 = 1f / ray.Direction.Z;
float
num4 = (
this
.Min.Z - ray.Position.Z) * num9;
float
num3 = (
this
.Max.Z - ray.Position.Z) * num9;
if
(num4 > num3)
{
float
num12 = num4;
num4 = num3;
num3 = num12;
}
num = MathHelper.Max(num4, num);
maxValue = MathHelper.Min(num3, maxValue);
if
(num > maxValue)
{
return
;
}
}
result =
new
float
?(num);
}
- XNA 3D游戏开发入门基本——鼠标选择3D模型(3D物体的拾取)
- 3D物体拾取及XNA实现
- xna 3D游戏开发(1)
- XNA系列—3d模型的导入
- XNA-顯示3D模型
- 3D对象鼠标像素拾取解决方案
- WP7-XNA 3D开发 显示3D模型(BasicEffect、DualTexturesEffect)
- 3D拾取技术
- 火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二十一节:拾取地表与物体
- 浅谈基于MDX的3D图元鼠标拾取
- 从零3D基础入门XNA 4.0——模型和BasicEffect
- XNA 的 3D 和 2D 游戏引擎(商业与开源)
- Windows Phone XNA 4.0 3D游戏开发
- XNA 3D文字
- XNA学习(二) 载入3D模型
- XNA 2D游戏物体碰撞
- XNA之进阶--3D模型的加载和控制
- XNA学习(四) 一个简单的3D游戏
- 宏观税负及相关概念
- C语言问题
- php初学者容易犯的几个错误
- 数据库的三级模式和两级映射
- wParam和lParam的区别
- XNA 3D游戏开发入门基本——鼠标选择3D模型(3D物体的拾取)
- x264的PSNR是如何计算出来的
- codeforces Round #146(div2) B.Easy Number Challenge
- 各种资料整理
- apache服务器配置
- C#将字符串转换为整型的三种方法的总结
- linux gcc原子操作
- Android之GridView
- UltraEdit正则表达式常用技巧