基于向量和点的四元数旋转

Quaternion rotations based on vector and point

我正在构建一个使用 3D 的程序。

它基本上是由一个对象(某种立方体)组成的,用户可以旋转和四处移动,而我可以通过用指向他们的箭头检查服务器上的同一个对象来观察他们对对象的看法观点。

客户端将其位置、朝向和向上作为 3 个向量发送给服务器。

我正在尝试根据箭头的位置和旋转来渲染我的箭头。


我正在使用以下代码执行此操作(对 3D 使用 LibGDX):

Vector3[] vs;
Vector3 tmp = new Vector3();
batch.begin(wm.cam);
for (SocketWrapper s : clientAtt.keySet()) {// for every client
    vs = clientAtt.get(s); // read the 3 vectors: { position, direction, up }

    tmp.set(vs[2].nor());
    vs[1].nor();

    // arr.transform is the transform of the arrow
    arr.transform.set(vs[0], new Quaternion().setFromCross(tmp.crs(Vector3.Z).nor(), vs[1]));

    batch.render(arr);
}

这是arr的定义:

arrow = new ModelBuilder().createArrow(0, 0, 0, 1, 0, 0, 0.1f, 0.1f, 5, GL20.GL_TRIANGLES, new Material(ColorAttribute.createDiffuse(Color.RED)), VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal);
arr = new ModelInstance(arrow);

如果我只绕 Y 轴旋转,一切正常,但如果我使用 X/Z 轴,它就会变得疯狂。

我想找出我的数学错误在哪里以及如何解决。

谢谢。

Quaternion用于定义一个旋转,方向。例如:它定义了如何将任何给定的(单位)向量转换为另一个向量,以便它旋转您指定的量。但它没有定义是哪个向量。甚至更多:有 无限 数量的可能转换可以实现这一点。

setFromCross 方法允许您通过提供两个任意向量来指定旋转。然后 Quaternion 将被设置为将第一个向量转换为第二个向量,方法是绕轴旋转 垂直 到您提供的向量。

所以,在你的情况下:

setFromCross(tmp.set(up).crs(Vector3.Z).nor(), direction)

这会设置 Quaternion 以便它会沿着垂直于这两个向量的轴将向上向量和 Z+ 向量的叉积旋转到方向向量。在某些情况下,这可能对您有用,但我怀疑这是您真正想要做的。所以,回答你的问题:这可能是你数学出错的地方。

尽管这超出了您的问题范围,但让我们看看如何实现您想要实现的目标。

  1. 首先定义模型未旋转时的方向。例如。哪一侧在上,哪一侧是向前(方向),哪一侧是右(或左)。让我们假设在这种情况下,在静止状态下,您的模型在 Y+ (upRest = new Vector3(0, 1, 0);) 的上方,它面向 X+ (directionRest = new Vector3(1, 0, 0)),而在右边是 Z+ (rightRest = new Vector3(0, 0, 1);).

  2. 现在定义您想要的旋转方向。你已经有了,除了我们可以使用叉积(垂直)向量的权利:upTarget = new Vector3(vs[2]).nor(); directionTarget = new Vector3(vs[1]).nor(); rightTarget = new Vector3().set(upTarget).crs(directionTraget).nor();请注意,你可能需要交换叉积中的向上和方向目标向量(.set(directionTarget).crs(upTarget).nor(); )

  3. 因为静止方向是轴对齐,我们可以通过矩阵的一个属性来走一个不错的小捷径。一个Matrix4可以定义为:一个由4个向量组成的向量。这四个向量中的第一个指定旋转的 X 轴,第二个指定旋转的 Y 轴,第三个向量指定旋转的 Z 向量,第四个向量指定位置。所以,使用这个单线将模型设置为我们想要的方向和位置:

    arr.transform.set(directionTarget, upTarget, rightTarget, vs[0]);