前回までの実習で、交差点の地図がウィンドウに表示されるプログラムができていたと思います。しかし、このプログラムでは、地図の表示位置や大きさが固定です。それに地図の向きも常に北が上になっています。より見易い表示のためには、地図の任意の位置を拡大して表示したり、回転して表示する方が良いのかもしれません。
では、地図の任意の位置を中心に拡大/回転表示するにはどのようなテクニックが必要なのでしょうか。答えは、「座標変換」です。データとして持っている交差点の位置と、それを表示するときのウィンドウ上での位置は同じではありません。このため、「地図上の位置座標」→「ウィンドウ上の位置座標」の変換が必要となります。この変換のことを、「座標変換」といいます。前回の実習のプログラムにも、当然座標変換のための数式が書かれています。この数式を変更することにより、地図の表示位置を変えたり、拡大縮小をしたり、回転を実現することができます。
このページでは、様々な地図表示のための座標変換式の作り方を解説します。
前回までの実習の「グラフィックスプログラミング(データの可視化)」で作成したプログラムを見て下さい。このプログラムの先頭付近に、次のような部分が ありますね。
/* 座標変換マクロの定義 */
#define ORIGIN_X -2.0
#define ORIGIN_Y 3.0
#define REAL_SIZE_X 10.0
#define REAL_SIZE_Y 10.0
#define
というのはマクロ定義を行なう文です。 以下に、これらのマクロを利用している関数、glOrtho
を示します。 glOrtho
は投影行列を正射影に設定する関数です。x
に関してはleft
からright
まで、y
に関してはtop
からbottom
までを射影領域とします。zNear
, zFar
は奥行きを示します。そのため、left
からright
まで、top
からbottom
までの領域が画面に描かれることになります。
glOrtho(left, right, bottom, top, zNear, zFar)
/* (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);
つまり、これは次のような座標変換を行なっていることになります。
(left) = -2.0 + 10.0 * (-0.5);
(right) = -2.0 + 10.0 * (0.5);
(top) = 3.0 + 10.0 * (-0.5);
(bottom) = 3.0 + 10.0 * (0.5);
皆さんお分かりとは思いますが、 (-2.0, 3.0)
は地図の中心の(ウィンドウ上での)表示位置を表します。また、10.0
はx
, y
それぞれのスケールを表します。すなわち、地図上での10.0
がウィンドウ上での1.0
に相当することになります。
では、地図の他の交差点(例:六丁の目)がウィンドウの中心に来るように、 プログラムを変更してみて下さい。ファイルmap.c
を改名して、ex11-1.c
を作成してください。変更を加えて、画面表示してください。また、地図のスケールを変更し、仙台市街部を拡大したり、全域を縮小表示したりしてみて下さい。表示中心の変更は
#define ORIGIN_X -2.0
#define ORIGIN_Y 3.0
を修正することでできます。拡大縮小は
#define REAL_SIZE_X 10.0
#define REAL_SIZE_Y 10.0
を修正することでできます。
さて、マクロを利用して平行移動と拡大縮小ができたことは確認できました。昔のプログラムではこの他に回転に関しても自分で座標の回転行列を考えて処理する必要がありました。しかし、OpenGLを利用することにより、これらの座標変換、平行移動と回転、拡大縮小はとても簡単になります。ただ、何をどのように回転させるのかをきっちり理解する必要があります。
第6回のOpenGL 簡単な解説のところで説明されているとおり、OpenGLではモデルビュー変換と、投影変換という変換行列があります。この変換行列に拡大行列、回転行列、並進行列をかけることで回転と並進を実現できます。
今回はモデルビュー行列に対する拡大、回転、並進を施す方法を説明します。 モデルビュー行列の記述は、
glMatrixMode(GL_MODELVIEW);
からはじまります。これまではモデルビュー行列に対して何もかけていません でした。今回はここに、回転行列や並進行列をかけてみます。
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は適当な変数です。*/
第9回で作成したファイルmap.c
を改名して、ex11-2.c
を作成してください。モデルビュー行列の定義に拡大・縮小(glScalef
関数)、回転(glRotatef
関数)、平行移動(glTranslatef
関数)を追記して、画面表示がどのように変化するか確認しましょう
#define
でORIGIN_X
とORIGIN_Y
をゼロにしてから試しましょう
#define ORIGIN_X 0.0
#define ORIGIN_Y 0.0
#define REAL_SIZE_X 8.0
#define REAL_SIZE_Y 8.0
また、glLoadIdentity()
をなくすとどうなるか、glScalef()
、glRotatef()
とglTranslatef()
の 順番をかえるとどうなるか確認してみましょう。
このように、行列の初期化、拡大、縮小、回転と並進がそれぞれの順番に意味があることが わかります。
OpenGLでの行列演算は左から行列をかけることに相当するので、 後にやるべき行列演算を先に記述する必要があります。
正しく地図を、平行移動、拡大縮小、回転表示できましたか?