从通用平面获取轴对齐坐标

时间:2018-03-07 12:37:15

标签: c# unity3d math plane

标题可能是错的,因为我不知道足够的数学来用一个小句子来描述我的问题。

  1. 我有一个闭合的3D矢量循环,我将称之为“3D多边形'。
  2. 我需要对它执行仅2D操作,这将返回给我一个 不同的2D点集
  3. 我需要将这些新的2D点转换回3D。
  4. 我目前的尝试如下:

    1. 获得最佳选择'飞机可以最大限度地减少制作“3D' 转换为2D时多边形自相交。
    2. 通过移动平面法​​线来获得两个垂直平面 坐标
    3. 对于每个3D点,获取到平面的距离以获得轴对齐坐标'
    4. 稍后将Y坐标保存在单独的变量中,使用X和Z进行2D操作
    5. 执行2D操作
    6. 获取新的2D点,并获得最接近的3个原始点的加权平均值,以假设新2D点的高度
    7. 将“<”轴对齐的坐标相乘' +假定的高度 各个平面法线将2D点返回到3D空间。
    8. 问题是,这不起作用,罪魁祸首似乎是我得到'轴对齐坐标的部分,因为将它们立即恢复会产生错误的结果

          public static List<Vector2> Planify3Dto2DPoints2(Vector3[] points, Vector3 centroid, Plane ply, out Vector3[] oldHeights) {
              var pz = ply.normal.z;
              var px = ply.normal.x;
              var py = ply.normal.y;
              Plane plx = new Plane(new Vector3(pz, px, py), 0);
              Plane plz = new Plane(new Vector3(py, pz, px), 0);
              oldHeights = new Vector3[points.Length];
              List<Vector2> m_points = new List<Vector2>();
              int i = 0;
              foreach (Vector3 v3 in points) {
                  Vector3 v4 = v3 - centroid;
                  float x = plx.GetDistanceToPoint(v4);//this part is wrong, attempting to get the v4 
                  float z = plz.GetDistanceToPoint(v4);//vector back from the x, z, y coordinates is not 
                  float y = ply.GetDistanceToPoint(v4);//working. removing x * plx.Normal from v4 before
                  m_points.Add(new Vector2(x, z));// extracting the z coordinate reduces the error, but does not remove it
                  oldHeights[i++] = new Vector3(x, z, y);
              }
              return m_points;
          }
      
          public static List<Vector3> Spacefy2Dto3DPoints(Vector2[] points, Vector3 centroid, Plane ply, Vector3[] oldHeights = null) {
              List<Vector3> m_points = new List<Vector3>();
              var pn = new Vector3(ply.normal.x, ply.normal.y, ply.normal.z);
              for (int i = 0; i < points.Length; i++) {
                  Vector3 mp = MoveInPlane(ply, points[i]);
                  if (oldHeights != null) {
                      mp += pn * oldHeights[i].z;//AverageOf3ClosestHeight(points[i], oldHeights); not needed yet, but working fine, it's weighted average
                  }
                  mp += centroid;
                  m_points.Add(mp);
              }
              return m_points;
          }
      
          private static Vector3 MoveInPlane(Plane plane, Vector2 vector2) {
              var z = plane.normal.z;
              var x = plane.normal.x;
              var y = plane.normal.y;
              return new Vector3(z, x, y) * vector2.x + new Vector3(y, z, x) * vector2.y;
          }
      

1 个答案:

答案 0 :(得分:3)

问题出在这一步:

  
      
  1. 通过移动平面法​​线坐标
  2. 获取两个垂直平面   

这不会给出垂直平面

您可能错误地认为这会因为一个简单的具体示例而起作用,例如: (1, 0, 0) => (0, 1, 0) & (0, 0, 1),或围绕坐标切换有效地切换轴的角色,这相当于旋转90度。但试试看,例如(1, 1, 0)你立即发现这不起作用。

一种方法是:

  • 取正常PX轴(任意选择)的点积。
  • 如果接近1或-1(设置阈值,例如abs(dot(X, P)) > 0.5),则设置矢量变量Q <- Z轴(再次,任意)。否则,请设置Q <- X
  • 因此,U = P ^ QV = P ^ U给出了两个垂直平面的法线。请注意,它们未规范化,{U, V, P}提供了一组右手轴。

您可以做的另一个小优化是将- centeroid合并到平面方程本身中,以避免必须明确地为每个点进行此操作。

Vector3 Q = (Math.Abs(ply.normal.x) > 0.5) ? new Vector3D(0.0, 1.0, 0.0)
                                           : new Vector3D(1.0, 0.0, 0.0);
Vector3 U = Vector3.Normalize(Vector3.CrossProduct(ply.normal, Q));
Vector3 V = Vector3.CrossProduct(ply.normal, U);
// no need to normalize V because U and P are already orthonormal

Plane plx = new Plane(U, Vector3.DotProduct(U, centeroid));
Plane plz = new Plane(V, Vector3.DotProduct(V, centeroid));

// ...

foreach (Vector3 v3 in points) {
    /* Vector3 v4 = v3 - centroid; // erase this line */
    float x = plx.GetDistanceToPoint(v3); // v4 -> v3 for all code following