围绕任意轴的矩阵旋转错误
Matrix Rotation Around an Arbitrary Axis Bug
我一直在尝试让矩阵围绕任意轴旋转,我想我已经接近了,但我有一个错误。我对 3D 旋转比较陌生,对正在发生的事情有基本的了解。
public static Matrix4D Rotate(Vector3D u, Vector3D v)
{
double angle = Acos(u.Dot(v));
Vector3D axis = u.Cross(v);
double c = Cos(angle);
double s = Sin(angle);
double t = 1 - c;
return new Matrix4D(new double [,]
{
{ c + Pow(axis.X, 2) * t, axis.X * axis.Y * t -axis.Z * s, axis.X * axis.Z * t + axis.Y * s, 0 },
{ axis.Y * axis.X * t + axis.Z * s, c + Pow(axis.Y, 2) * t, axis.Y * axis.Z * t - axis.X * s, 0 },
{ axis.Z * axis.X * t - axis.Y * s, axis.Z * axis.Y * t + axis.X * s, c + Pow(axis.Z, 2) * t, 0 },
{ 0, 0, 0, 1 }
});
}
以上代码是矩阵旋转的算法。当我用单位向量测试算法时,我得到以下信息:
Matrix4D rotationMatrix = Matrix4D.Rotate(new Vector3D(1, 0, 0), new Vector3D(0, 0, 1));
Vector4D vectorToRotate = new Vector4D(1,0,0,0);
Vector4D result = rotationMatrix * vectorToRotate;
//Result
X = 0.0000000000000000612;
Y = 0;
Z = 1;
Length = 1;
旋转 90 度后,我发现效果几乎完美。现在让我们看一下 45 度旋转:
Matrix4D rotationMatrix = Matrix4D.Rotate(new Vector3D(1, 0, 0), new Vector3D(1, 0, 1).Normalize());
Vector4D vectorToRotate = new Vector4D(1,0,0,0);
Vector4D result = rotationMatrix * vectorToRotate;
//Result
X = .70710678118654746;
Y = 0;
Z = .5;
Length = 0.8660254037844386;
当我们采用 atan(.5/.707) 时,我们发现我们有 35.28 度的旋转而不是 45 度的旋转。矢量的长度也从 1 变为 .866。有人对我做错了什么有任何提示吗?
您的矩阵代码看起来是正确的,但您也需要 标准化 轴 (只是因为 u
和 v
被归一化并不意味着 u cross v
也是)。
(出于性能和准确性的原因,我还建议使用简单的乘法而不是 Pow
;但这是次要的,而不是问题的根源)
为了后代,这是我用于这件事的代码。我总是建议使用 Atan2(dy,dx)
而不是 Acos(dx)
因为它在接近 90° 的角度在数值上更稳定。
public static class Rotations
{
public static Matrix4x4 FromTwoVectors(Vector3 u, Vector3 v)
{
Vector3 n = Vector3.Cross(u, v);
double sin = n.Length(); // use |u×v| = |u||v| SIN(θ)
double cos = Vector3.Dot(u, v); // use u·v = |u||v| COS(θ)
// what if u×v=0, or u·v=0. The full quadrant arctan below is fine with it.
double angle = Atan2(sin, cos);
n = Vector3.Normalize(n);
return FromAxisAngle(n, angle);
}
public static Matrix4x4 FromAxisAngle(Vector3 n, double angle)
{
// Asssume `n` is normalized
double x = n.X, y = n.Y, z = n.Z;
double sin = Sin(angle);
double vcos = 1.0-Cos(angle);
return new Matrix4x4(
1.0-vcos*(y*y+z*z), vcos*x*y-sin*z, vcos*x*z+sin*y, 0,
vcos*x*y+sin*z, 1.0-vcos*(x*x+z*z), vcos*y*z-sin*x, 0,
vcos*x*z-sin*y, vcos*y*z+sin*x, 1.0-vcos*(x*x+y*y), 0,
0, 0, 0, 1.0);
}
}
密码是C#
我一直在尝试让矩阵围绕任意轴旋转,我想我已经接近了,但我有一个错误。我对 3D 旋转比较陌生,对正在发生的事情有基本的了解。
public static Matrix4D Rotate(Vector3D u, Vector3D v)
{
double angle = Acos(u.Dot(v));
Vector3D axis = u.Cross(v);
double c = Cos(angle);
double s = Sin(angle);
double t = 1 - c;
return new Matrix4D(new double [,]
{
{ c + Pow(axis.X, 2) * t, axis.X * axis.Y * t -axis.Z * s, axis.X * axis.Z * t + axis.Y * s, 0 },
{ axis.Y * axis.X * t + axis.Z * s, c + Pow(axis.Y, 2) * t, axis.Y * axis.Z * t - axis.X * s, 0 },
{ axis.Z * axis.X * t - axis.Y * s, axis.Z * axis.Y * t + axis.X * s, c + Pow(axis.Z, 2) * t, 0 },
{ 0, 0, 0, 1 }
});
}
以上代码是矩阵旋转的算法。当我用单位向量测试算法时,我得到以下信息:
Matrix4D rotationMatrix = Matrix4D.Rotate(new Vector3D(1, 0, 0), new Vector3D(0, 0, 1));
Vector4D vectorToRotate = new Vector4D(1,0,0,0);
Vector4D result = rotationMatrix * vectorToRotate;
//Result
X = 0.0000000000000000612;
Y = 0;
Z = 1;
Length = 1;
旋转 90 度后,我发现效果几乎完美。现在让我们看一下 45 度旋转:
Matrix4D rotationMatrix = Matrix4D.Rotate(new Vector3D(1, 0, 0), new Vector3D(1, 0, 1).Normalize());
Vector4D vectorToRotate = new Vector4D(1,0,0,0);
Vector4D result = rotationMatrix * vectorToRotate;
//Result
X = .70710678118654746;
Y = 0;
Z = .5;
Length = 0.8660254037844386;
当我们采用 atan(.5/.707) 时,我们发现我们有 35.28 度的旋转而不是 45 度的旋转。矢量的长度也从 1 变为 .866。有人对我做错了什么有任何提示吗?
您的矩阵代码看起来是正确的,但您也需要 标准化 轴 (只是因为 u
和 v
被归一化并不意味着 u cross v
也是)。
(出于性能和准确性的原因,我还建议使用简单的乘法而不是 Pow
;但这是次要的,而不是问题的根源)
为了后代,这是我用于这件事的代码。我总是建议使用 Atan2(dy,dx)
而不是 Acos(dx)
因为它在接近 90° 的角度在数值上更稳定。
public static class Rotations
{
public static Matrix4x4 FromTwoVectors(Vector3 u, Vector3 v)
{
Vector3 n = Vector3.Cross(u, v);
double sin = n.Length(); // use |u×v| = |u||v| SIN(θ)
double cos = Vector3.Dot(u, v); // use u·v = |u||v| COS(θ)
// what if u×v=0, or u·v=0. The full quadrant arctan below is fine with it.
double angle = Atan2(sin, cos);
n = Vector3.Normalize(n);
return FromAxisAngle(n, angle);
}
public static Matrix4x4 FromAxisAngle(Vector3 n, double angle)
{
// Asssume `n` is normalized
double x = n.X, y = n.Y, z = n.Z;
double sin = Sin(angle);
double vcos = 1.0-Cos(angle);
return new Matrix4x4(
1.0-vcos*(y*y+z*z), vcos*x*y-sin*z, vcos*x*z+sin*y, 0,
vcos*x*y+sin*z, 1.0-vcos*(x*x+z*z), vcos*y*z-sin*x, 0,
vcos*x*z-sin*y, vcos*y*z+sin*x, 1.0-vcos*(x*x+y*y), 0,
0, 0, 0, 1.0);
}
}
密码是C#