ティーポットを回転する

Quaternion を使った自作の Interpolator で、ティーポットを回転します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

Main プログラムの説明

  1. 自作の Interpolator で、ティーポットを回転します。
    DirectX 3D で Form を表示する に習って、空のプロジェクトから作成します。
    次のファイルを格納して、プロジェクトに取り込んで下さい。
    /****************************************************/
    /*★ Interpolator でティーポットを回転    前田 稔 ★*/
    /****************************************************/
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using Microsoft.DirectX;
    using Microsoft.DirectX.Direct3D;
    using Direct3D=Microsoft.DirectX.Direct3D;
    using System.Collections;
    
    namespace Anime
    {
        public class Meshes : Form
        {
            Device device = null;       // Our rendering device
            Mesh mesh = null;           // Our mesh object in sysmem
            PresentParameters presentParams = new PresentParameters();
            bool pause = false;
            InterRot    interrot;       //Interpolator の定義
    
            //回転情報の定義
            InterRotDat[]   Rot =
            {   new InterRotDat(0,new Quaternion(0.0f,0.0f,0.0f,1.0f)),
                new InterRotDat(100,new Quaternion(0.0f,0.72f,0.0f,0.70f)),
                new InterRotDat(200,new Quaternion(0.0f,1.00f,0.0f,0.02f)),
                new InterRotDat(300,new Quaternion(0.0f,-0.71f,0.0f,0.70f)),
                new InterRotDat(400,new Quaternion(0.0f,0.0f,0.0f,1.0f))
            };
            int     tim= 0;
            Matrix  mat;
    
            //Constructor
            public Meshes()
            {   // Set the initial size of our form
                this.ClientSize = new System.Drawing.Size(400, 400);
                this.Text = "Direct3D Teapot";
                interrot = new InterRot(Rot);
            }
    
            //D3DDevice の取得
            bool InitializeGraphics()
            {
                try
                {   presentParams.Windowed = true;
                    presentParams.SwapEffect = SwapEffect.Discard;
                    presentParams.EnableAutoDepthStencil = true;
                    presentParams.AutoDepthStencilFormat = DepthFormat.D16;
    
                    // Create the D3DDevice
                    device = new Device(0, DeviceType.Hardware, this,
                        CreateFlags.SoftwareVertexProcessing, presentParams);
                    device.DeviceReset += new System.EventHandler(this.OnResetDevice);
                    this.OnResetDevice(device, null);
                    pause = false;
                }
                catch (DirectXException)
                {   return false;  }
                return true;
            }
    
            //Direct3D の初期化
            public void OnResetDevice(object sender, EventArgs e)
            {
                Device dev = (Device)sender;
                dev.RenderState.ZBufferEnable = true;  // Turn on the zbuffer
                device.RenderState.Lighting = true;    //make sure lighting is enabled
                //ティーポットの生成
                mesh = Mesh.Teapot(device);
            }
    
            //描画環境の設定
            void SetupMatrices()
            {
                mat= interrot.GetMat(tim);
                device.Transform.World = mat;
                device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f,2.0f,-5.0f),
                    new Vector3(0.0f,0.0f,0.0f), new Vector3(0.0f,1.0f,0.0f));
                device.Transform.Projection = Matrix.PerspectiveFovLH((float)(Math.PI/4),
                    (float)this.Width / (float)this.Height, 1.0f, 500.0f);
            }
    
            //ライトの設定
            private void SetupLights()
            {
                System.Drawing.Color col = System.Drawing.Color.White;
                Direct3D.Material mtrl = new Direct3D.Material();
                mtrl.Diffuse = col;
                mtrl.Ambient = col;
                device.Material = mtrl;
                
                device.Lights[0].Type = LightType.Directional;
                device.Lights[0].Diffuse = System.Drawing.Color.FromArgb(0xF0F020);
                //ライトの座標
                device.Lights[0].Direction = new Vector3(50.0f, -40.0f, 100.0f);
                device.Lights[0].Enabled = true;
                device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x202020);
            }
    
            //モデルの描画
            private void Render()
            {   if (device == null) return;
                if (pause)          return;
    
                device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Blue, 1.0f, 0);
                device.BeginScene();
                SetupLights();
                SetupMatrices();
                mesh.DrawSubset(0);
                device.EndScene();
                device.Present();
            }
    
            //マウスのクリックを検出
            protected override void OnMouseDown(MouseEventArgs e)
            //{   tim+= 100;  }       //アニメーションの間隔
            {   tim+= 20;  }        //アニメーションの間隔
    
            protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
            {   this.Render();  }
            protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e)
            {   if ((int)(byte)e.KeyChar == (int)System.Windows.Forms.Keys.Escape)
                    this.Dispose(); // Esc was pressed
            }
    
            protected override void OnResize(System.EventArgs e)
            {   pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);  }
    
            //☆ Main() メソッド
            static void Main()
            {   using (Meshes frm = new Meshes())
                {   if (!frm.InitializeGraphics()) // Initialize Direct3D
                    {   MessageBox.Show("Could not initialize Direct3D.  This tutorial will exit.");
                        return;
                    }
                    frm.Show();
    
                    // While the form is still valid, render and process messages
                    while (frm.Created)
                    {   frm.Render();
                        Application.DoEvents();
                    }
                }
            }
        }
    }
    
    /*******************************************/
    /*★ Interpolator Class(回転)    前田 稔 ★*/
    /*******************************************/
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using Microsoft.DirectX;
    using Microsoft.DirectX.Direct3D;
    using Direct3D=Microsoft.DirectX.Direct3D;
    using System.Collections;
    
    namespace Anime
    {
        //Rotation Interpolator Data
        class InterRotDat
        {
            public int  Tim;
            public Quaternion Qt;
    
            //InterRotDat Constructor
            public InterRotDat(int tim, Quaternion qt)
            {   Tim= tim;
                //Qt= Quaternion.Normalize(qt);
                Qt= qt;
            }
            public void Print()
            {
                Console.WriteLine("Time={0}  {1},{2},{3},{4}", Tim, Qt.X, Qt.Y, Qt.Z, Qt.W);
            }
        }
    
        //☆Interpolator Class
        class InterRot
        {
            InterRotDat[]   Rot;
            int         Leng;
    
            //InterRot Constructor
            public InterRot(InterRotDat[] rot)
            {   Rot= rot;
                Leng= Rot.GetLength(0)-1;
            }
    
            //tim の Matrix を計算
            public Matrix GetMat(int tim)
            {   int     wk,i;
                float   rate;
                Quaternion wqt;
                wk= tim % Rot[Leng].Tim;
                for(i=0; i<Leng && Rot[i].Tim<wk; i++);
                if (Rot[i].Tim==wk) return Matrix.RotationQuaternion(Rot[i].Qt);
                rate= (float)(wk-Rot[i-1].Tim)/(float)(Rot[i].Tim-Rot[i-1].Tim);
                wqt = Quaternion.Slerp(Rot[i-1].Qt, Rot[i].Qt, rate);
                return Matrix.RotationQuaternion(wqt);
            }
        }
    }
    
  2. Quaternion を使って、座標回転の Interpolator(補間)を自作して、マウスのクリックでティーポットを回転しながら描画します。
    Quaternion は X-FILE に使われている回転アニメーションの形式で、次のような特徴があります。
    1.メモリ使用量が3x3の回転行列の半分以下になる
    2.回転の結合が容易にできる
    3.回転の補間が容易にできる
    4.正規化ができる
    5.行列への変換がオイラー角を使うのに比べて高速
  3. InterRot Class を使ってティーポットを回転する Main Program です。
    モデルの描画は トーラスをライトで照らして描画する を参照して下さい。
    interrot; が自作した座標の回転を補完する InterRot Class の定義です。
    InterRotDat[] が回転アニメーションの姿勢の記述で、Y軸を中心に90度ずつ回転します。
    定義した姿勢は4個(最初と最後は同じ)ですが、その間を Interpolator で補間して滑らかに回転します。
    Quaternion のデータが必要なときは、座標変換ツールを使って下さい。
    「Game Program & 各種 Tool/Windows Tool Program/座標変換ツール」から提供しています。
    リンクがエラーになるときは「前田稔(Maeda Minoru)の超初心者のプログラム入門」から辿って下さい。
    InterRotDat() の先頭の数字(0,100,200,...)は Time(または DT)で、100 Time で次の姿勢に移ります。
    この値は同じ間隔に設定する必要は無く、大きな値の区間はより滑らかにアニメーションされます。
        InterRot    interrot;       //Interpolator の定義
    
        //回転情報の定義
        InterRotDat[]   Rot =
        {   new InterRotDat(0,new Quaternion(0.0f,0.0f,0.0f,1.0f)),
            new InterRotDat(100,new Quaternion(0.0f,0.72f,0.0f,0.70f)),
            new InterRotDat(200,new Quaternion(0.0f,1.00f,0.0f,0.02f)),
            new InterRotDat(300,new Quaternion(0.0f,-0.71f,0.0f,0.70f)),
            new InterRotDat(400,new Quaternion(0.0f,0.01f,0.0f,1.00f))
        };
        int     tim= 0;
        
  4. マウスのクリックでアニメーションを進めます。
    tim+= 20; の値を小さくすれば、より滑らかにアニメーションされます。
        //マウスのクリックを検出
        protected override void OnMouseDown(MouseEventArgs e)
        {   tim+= 20;  }    //アニメーションの間隔
        
  5. ティーポットの描画環境を設定する関数です。
    interrot.GetMat(tim) で Interpolator を呼び出して、tim に対応する回転 Matrix を取得します。
    取得した回転 Matrix を World 座標に設定して描画します。
    ティーポットの形が崩れないようにアスペクト比を設定しています。
        void SetupMatrices()
        {
            mat= interrot.GetMat(tim);
            device.Transform.World = mat;
            device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f,2.0f,-5.0f),
                new Vector3(0.0f,0.0f,0.0f), new Vector3(0.0f,1.0f,0.0f));
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)(Math.PI/4),
                (float)this.Width / (float)this.Height, 1.0f, 500.0f);
        }
        

