回転情報の変換

Matrix, Quaternion, Euler などの回転情報の変換プログラムです。

前田稔の超初心者のプログラム入門

回転情報

  1. モデルの回転座標を計算する方法として、最も基本的なのが変換行列を使う方法です。
    変換行列は4行4列の大きさで、XYZの回転に加えて移動座標の変換も行うことが出来ます。
    Java と DirectX では、行と列が入れ替わっているようなので要注意です。
  2. 変換行列以外に、オイラー角やクォータニオンも使われます。
    オイラー角は、XYZの軸を順番に回転してモデルの姿勢を決める方法です。
    yaw, pitch, roll の回転順序によって姿勢が変わるので要注意です。
    DirectX では、軸回転の順序はZXYです。
  3. クォータニオンは、回転する軸の傾きをXYZで指定して、Wで回転の度合いを指定します。
    DirectX のアニメーション設定などで使われています。
  4. モデルで姿勢を確認しながら変換行列, オイラー角, クォータニオンの値を印字するツールを開発しました。
    前田稔(Maeda Minoru)の超初心者のプログラム入門 の「Game Program & Tool のダウンロード」から提供しています。

プログラムの作成

  1. Quaternion の回転情報を Matrix に変換します。
    Java には Quaternion の回転情報を Matrix に設定する Matrix.set() メソッドがあります。
    //★ Quaternion ⇒ Matrix     前田 稔
    import java.awt.*;
    import javax.vecmath.*;
    
    class Quat2Matrix
    {   static Matrix4f Mat4= new Matrix4f();
    
        public static void main(String[] args)
        {   System.out.println("\r\n☆Quat4f(0.0f,0.0f,0.0f,1.0f)");
            Mat4.set(new Quat4f(0.0f,0.0f,0.0f,1.0f));
            MatDisp(Mat4);
            System.out.println("\r\n☆Quat4f(0.55f,0.20f,0.79f,-0.17f)");
            Mat4.set(new Quat4f(0.55f,0.20f,0.79f,-0.17f));
            MatDisp(Mat4);
            System.out.println("\r\n☆Quat4f(0.11f,-0.14f,0.98f,0.03f)");
            Mat4.set(new Quat4f(0.11f,-0.14f,0.98f,0.03f));
            MatDisp(Mat4);
        }
    
        // Matrix4f を表示する
        public static void MatDisp(Matrix4f mat4)
        {   System.out.println("Matrix4f=");
            System.out.println("  " + mat4.m00 + "  " + mat4.m01 + "  " + mat4.m02 + "  " + mat4.m03);
            System.out.println("  " + mat4.m10 + "  " + mat4.m11 + "  " + mat4.m12 + "  " + mat4.m13);
            System.out.println("  " + mat4.m20 + "  " + mat4.m21 + "  " + mat4.m22 + "  " + mat4.m23);
            System.out.println("  " + mat4.m30 + "  " + mat4.m31 + "  " + mat4.m32 + "  " + mat4.m33);
        }
    }
    
  2. Matrix の回転情報を Quaternion に変換します。
    Java には Matrix の回転情報を Quaternion に変換する Matrix.get() メソッドがあります。
    //★ 変換行列⇒クオータニオン    前田 稔
    import java.awt.*;
    import javax.vecmath.*;
    
    public class Mat2Quat
    {
        public static void main(String[] args)
        {   int     i;
            Quat4f      quat= new Quat4f();
    
            Matrix4f[]  mat4 = new Matrix4f[5];
            mat4[0]= new Matrix4f       //回転無し
                ( 1.0f, 0.0f, 0.0f, 0.0f,  0.0f, 1.0f, 0.0f, 0.0f, 
                  0.0f, 0.0f, 1.0f, 0.0f,  0.0f, 0.0f, 0.0f, 1.0f );
            mat4[1]= new Matrix4f       //Y軸+回転
                ( 0.83f, 0.0f, -0.56f, 0.0f,  0.0f, 1.0f, 0.0f, 0.0f, 
                  0.56f, 0.0f, 0.83f, 0.0f,  0.0f, 0.0f, 0.0f, 1.0f );
            mat4[2]= new Matrix4f       //X軸+回転
                ( 1.0f, 0.0f, 0.0f, 0.0f,  0.0f, 0.76f, 0.64f, 0.0f, 
                  0.0f, -0.64f, 0.76f, 0.0f,  0.0f, 0.0f, 0.0f, 1.0f );
            mat4[3]= new Matrix4f       //Z軸+回転
                ( 0.70f, 0.72f, 0.0f, 0.0f,  -0.72f, 0.70f, 0.0f, 0.0f, 
                  0.0f, 0.0f, 1.0f, 0.0f,  0.0f, 0.0f, 0.0f, 1.0f );
            mat4[4]= new Matrix4f       //XY軸−回転
                ( 0.92f, 0.12f, 0.37f, 0.0f,  0.0f, 0.96f, -0.30f, 0.0f, 
                  -0.39f, 0.27f, 0.88f, 0.0f,  0.0f, 0.0f, 0.0f, 1.0f );
    
            System.out.println("変換行列⇒クオータニオン");
            for(i=0; i<5; i++)
            {   mat4[i].get(quat);
                System.out.println("x=" + quat.x + "  y=" + quat.y + "  z=" + quat.z + "  w=" + quat.w);
            }
        }
    }
    
  3. Java の座標回転を使って、オイラー角を Matrix に変換します。
    オイラー角は yaw(Y), pitch(X), roll(Z) の順に並んでいるので、ZXYの順に回転するだけです。
    //★ オイラー角⇒変換行列    前田 稔
    import java.awt.*;
    import javax.vecmath.*;
    import javax.media.j3d.Transform3D;
    
    public class Euler2Mat
    {
        public static void main(String[] args)
        {   Matrix4f    Mat4= new Matrix4f();
            float       Euler[][]=
            { {0.0f,0.0f,0.0f}, {0.1f,0.0f,0.0f}, {0.0f,0.2f,0.0f}, {0.0f,0.0f,0.3f}, {-0.4f,-0.5f,0.0f} };
    
            System.out.println("★オイラー角⇒変換行列");
            for(int i=0; i<5; i++)
            {   System.out.println("☆yaw(Y)=" + Euler[i][0] + " pitch(X)=" + Euler[i][1] + " roll(Z)=" + Euler[i][2]);
                EulerToMatrix(Euler[i], Mat4);
                MatDisp(Mat4);
            }
        }
    
        // オイラー角(yaw(Y),pitch(X),roll(Z))⇒変換行列
        public static void EulerToMatrix(float[] euler, Matrix4f mat4)
        {   Transform3D rotz = new Transform3D();
            Transform3D rotx = new Transform3D();
            Transform3D roty = new Transform3D();
    
            rotz.rotZ((double)euler[2]);
            rotx.rotX((double)euler[1]);
            roty.rotY((double)euler[0]);
            rotz.mul(rotx);
            rotz.mul(roty);
            rotz.get(mat4);
        }
    
        // Matrix4f を表示する
        public static void MatDisp(Matrix4f mat4)
        {   System.out.println("  " + mat4.m00 + "  " + mat4.m01 + "  " + mat4.m02 + "  " + mat4.m03);
            System.out.println("  " + mat4.m10 + "  " + mat4.m11 + "  " + mat4.m12 + "  " + mat4.m13);
            System.out.println("  " + mat4.m20 + "  " + mat4.m21 + "  " + mat4.m22 + "  " + mat4.m23);
            System.out.println("  " + mat4.m30 + "  " + mat4.m31 + "  " + mat4.m32 + "  " + mat4.m33);
        }
    }
    
  4. Matrix からオイラー角を抽出します。
    Java にメソッドが無いので自前で作成しました。
    EulerToMatrix() を逆変換して、実行の結果を確認しました。
    オイラー角を使ってアニメーションする場合、ヘディングとバンクを ±180°に、ピッチを ±90°に制限します。
    //★ 変換行列⇒オイラー角    前田 稔
    import java.awt.*;
    import javax.vecmath.*;
    
    public class Mat2Euler
    {
        public static void main(String[] args)
        {   int     i;
            float[]     euler= new float[3];
    
            Matrix4f[]  mat4 = new Matrix4f[5];
            mat4[0]= new Matrix4f       //回転無し
                ( 1.0f, 0.0f, 0.0f, 0.0f,
                  0.0f, 1.0f, 0.0f, 0.0f,
                  0.0f, 0.0f, 1.0f, 0.0f,
                  0.0f, 0.0f, 0.0f, 1.0f );
            mat4[1]= new Matrix4f       //yaw(Y)= 0.1
                ( 0.995f, 0.0f, 0.10f, 0.0f,
                  0.0f, 1.0f, 0.0f, 0.0f, 
                 -0.10f, 0.0f, 0.995f, 0.0f,
                  0.0f, 0.0f, 0.0f, 1.0f );
            mat4[2]= new Matrix4f       //pitch(X)= 0.2
                ( 1.0f, 0.0f, 0.0f, 0.0f,
                  0.0f, 0.98f, -0.199f, 0.0f,
                  0.0f, 0.199f, 0.98f, 0.0f,
                  0.0f, 0.0f, 0.0f, 1.0f );
            mat4[3]= new Matrix4f       //roll(Z)= 0.3
                ( 0.955f, -0.296f, 0.0f, 0.0f,
                  0.296f, 0.955f, 0.0f, 0.0f,
                  0.0f, 0.0f, 1.0f, 0.0f,
                  0.0f, 0.0f, 0.0f, 1.0f );
            mat4[4]= new Matrix4f       //yaw= -0.4, pitch= -0.5
                ( 0.921f, 0.0f, -0.389f, 0.0f,
                  0.187f, 0.878f, 0.442f, 0.0f,
                  0.342f, -0.479f, 0.808f, 0.0f,
                  0.0f, 0.0f, 0.0f, 1.0f );
    
            System.out.println("★変換行列⇒オイラー角");
            for(i=0; i<5; i++)
            {   MatDisp(mat4[i]);
                MatrixToEuler(mat4[i],euler);
                System.out.println("yaw(Y)=" + euler[0] + "  pitch(X)=" + euler[1] + "  roll(Z)=" + euler[2]);
                System.out.println("");
            }
        }
    
        // 変換行列⇒オイラー角(yaw(Y),pitch(X),roll(Z))
        public static void MatrixToEuler(Matrix4f mat4, float[] euler)
        {   double  yaw,pitch,roll;
    
            roll= Math.atan2((double)mat4.m10, (double)mat4.m11);
            pitch= Math.asin((double)-mat4.m12);
            yaw= Math.atan2((double)mat4.m02, (double)mat4.m22);
            if (Math.abs(Math.cos(pitch)) < 1.0e-6f)
            {   if(mat4.m10>0.0f)   roll= Math.PI;
                else                roll= -Math.PI;
                if(mat4.m02>0.0f)   yaw= Math.PI;
                else                yaw= -Math.PI;
            }
            euler[0]= (float)yaw;
            euler[1]= (float)pitch;
            euler[2]= (float)roll;
        }
    
        // Matrix4f を表示する
        public static void MatDisp(Matrix4f mat4)
        {   System.out.println("  " + mat4.m00 + "  " + mat4.m01 + "  " + mat4.m02 + "  " + mat4.m03);
            System.out.println("  " + mat4.m10 + "  " + mat4.m11 + "  " + mat4.m12 + "  " + mat4.m13);
            System.out.println("  " + mat4.m20 + "  " + mat4.m21 + "  " + mat4.m22 + "  " + mat4.m23);
            System.out.println("  " + mat4.m30 + "  " + mat4.m31 + "  " + mat4.m32 + "  " + mat4.m33);
        }
    }
    

超初心者のプログラム入門(Java2)