上一篇中我们谈到了WPF 3d做图的一些简单原理,这里我们简单介绍一下怎样获得鼠标在场景中的3d坐标,知道了3d坐标就可以进行很多操作了:
首先介绍一下3d图形的构成以及它的一些成员属性:
在 3D 图形编程中,没有线条、Bezier 样条曲线、矩形或椭圆。每个 3D 物体都是三维坐标空间中的三角形的集合。三角形是 3D 编程的基本单位,这是因为每个单独的三角形总是能定义一个平面,而三角形集合可以模仿立体物体,甚至可以模拟曲面。3D 视图由 Viewport3D 元素组成。3D 场景需要一个或多个 GeometryModel3D 类型的物体、一个或多个光源、以及一个用于控制 3D 物体如何投射到 2D 表面从而控制观看者如何看到图像的摄像机。
GeometryModel3D 元素有三个重要属性:Geometry、Material 和 BackMaterial。
Geometry 属性被设置为 MeshGeometry3D 元素,用于根据坐标点和三角形描述可视物体。
Material 和 BackMaterial 属性说明物体的前面和背面如何着色。
MeshGeometry3D 类,该类用于定义 3D 物体的实际几何表示形式。该类有四个重要属性:Positions、TriangleIndices、TextureCoordinates 和 Normals。
Positions 属性表示物体的所有顶点。这些顶点在定义物体时肯定是有作用的,但它们不能描述所有信息。任何一组三个顶点都可以组合成一个三角形,这就是 TriangleIndices 集合所说明的内容
TriangleIndices 集合实际上驱动物体的呈现。TriangleIndices 未引用的任何 Positions 元素都会忽略(如果没有 TriangleIndices,则 Positions 集合中的每个 Point3D 三联数都将解释为一个三角形)。每个三角形都应有正面和背面。查看三角形的正面时,三联数以反时针方向表示顶点。如果将第一个三联数更改为 0 1 3,将看到左上三角形以红色着色,这是因为查看的是三角形的背面,而不是它的正面。
Normals 属性是按与 Positions 集合的一对一对应关系得到的向量的集合。每个顶点均被视为面向特定方向,该方向以该顶点的 Normals 向量表示。每个三角形内的每个点基于在其三个顶点上的向量的内插值,以不同方式反射光线。如果不提供 Normals 集合,则会基于在网格规范*享的每个顶点上会合的三角形的 Normals 的平均值计算一个该集合。
如果对其还是不明白,可以参考这一篇文章,这里有比较详细的介绍:http://msdn.microsoft.com/zh-cn/magazine/cc163449.aspx
有了上面的基础,下面我们就来介绍一下怎样获得鼠标在三维场景中的坐标:
主要是通过viewport3d中的三维视觉效果进行命中测试:
C#源码如下:
public void HitTest(object sender, System.Windows.Input.MouseButtonEventArgs args)
{
Point mouseposition = args.GetPosition(myViewport);
Point3D testpoint3D = new Point3D(mouseposition.X, mouseposition.Y, 0);
Vector3D testdirection = new Vector3D(mouseposition.X, mouseposition.Y, 10);
PointHitTestParameters pointparams = new PointHitTestParameters(mouseposition);
RayHitTestParameters rayparams = new RayHitTestParameters(testpoint3D, testdirection);
//test for a result in the Viewport3D
VisualTreeHelper.HitTest(myViewport, null, HTResult, pointparams);
以下代码中的 HitTestResultBehavior 确定如何处理命中测试结果。 UpdateResultInfo 和 UpdateMaterial 是本地定义的方法。
public HitTestResultBehavior HTResult(System.Windows.Media.HitTestResult rawresult)
{
//MessageBox.Show(rawresult.ToString());
RayHitTestResult rayResult = rawresult as RayHitTestResult;
if (rayResult != null)
{
RayMeshGeometry3DHitTestResult rayMeshResult = rayResult as RayMeshGeometry3DHitTestResult;
if (rayMeshResult != null)
{
GeometryModel3D hitgeo = rayMeshResult.ModelHit as GeometryModel3D;
GeometryModel3D hitgeo = rayMeshResult.ModelHit as GeometryModel3D;
MeshGeometry3D hitmesh = hitgeo.Geometry as MeshGeometry3D;
Point3D p1=hitmesh.Positions.ElementAt(rayMeshResult.VertexIndex1);
double weight1 = rayMeshResult.VertexWeight1;
Point3D p2 = hitmesh.Positions.ElementAt(rayMeshResult.VertexIndex2);
double weight2 = rayMeshResult.VertexWeight2;
Point3D p3 = hitmesh.Positions.ElementAt(rayMeshResult.VertexIndex3);
double weight3 = rayMeshResult.VertexWeight3;
Point3D prePoint = new Point3D(p1.X * weight1 + p2.X * weight2 + p3.X * weight3, p1.Y * weight1 + p2.Y * weight2 + p3.Y * weight3, p1.Z * weight1 + p2.Z * weight2 + p3.Z * weight3);
UpdateResultInfo(rayMeshResult);
UpdateMaterial(hitgeo, (side1GeometryModel3D.Material as MaterialGroup));
}
}
return HitTestResultBehavior.Continue;
}
代码可以参见:http://msdn.microsoft.com/zh-cn/library/ms745195.aspx
红色代码就是获得鼠标3d坐标的关键,具体解释一下:
Hitgeo 是命中测试hittest的返回结果,具体属性可以参见
http://msdn.microsoft.com/zh cn/library/ms606198.aspx
通过上面GeometryModel3D的介绍可以知道,hitgeo的geometry属性就是MeshGeometry3D类型的,也就是三角形网络图形类,
命中测试的返回结果非常丰富,VertexIndex1,VertexIndex2 VertexIndex3 代表的是命中小三角形的顶点坐标在整个3d图中的点集合的索引,而VertexWeight1 VertexWeight2 VertexWeight3 分别代表了命中点在各个点所占的权重,这样通过权重和顶点坐标我们就可以计算出鼠标点击的点在三维场景中的坐标了。