浅谈基于MDX的3D图元鼠标拾取

      在网上看了很多相关资料发现都是单个物体的Pick对于多物体进行World变换后的Pick则很少有讨论的。在dxsdk中也只有单个物体移动相机实现Pick的c++的代码。在此我用c#实现多物体Pick。
所谓Pick(拾取操作)就是通过鼠标选取3d空间中的几何图元(就像魔兽中选择英雄^_^)。

要实现多物体Pick需要实现以下几步:
1,通过鼠标在窗体中的位置获得拾取射线的方向矢量(RayDir)
 首先将屏幕鼠标坐标映射到投影坐标空间(重剑兄台所说的1/2立方体)中,得到向量v:

 Matrix mPj = device.GetTransform(TransformType.Projection);
 Vector3 v;
 v.X 
=  ( float )(( 2.0f   *  mousePoint.X  /   this .Bounds.Width)  -   1.0f /  mPj.M11;
 v.Y 
=   - ( float )(( 2.0f   *  mousePoint.Y  /   this .Bounds.Height)  -   1.0f /  mPj.M22;
 v.Z 
=   1.0f ;


 再将其映射为视察坐标空间中的向量:
 因为v=RayDir*ViewMatrix
 所以要将视察矩阵进行你变换:

  Matrix mView  =  device.GetTransform(TransformType.View);
  Matrix m 
=  Matrix.Invert(mView);


 由于v是向量所以要进行向量乘法得到RayDir:

 vPickRayDir.X  =  v.X  *  m.M11  +  v.Y  *  m.M21  +  v.Z  *  m.M31;
 vPickRayDir.Y 
=  v.X  *  m.M12  +  v.Y  *  m.M22  +  v.Z  *  m.M32;
 vPickRayDir.Z 
=  v.X  *  m.M13  +  v.Y  *  m.M23  +  v.Z  *  m.M33;


2,获得摄像机位置坐标(RayOrig)作为射线起始点
 这里要注意一点,视察矩阵的逆矩阵的第4行前三个数正好是视察点.所以有此的得到RayOrig:

 vPickRayOrig.X  =  m.M41;
 vPickRayOrig.Y 
=  m.M42;
 vPickRayOrig.Z 
=  m.M43;


 以上两步只需在鼠标位置改变或视察矩阵改变的情况下刷新,(我是在MouseMove事件中刷新的)而下面的步骤需要在渲染时实时进行。

3,将投影空间中的RayDir向量和RayOrig坐标变换到世界空间
 由于每个模型都有自己的世界矩阵(本例中用matrixList存储)
 首先给出算法代码如下:

  Matrix m  =  Matrix.Invert(matrixList[i]); // 求第i个模型世界矩阵的逆
 
  
// 对vRayDir作向量变换
  Vector3 vRayDir2;
  vRayDir2.X 
=  vRayDir.X  *  m.M11  +  vRayDir.Y  *  m.M21  +  vRayDir.Z  *  m.M31 ;
  vRayDir2.Y 
=  vRayDir.X  *  m.M12  +  vRayDir.Y  *  m.M22  +  vRayDir.Z  *  m.M32 ;
  vRayDir2.Z 
=  vRayDir.X  *  m.M13  +  vRayDir.Y  *  m.M23  +  vRayDir.Z  *  m.M33 ;
 
  
// 对vRayOrig作坐标变换
  Vector3 vRayOrig2;
  vRayOrig2.X 
=  vRayOrig.X  *  m.M11  +  vRayOrig.Y  *  m.M21  +  vRayOrig.Z  *  m.M31  +  m.M41;
  vRayOrig2.Y 
=  vRayOrig.X  *  m.M12  +  vRayOrig.Y  *  m.M22  +  vRayOrig.Z  *  m.M32  +  m.M42;
  vRayOrig2.Z 
=  vRayOrig.X  *  m.M13  +  vRayOrig.Y  *  m.M23  +  vRayOrig.Z  *  m.M33  +  m.M43;


        以上代码应在Render函数或FrameMove函数中调用。当然以上只是为了方便说明,实际完全可以用Vector4.Transform()简化编程。应该注意的是vRayDir是一个向量所以用Vector4结构表示是W值应设置为0,而vRayOrig是一个坐标用Vector4结构表示是W值应设置为1。在获得了世界空间的vRayDir2和vRayOrig2后即可调用Mesh类的Intersect方法进行交叉检测,例如:meshList[i].Intersect(vRayOrig2, vRayDir2);

3,总结

      对于射线与三角形交点的计算我将在以后的文章中继续讨论。
      其实纵观整个Pick过程其实就是一系列逆变换,反过来看看也很简单,最初一个3D物体经过世界变换,得到在世界坐标空间的位置,在经过视察变换,得到在视察空间的坐标,最后在经过投影变换得到屏幕坐标。而鼠标本身给出的是一个屏幕坐标,就需要经过上述变换的逆过程得到鼠标指针在3D空间中的投影,既选取射线Ray(^_^)。
到此我们讨论了整个Pick过程,本文写的有些仓促,如需完整代码请于本人联系。
(^_^)QQ:253896246
Mail:whl0070179@163.com

参考文献:
Direct3D中实现图元的鼠标拾取BY 重剑,2004.5.28
Improved Ray Picking  http://www.mvps.org/directx/articles/improved_ray_picking.htm

声明:
本文写作的目的是为了广大MDX学习者方便学习服务,文中算法为作者参考相关文献总结,作者无意把这些据为自己的成果,所有权原算法提出者所有(参阅参考文献)

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值