Interpolator の説明

  1. Quaternion を使って、座標の回転を補間する Interpolator の説明です。
    class InterRotDat で Interpolator のデータ構造を定義します。
    Tim は Time(または DT)で、次の姿勢に移る間隔です。
    Qt は回転座標を定義する Quaternion です。
    Print() は格納されている Quaternion の値を印字するために追加したメソッド(関数)です。
        class InterRotDat
        {
            public int  Tim;
            public Quaternion Qt;
    
            //InterRotDat Constructor
            public InterRotDat(int tim, Quaternion qt)
            {   Tim= tim;
                Qt= Quaternion.Normalize(qt);
            }
            public void Print()
            {
                Console.WriteLine("Time={0}  {1},{2},{3},{4}", Tim, Qt.X, Qt.Y, Qt.Z, Qt.W);
            }
        }
        
  2. InterRot Class の GetMat() 関数です。
    Quaternion の回転データを補間して、Matrix をリターンします。
    Rot[i].Tim を tim で検索して、姿勢が定義されている Index を求めます。
    Rot[i].Tim と tim が一致すれば、補間の必要がないので Matrix に変換してリターンします。
    DirectX には Quaternion から Matrix に変換する RotationQuaternion() 関数が用意されています。
    二個の姿勢の補間処理です。
    rate に補間の係数を求めます。
    rate を使って Rot[i-1].Qt と Rot[i].Qt の姿勢を補完した Quaternion を求めます。
    DirectX には Quaternion を補完する Slerp() 関数が用意されています。
    補完した Quaternion を Matrix に変換してリターンします。
            ・・・
        public Matrix GetMat(int tim)
        {   int     wk,i;
            float   rate;
            Quaternion wqt;
            wk= tim % Rot[Leng].Tim;
            for(i=0; i<Leng && Rot[i].Tim<wk; i++);
            if (i==0 || Rot[i].Tim==wk)     return Matrix.RotationQuaternion(Rot[i].Qt);
            rate= (float)(wk-Rot[i-1].Tim)/(float)(Rot[i].Tim-Rot[i-1].Tim);
            wqt = Quaternion.Slerp(Rot[i-1].Qt, Rot[i].Qt, rate);
            return Matrix.RotationQuaternion(wqt);
        }
        

移動 Interpolator

  1. 三次元座標を移動する Interpolator を作成して下さい。
    移動は回転より簡単で、InterRot Object Class と同じ要領です。
  2. 三次元座標のデータ構造を定義する InterMovDat Class を定義します。
    回転座標のときは Quaternion を使いましたが、移動のときは Vector3 を使います。
  3. 座標の移動を補間する InterMov Object Class です。
    Vector3 を Matrix に変換する関数は Matrix.Translation(Vector3); です。
    Quaternion で補完するときは Slerp() 関数使いましたが、Vector3 でも同様の関数が用意されています。
    Vector3.Lerp(Vector3-1, Vector3-2, 補間の係数); で、使い方は回転と同様です。
  4. JAVA でも同様のプログラムを作成しています。
    「超初心者のプログラム入門(Java)/Java 3D 入門/PositionPath&RotationPath&ScalePath」を参照して下さい。
    JAVA には Interpolator の専用クラスが用意されています。
    PositionPath&RotationPath&ScalePath

超初心者のプログラム入門(C# Frame Work)