2017-08-31 18 views
1

任意の軸を中心に行列の回転を試行していますが、私は近いと思いますが、バグがあります。私は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度の回転を見てみましょう:私たちはATAN(0.5/0.707)を取るとき

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; 

我々は代わりに45度回転の35.28度回転を持っていることがわかります。また、ベクトルの長さは1から866に変化しています。誰かが私が間違っていることについてのヒントを持っていますか?

+1

です。代わりに 'Atan2((u.Cross(v))。Magnitude、u.Dot(v))'を使用してください。 – ja72

答えて

4

あなたのマトリックスコードが正しく見えますが、あなたは(だけのためuvu cross vでもあるというわけではありません正規化されている)、あまりにもを正規する必要があります。

(私も性能と精度の理由から、代わりにPowの簡単な乗算を使用することをお勧めします。しかし、これはあなたの問題の原因マイナーではない)

1

ただ、後世のために、これは私が使用するコードですこの正確な事のために。私はいつも、Acos(dx)の代わりにAtan2(dy,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); 
    } 
} 

コードは2つのベクトルが結果が不安定で、ほぼ垂直である場合ので、私は `ACOSを使用して角度を得ることは(u.Dot(V))`非常に良いではないことを追加してみましょうC#

関連する問題