关于数学:计算3D中垂直于第三矢量的两个矢量

 2021-04-09 

Computing two vectors that are perpendicular to third vector in 3D

计算两个与第三个向量(X)垂直且彼此垂直的向量的最佳(最快)方法是什么?

这是我现在如何计算这些向量:

1
2
3
4
// HELPER - unit vector that is NOT parallel to X
x_axis = normalize(X);
y_axis = crossProduct(x_axis, HELPER);
z_axis = crossProduct(x_axis, y_axis);

我知道有无数种解决方案,我不在乎哪一种是我的解决方案。

这个问题的背后是什么:我需要构造变换矩阵,在该变换矩阵中,我知道X轴(矩阵中的第一列)应指向哪个方向。我需要计算Y和Z轴(第二列和第三列)。众所周知,所有轴必须彼此垂直。


如果X<>0Y<>0

  • A = [-Y, X, 0]
  • B = [-X*Z, -Y*Z, X*X+Y*Y]
  • 然后将向量归一化。

    1
    2
    3
    [ X,Y,Z]·[-Y,X,0] = -X*Y+Y*X = 0
    [ X,Y,Z]·[-X*Z,-Y*Z,X*X+Y*Y] = -X*X*Z-Y*Y*Z+Z*(X*X+Y*Y) = 0
    [-Y,X,0]·[-X*Z,-Y*Z,X*X+Y*Y] = Y*X*Z+X*Y*Z = 0

    这称为向量的空空间。

    如果X=0Y=0,则A=[1,0,0]B=[0,1,0]


    有一种方法可以找到一个好的HELPER(确实-准备好成为您的y_axis)。

    让我们的X =(ax,ay,az)。选择2个大小更大的元素,交换它们,并取反其中之一。设置为零的第三元素(幅度最小)。此向量垂直于X。

    示例:

    如果(ax <= ay)和(ax <= az)然后HELPER =(0,-az,ay)(或(0,az,-ay))

    X * HELPER = 0 * 0-ay * az az * ay = 0

    如果(ay <= ax)和(ay <= az)然后HELPER =(az,0,-ay)


    这是执行此操作的方法。
    这也可能是唯一的方法。任何其他方式在数学上都是等效的。
    通过打开crossProduct计算并确保您多次进行相同的乘法运算,可以节省几个周期,但这确实与微优化领域相去甚远。

    您应该注意的一件事当然是HELPER向量。它不仅不必与X平行,而且还很不错,它与X绝对不平行。如果X和HELPER甚至在某种程度上平行,则您的浮点计算将是不稳定且不准确。您可以测试一下,看看X和HELPER的点积是否为0.9999。


    为获得良好的HELPER向量:找到具有最小绝对值的X坐标,并使用该坐标轴:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    absX = abs(X.x); absY = abs(X.y); absZ = abs(X.z);
    if(absX < absY) {
      if(absZ < absX)
        HELPER = vector(0,0,1);
      else // absX <= absZ
        HELPER = vector(1,0,0);
    } else { // absY <= absX
      if(absZ < absY)
        HELPER = vector(0,0,1);
      else // absY <= absZ
        HELPER = vector(0,1,0);
    }

    注意:这实际上与@MBo的答案非常相似:取具有最小坐标轴的叉积等于将最小坐标设置为零,交换大的两个坐标,然后取一个负的坐标。


    我认为单位向量中所有元素的最小最大魔方总是大于0.577,因此您可以逃避此操作:

    ->通过查找魔方大于例如0.5的任何元素,然后忽略其他元素(在其位置使用0)并将其垂直应用于,将找到3D向量和2D向量的垂直向量的问题减少。其余元素中的2D矢量公式(对于2D x轴=(ax,ay)-> y轴=(-ay,ax))

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let x-axis be represented by (ax,ay,az)

    if (abs(ay) > 0.5) {
      y-axis = normalize((-ay,ax,0))
    } else if (abs(az) > 0.5) {
      y-axis = normalize((0,-az,ay))
    } else if (abs(ax) > 0.5) {
      y-axis = normalize((az,0,-ax))
    } else {
      error("Impossible unit vector")
    }