さて、前回の実習で作成した「簡易」カーナビでは、車を示すマーカが固定表示の地図の上を移動していました。しかし、地図の表示が固定では、マーカがウィンドウの外に飛び出てしまったり、また、交差点が密集した場所を移動中のマーカが見づ らいという問題があります。
これを解決する方法として、地図自身を移動(又は拡大縮小/回転)させることが考えられます。このような地図のアニメーションはカーナビの課題としては必修ではありませんが、より本物らしいカーナビを完成させるためにも是非取り組んで見て下さい。
このページでは、地図のアニメーションについて簡単に解説します。
これまでのプログラムでは、座標変換は#define文によるマクロ定義により実現されていました。しかし、この方法では地図のアニメーションを行なうことができません。
#define
文によるマクロでは、定義した置換が行なわれます。つまり、
#define REAL_SIZE_X 20.0
#define REAL_SIZE_Y 20.0
というマクロが定義されると、プログラム中の全ての REAL_SIZE_X
が 20.0
に置き換えられます。しかし、この置き換えはプログラム動作中に変更することができません。変更するにはプログラムの再コンパイルが必要となります。
地図をアニメーションさせる、つまり、地図の表示を動的に変化させるには、座標変換式中のパラメータ, REAL_SIZE_X
, REAL_SIZE_Y
...等をプログラム動作中に変える必要があります。このためには、これらのパラメータをマクロではなく変数にする必要が あります。
/* グローバル変数によりパラメータを定義&初期化*/
double REAL_SIZE_X = 20.0;
double REAL_SIZE_Y = 20.0;
ただし、これだけでは動的に座標変換されません。これらの変数を利用しているglOrtho()
がwhile文の外側にあるからです。そこで、glMatrixMode(GL_PROJECTION)
およびglMatrixMode(GL_MODELVIEW)
周辺のコードをwhile文の内側に入れましょう。入れる場所は、while (1)
の直後です。これでwhile文が回るたびに毎回glOrtho()
を呼ぶようになります。
while (1) {
/* (ORIGIN_X, ORIGIN_Y) を中心に、REAL_SIZE_X * REAL_SIZE_Y の範囲の
* 空間をビューポートに投影する
*/
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(ORIGIN_X + REAL_SIZE_X * -0.5, ORIGIN_X + REAL_SIZE_X * 0.5,
ORIGIN_Y + REAL_SIZE_Y * -0.5, ORIGIN_Y + REAL_SIZE_Y * 0.5,
-1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); /* それ以外の座標変換は行わない */
OpenGLならではの座標変換のページで述べられているように、OpenGLでは変換行列の詳細を意識することなく座標変換を行うことができます。マクロを変数化する手法で実施した拡大縮小を、今度はglScalef()
をつかって実装してみましょう。
マクロの変数化による方法
さて、地図表示のパラメータを変更できるようになった所で、アニメーションに取り組んでみましょう。このアニメーションは課題として必修ではないので、ヒントを示すだけにとどめます。
地図をスクロール等のアニメーション表示させるには、座標変換パラメータをわずか に変化させて地図を再表示...ということを繰り返し行ないます。すなわち、
REAL_SIZE_X
等のパラメータを変更glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho();
glMatrixMode(GL_MODELVIEW); /* オブジェクト描画の前にモデルビュー行列モードに戻しておく */
を繰り返すことで地図が動くようになります。
map.c
を改名して、ex11-3.c
を作成してください。#define
文をなくして、すべて変数として定義してください。まずは、適当に地図を動かしてみましょう。地図をじわじわ拡大させる或はぐるぐる回転をさせると面白いかもしれません。次に、マーカーを動かす代わりに地図の方を動かしてみましょう。どのようにパラメータを変化させればいいのかは、皆さんで考えて下さい。
参考例
while文の中にREAL_SIZE_X
, REAL_SIZE_Y
を逐次縮小する記述を追加すると、画面がじわじわ拡大していきます。
REAL_SIZE_X *= 0.99;
REAL_SIZE_Y *= 0.99;
前述のようにマクロを変数化してglOrtho()
を呼ぶ方法でも拡大縮小は実装できますが、よりOpenGLらしい書き方としてはOpenGLならではの座標変換のページで述べられている方法で実装してみましょう。地図をスクロール等のアニメーション表示させるには、座標変換パラメータをわずかに変化させて地図を再表示...ということを繰り返し行うことはマクロの変数化の方法と同様です。
前述のページではモデルビュー行列を変化させることで回転などをしていましたが、while文の中に投影行列を変化させるコードを書くことで投影行列を変化させることでも同様のことができます。対象物が回転するか、視点が回転するかの違いです。いろいろ試して違いについて考察してみましょう。
モデルビュー行列を変化させる場合
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glScalef(a,b,c); /* (a,b,c) 倍に拡大、縮小 */
glRotatef(deg,X,Y,Z); /* (X,Y,Z) のベクトルを中心としたdeg度の回転 */
glTranslatef(x,y,z); /* x,y,z の平行移動 */
/* このとき、a,b,c,deg,x,y,z,X,Y,Zは適当な変数です。*/
投影行列を変化させる場合
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glScalef(a,b,c); /* (a,b,c) 倍に拡大、縮小 */
glRotatef(deg,X,Y,Z); /* (X,Y,Z) のベクトルを中心としたdeg度の回転 */
glTranslatef(x,y,z); /* x,y,z の平行移動 */
glOrtho();
glMatrixMode(GL_MODELVIEW); /* オブジェクト描画の前にモデルビュー行列モードに戻しておく */
/* このとき、a,b,c,deg,x,y,z,X,Y,Zは適当な変数です。*/
ex11-3.c
を改名して、ex11-4.cを作成してください。ヒント
対象物をx軸まわりに一定速度で回転する場合、まず回転角の変数をwhile文の外で宣言します。
double x_deg = 0.0;
while (1) {
/* 略 */
}
while文内のMODELVIEW
の定義文で、glRotatef
関数をコールします。そしてx_deg
をwhile文が繰り返されるたびに 3.0
ずつ増加させます。
対象物が回転するときは、glOrtho
で画面に表示されるZ軸の領域に注意してください。-1.0,1.0
だとすべて表示されませんので、-10.0,10.0
にしてください。
glMatrixMode(GL_PROJECTION); /* 視点側 */
glLoadIdentity();
glOrtho(ORIGIN_X + REAL_SIZE_X * -0.5,
ORIGIN_X + REAL_SIZE_X * 0.5,
ORIGIN_Y + REAL_SIZE_Y * -0.5,
ORIGIN_Y + REAL_SIZE_Y * 0.5,
-10.0, 10.0); /* Z軸領域を拡張 */
glMatrixMode(GL_MODELVIEW); /* 対象物 */
glLoadIdentity();
glRotatef(x_deg, 1.0, 0.0, 0.0); /* X軸まわりにx_deg回転 */
x_deg += 3.0; /* x_degを逐次増加 */
気合いが入っている人は、3次元での立体表示にも取り組んで見て下さい。