2011-07-11

Rotacja wokół arbitrarnej osi.

Musimy znaleźć macierz obracającą o kąt $\mathbf{\theta}$ wokół wektora $\mathbf{\vec A(u,v,w)}$.

Zakładamy, że nasz układ współrzędnych jest lewoskrętny.

Macierz wynikowa będzie złożeniem 5 macierzy:
  1. obrót wokół wybranej osi sprowadzający wektor $\mathbf{\vec A}$ do płaszczyzny osi układu współrzędnych (np. wokół osi Z do płaszczyzny XZ)
  2. obrót wokół osi układu współrzędnych tak by sprowadzić wektor $\mathbf{\vec A}$ do jednej z osi układ współrzędnych (np. wokół osi Y do osi Z)
  3. obrót wokół tej osi (tym samym wokół wektora w jego lokalnym układzie współrzędnych od którego sprowadziliśmy go obrotami) o kąt $\mathbf{\theta}$
  4. obrót odwrotny do 2
  5. obrót odwrotny do 1
Zróbmy dokładnie tak jak jest napisane w nawiasach. Jednakże możemy wybrać dowolnie inne osie układu współrzędnych jako osie obrotu, wynik musi być ten sam.

image/svg+xml x y P α z v w u
Najpierw robimy obrót o kąt $\mathbf{-\alpha}$ wokół osi Z sprowadzając wektor $\mathbf{\vec A}$ do płaszczyzny XY.

$\alpha=-\arctan(\displaystyle\frac{v}{u})$
image/svg+xml x y P β z w s
Teraz robimy obrót o kąt $\mathbf{-\beta}$ wokół osi Y sprowadzając wektor $\mathbf{\vec A}$ do osi Z.

$s=\sqrt{v^2+u^2}$
$\beta=-arctan(\displaystyle\frac{s}{w})$
Teraz nie pozostaje nam nic innego jak dokonać obrotu o kąt $\mathbf{\theta}$ wokół osi Z.

Kompletna macierz ma postać:

$M=M_Z^{-1}(\alpha)M_Y^{-1}(\beta)M_Z(\theta)M_{Y}(\beta)M_Z(\alpha)$
Zakładając, że wektor $\mathbf{\vec A}$ jest wektorem jednostkowym (co znacząco upraszcza i przyspiesza obliczenia), po wytrwałym mnożeniu powinniśmy dostać:

$t=1-cos\theta$

$M=\begin{bmatrix}
tuu+cos\theta & tuv-wsin\theta & tuw+vsin\theta & 0\\
tuv+wsin\theta & tvv+cos\theta & tvw-usin\theta & 0 \\
tuw-vsin\theta & tvw+usin\theta & tww+cos\theta & 0 \\
0 & 0 & 0 & 1
\end{bmatrix}$
Przykład w C# prosto z definicji:
public static Matrix4 CreateRotateAroundVector(Vector3 a_rotate_around,
    double a_angle)
{
    a_rotate_around = a_rotate_around.Normalized;

    double u = a_rotate_around.X;
    double v = a_rotate_around.Y;
    double w = a_rotate_around.Z;

    var m_alpha = Matrix4.CreateRotateZ(-Math.Atan2(v, u));
    var m_beta = Matrix4.CreateRotateY(
        -Math.Atan2(Math.Sqrt(v*v + u*u), w)));
    var m_angle = Matrix4.CreateRotateZ(a_angle);

    return m_alpha.Inverted * m_beta.Inverted * m_angle * m_beta * m_alpha;
}
I wersja szybka:
public static Matrix4 CreateRotateAroundVector(Vector3 a_rotate_around,
    double a_angle)
{
    a_rotate_around = a_rotate_around.Normalized;

    double cos = Math.Cos(a_angle);
    double sin = Math.Sin(a_angle);
    double t = 1 - cos;

    double u = a_rotate_around.X;
    double v = a_rotate_around.Y;
    double w = a_rotate_around.Z;

    return new Matrix4(

        t * u * u + cos,
        t * u * v - sin * w,
        t * u * w + sin * v, 
        0, 

        t * u * v + sin * w,
        t * v * v + cos,
        t * v * w - sin * u,
        0, 

        t * u * w - sin * v,
        t * v * w + sin * u,
        t * w * w + cos, 
        0, 

        0, 0, 0, 1 );
}

Brak komentarzy:

Prześlij komentarz