Pol*_*878 111
Quaternion q;
vector a = crossproduct(v1, v2);
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2);
Run Code Online (Sandbox Code Playgroud)
不要忘记将q标准化.
理查德对于没有一个独特的旋转是正确的,但上面应该给出"最短的弧度",这可能是你需要的.
Jos*_*son 60
我想出了我认为Imbrondir试图呈现的解决方案(尽管有一个小错误,这可能是为什么sinisterchipmunk无法验证它).
鉴于我们可以构造一个四元数来表示围绕轴的旋转,如下所示:
q.w == cos(angle / 2)
q.x == sin(angle / 2) * axis.x
q.y == sin(angle / 2) * axis.y
q.z == sin(angle / 2) * axis.z
Run Code Online (Sandbox Code Playgroud)
并且两个归一化向量的点和叉积是:
dot == cos(theta)
cross.x == sin(theta) * perpendicular.x
cross.y == sin(theta) * perpendicular.y
cross.z == sin(theta) * perpendicular.z
Run Code Online (Sandbox Code Playgroud)
看到从u到v的旋转可以通过围绕垂直向量旋转theta(矢量之间的角度)来实现,看起来好像我们可以直接构造一个四元数来表示点和交叉乘积的结果的旋转; 然而,就其而言,θ=角度/ 2,这意味着这样做会导致所需旋转的两倍.
一种解决方案是计算u和v之间的向量,并使用u和中间向量的点和叉积来构造一个四元数,表示u和中间向量之间角度的两倍旋转,这让我们一直到v!
有一种特殊情况,其中u == -v并且无法计算唯一的中间向量.这是预期的,因为无限多的"最短弧"旋转可以将我们从u带到v,并且我们必须简单地围绕与u(或v)正交的任何向量旋转180度作为我们的特例解决方案.这是通过将u的归一化叉积与任何其他与u 不平行的向量相乘来完成的.
伪代码遵循(显然,实际上特殊情况必须考虑浮点不准确性 - 可能通过检查点积与某个阈值而不是绝对值).
另请注意,当u == v时,没有特殊情况(生成身份四元数 - 请自行检查和查看).
// N.B. the arguments are _not_ axis and angle, but rather the
// raw scalar-vector components.
Quaternion(float w, Vector3 xyz);
Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
// It is important that the inputs are of equal length when
// calculating the half-way vector.
u = normalized(u);
v = normalized(v);
// Unfortunately, we have to check for when u == -v, as u + v
// in this case will be (0, 0, 0), which cannot be normalized.
if (u == -v)
{
// 180 degree rotation around any orthogonal vector
return Quaternion(0, normalized(orthogonal(u)));
}
Vector3 half = normalized(u + v);
return Quaternion(dot(u, half), cross(u, half));
}
Run Code Online (Sandbox Code Playgroud)
该orthogonal
函数返回与给定向量正交的任何向量.该实现使用具有最正交基矢量的叉积.
Vector3 orthogonal(Vector3 v)
{
float x = abs(v.x);
float y = abs(v.y);
float z = abs(v.z);
Vector3 other = x < y ? (x < z ? X_AXIS : Z_AXIS) : (y < z ? Y_AXIS : Z_AXIS);
return cross(v, other);
}
Run Code Online (Sandbox Code Playgroud)
这实际上是在接受的答案中提出的解决方案,它似乎比中途矢量解决方案略快(我的测量速度快了约20%,但不记得我的话).我在这里添加它,以防其他像我这样的人对解释感兴趣.
基本上,不是使用中间向量计算四元数,而是可以计算四元数,这将导致所需旋转的两倍(如另一个解决方案中详述),并找到四元数和零度之间的四元数.
正如我之前解释的那样,所需旋转加倍的四元数是:
q.w == dot(u, v)
q.xyz == cross(u, v)
Run Code Online (Sandbox Code Playgroud)
零旋转的四元数是:
q.w == 1
q.xyz == (0, 0, 0)
Run Code Online (Sandbox Code Playgroud)
计算中间四元数只需要对四元数求和并对结果进行归一化,就像使用向量一样.但是,与矢量的情况一样,四元数必须具有相同的幅度,否则结果将偏向具有较大幅度的四元数.
由两个向量的点和叉积构成的四元数将具有与那些乘积相同的量级:length(u) * length(v)
.我们可以改为扩展身份四元数,而不是将所有四个组件除以此因子.如果你想知道为什么接受的答案似乎通过使用使事情变得复杂sqrt(length(u) ^ 2 * length(v) ^ 2)
,那是因为矢量的平方长度比长度更快计算,所以我们可以保存一个sqrt
计算.结果是:
q.w = dot(u, v) + sqrt(length_2(u) * length_2(v))
q.xyz = cross(u, v)
Run Code Online (Sandbox Code Playgroud)
然后规范化结果.伪代码如下:
Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
float k_cos_theta = dot(u, v);
float k = sqrt(length_2(u) * length_2(v));
if (k_cos_theta / k == -1)
{
// 180 degree rotation around any orthogonal vector
return Quaternion(0, normalized(orthogonal(u)));
}
return normalized(Quaternion(k_cos_theta + k, cross(u, v)));
}
Run Code Online (Sandbox Code Playgroud)
所陈述的问题尚未明确定义:给定的一对矢量没有唯一的旋转。考虑这种情况,例如u = <1,0,0>和v = <0,1,0>。从u到v的一圈是围绕z轴的pi / 2圈。从u到v的另一个旋转将是围绕向量<1,1,0>的pi旋转。
我对四元数不太擅长。然而,我为此苦苦挣扎了几个小时,无法使 Polaris878 解决方案发挥作用。我尝试过预规范化 v1 和 v2。标准化 q。标准化 q.xyz。但我还是不明白。结果还是没有给我正确的结果。
但最终我找到了一个可行的解决方案。如果它对其他人有帮助,这是我的工作(python)代码:
def diffVectors(v1, v2):
""" Get rotation Quaternion between 2 vectors """
v1.normalize(), v2.normalize()
v = v1+v2
v.normalize()
angle = v.dot(v2)
axis = v.cross(v2)
return Quaternion( angle, *axis )
Run Code Online (Sandbox Code Playgroud)
如果 v1 和 v2 是并行的,例如 v1 == v2 或 v1 == -v2 (有一定的容差),则必须采用特殊情况,我认为解决方案应该是 Quaternion(1, 0,0,0) (无旋转)或四元数(0, *v1)(180 度旋转)
为什么不使用纯四元数表示向量?最好先将它们标准化。
q 1 =(0 U X ü ÿ Ù Ž) '
q 2 =(0V X v ÿ v Ž)'
q 1 q 腐 = Q 2
预乘法其中q 1 -1
q 腐 = Q 1 -1 q 2
其中q 1 -1 = q 1 conj / q 范数
这可以被认为是“左分裂”。右除法,这不是您想要的:
q 腐烂,右 = q 2 -1 q 1