2017-08-31 4 views
1

J'ai essayé d'obtenir une rotation de matrice autour d'un axe arbitraire, je pense que je suis proche mais j'ai un bug. Je suis relativement nouveau dans les rotations 3D et j'ai une compréhension de base de ce qui se passe.Rotation de matrice autour d'un axe arbitraire Bug

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 } 
    }); 
} 

Le code ci-dessus est l'algorithme pour la rotation de la matrice. Lorsque je teste l'algorithme avec des vecteurs unitaires, je reçois ce qui suit:

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; 

Avec une rotation de 90 degrés, je trouve que cela fonctionne presque parfaitement. Maintenant, regardons un 45 degrés de rotation:

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; 

Quand nous prenons, nous constatons que nous avons une rotation au lieu d'une rotation de 45 degrés degrés 35,28 le atan (0,5/0,707). En outre, la longueur du vecteur passe de 1 à 0,866. Est-ce que quelqu'un a des conseils sur ce que je fais mal?

+1

Permettez-moi d'ajouter que d'obtenir l'angle en utilisant 'ACOS (u.Dot (v))' est pas très bon parce que si les deux vecteurs sont presque perpendiculaires les résultats sont instables . Utilisez 'Atan2 ((u.Cross (v)). Magnitude, u.Dot (v))' à la place. – ja72

Répondre

4

Votre code matriciel semble correct, mais vous devez normaliser le axe trop (juste parce que u et v sont normalisés ne signifie pas u cross v est aussi).

(Je recommande également d'utiliser un simple, multiplier au lieu de Pow, pour des raisons de performance et de précision, mais cela est mineur et non la source de votre problème)

1

Juste pour la postérité, c'est le code que j'utilise pour cette chose exacte. Je recommande toujours d'utiliser Atan2(dy,dx) au lieu de Acos(dx) car il est plus stable numériquement à des angles proches de 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); 
    } 
} 

code est C#