2 アニメーション

2.1 アニメーションの仕組み

時間とともに絵を移動させることにより,アニメーションを作成することができる.例え ば,図1のように,プロペラの角度を少しずつ変化させると風車 のアニメーションができあがる.

コンピューターでアニメーションを作成する場合,シーン毎の座標をデータとして指定す ると大変面倒である.シーン毎に膨大なデータが必要となるからである.データを軽減す るために,動きは計算により求めることが多い.

図 1: プロペラの角度が少しずつ回転する図を重ねるとアニメーションになる.
\includegraphics[keepaspectratio, scale=0.8]{figure/make_anime.eps}

2.2 アニメーションのプログラム例

それでは,実際にアニメーションのプログラムを見てみよう.リスト 1が簡単なアニメーションの例で,これを実行すると図 2の風車のプロペラが回る.

アニメーションは,次のようにして描いている.


   1 #include <stdio.h>
   2 #include <GL/glut.h>
   3 #include <math.h>
   4 
   5 //---- プロトタイプ宣言 -----
   6 void draw(void);                            // 図を描く
   7 void resize(int w, int h);                  // サイズの調整
   8 void anime(void);                           // アニメーション
   9 void set_color(void);                       // 塗りつぶし色の設定
  10 
  11 //---- グローバル変数 -------
  12 double theta = 0.0;                         // プロペラの角度,初期値
  13 double deg = M_PI/180;                      // 度をラジアンに
  14 
  15 //====================================================================
  16 // main関数
  17 //====================================================================
  18 int main(int argc, char *argv[])
  19 {
  20   glutInitWindowPosition(100,200);          // 初期位置(x,y)指定
  21   glutInitWindowSize(200,200);              // 初期サイズ(幅,高さ)指定
  22   glutInit(&argc, argv);                    // GLUT 初期化
  23   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);           // 表示モードの指定
  24   glutCreateWindow("windmill");             // windowをタイトルを付けてを開く
  25   glutDisplayFunc(draw);                    // イベントにより呼び出し
  26   glutReshapeFunc(resize);                  // サイズ変更のときに呼び出す関数指定
  27   glutIdleFunc(anime);                      // 暇なときに実行(アニメーション)
  28   set_color();                              // 塗りつぶす色指定
  29   glutMainLoop();                           // GLUTの無限ループ
  30 
  31   return 0;
  32 }
  33 
  34 //====================================================================
  35 // 図形を描く
  36 //====================================================================
  37 void draw(void)
  38 {
  39   double l1=0.7, l2=0.05;
  40   double dthe;
  41   int i;
  42 
  43   glClear(GL_COLOR_BUFFER_BIT);
  44   // ---- タワー ---------
  45   glColor3d(0.5, 1.0, 0.5);            // 線の色指定(RGB)
  46   glBegin(GL_POLYGON);
  47   glVertex2d(-0.1, -0.9);
  48   glVertex2d(-0.05, 0.1);
  49   glVertex2d( 0.05, 0.1);
  50   glVertex2d( 0.1, -0.9);
  51   glEnd();
  52 
  53   // ---- プロペラ ---------
  54   glColor3d(1.0, 1.0, 1.0);            // 線の色指定(RGB)
  55   for(i=0; i<3; i++){
  56     glBegin(GL_POLYGON);
  57     dthe=i*120;                        // プロペラの3軸の角度
  58     glVertex2d(0.0, 0.0);
  59     glVertex2d(l2*cos((theta+dthe-60)*deg), l2*sin((theta+dthe-60)*deg));
  60     glVertex2d(l1*cos((theta+dthe)*deg),    l1*sin((theta+dthe)*deg));
  61     glVertex2d(l2*cos((theta+dthe+60)*deg), l2*sin((theta+dthe+60)*deg));
  62     glEnd();                             
  63   }
  64 
  65   glutSwapBuffers();                    // 描画
  66 }
  67 
  68 
  69 //====================================================================
  70 // リサイズ
  71 //     この関数は window のサイズが変化したら呼び出される
  72 //     引数
  73 //            w:ウィンドウの幅
  74 //            h:ウィンドウの高さ
  75 //====================================================================
  76 void resize(int w, int h)
  77 {
  78   glLoadIdentity();             // 変換行列を単位行列に
  79   gluOrtho2D(-w/200.0, w/200.0, -h/200.0, h/200.0); // world座標系の範囲
  80   glViewport(0, 0, w, h);       // ウィンドウ座標系を指定
  81 }
  82 
  83 //====================================================================
  84 // PCが暇なときに実行する.これが実行されるとアニメーションが描かれる
  85 //====================================================================
  86 void anime(void)
  87 {
  88   theta += 0.02;
  89   if(theta>=360) theta-=360;                // 360度を超えた処理
  90   glutPostRedisplay();
  91 }
  92 
  93 //====================================================================
  94 // 色の指定
  95 //====================================================================
  96 void set_color(void)
  97 {
  98   glClearColor(0.0, 0.0, 1.0, 1.0);        //赤緑青と透明度
  99 }


\fbox{\textgt{実行結果}}
2に示す風車が回る.
図 2: リスト1のプログラムの実行結果.風車が回る.
\includegraphics[keepaspectratio, scale=0.7]{figure/windmill.eps}

2.3 プログラムの内容説明

2.3.1 各行の動作

復習もかねて,リスト1のプログラムの内容を説明しておく.

2.3.2 新たな関数

アニメーションを描くために,新たに加わった関数は,以下の三つである.
void glutIdelFunc(vod (*func)void))

イベントキューにイベントが無いときに実行されるコールバック関数を登録 する.すなわち,コンピューターが暇なときに実行される関数を登録するの である.引数は難しそうなことを書いているが,関数名でOK.関数名はポイ ンターと成っている.
void glutPostRedisplay(vod)

現在のウィンドウの再描画が必要とマークする.次の機会に, glutDisplayFunc()で登録したコールバック関数を実行する.ここでは, draw()関数が実行される.
void glutSwapBuffers()

裏バッファーと表バッファーを交換する.アニメーションを作成する場合は, 2枚のバッファーに交互に得を作成する.

2.3.3 プロペラの描き方

プロペラが回転しているように見せるためには,その絵を角度$ \theta$を使って表現しな くてはならない.この角度を連続的に変えることにより,回転している絵を描く.

プロペラは,GL_POLYGONを使って描く.しかし,これは凸の図形しか描くことがで きないので,プロペラの羽を一つずつ描いてそれを足しあわせて一つのプロペラとする.

ひとつの羽は,図3のような座標系を用いる.角度$ \theta$を用い て,図中の4つの座標を表現する必要がある.三角関数の知識を使うと,次のようになる.

  $\displaystyle (0,\quad0)$   $\displaystyle \texttt{座標(1)}$   (1)
  $\displaystyle (\ell_2\cos(\theta-\pi/3),\quad\ell_2\sin(\theta-\pi/3))$   $\displaystyle \texttt{座標(2)}$ (2)
  $\displaystyle (\ell_1\cos(\theta),\quad\ell_1\sin(\theta))$   $\displaystyle \texttt{座標(3)}$ (3)
  $\displaystyle (\ell_2\cos(\theta+\pi/3),\quad\ell_2\sin(\theta+\pi/3))$   $\displaystyle \texttt{座標(4)}$   (4)

これを,glVertex2dで決めれば一枚の羽が描ける.他の2枚の羽は,これに $ 2\pi/3[\mathrm{rad}]$ $ 4\pi/3[\mathrm{rad}]$の角度を加えれば良い.

図 3: プロペラの座標系.
\includegraphics[keepaspectratio, scale=0.7]{figure/propeller.eps}

ホームページ: Yamamoto's laboratory
著者: 山本昌志
Yamamoto Masashi
平成19年11月28日


no counter