ウィンドウ内に表示する図形の座標軸は、そのウィンドウ自体の大きさと図形を表示する「空間」との関係で決めます。開いたウィンドウの位置や大きさはマウスを使って変更できますが、その情報はウィンドウマネージャを通じて、イベントとしてプログラムに伝えられます。これまでのプログラムでは、ウィンドウのサイズを変更すると、表示内容もそれにつれて拡大縮小していました。
これを、表示内容の大きさを変えずに、表示領域のみ広げるようにします。これまでのプログラムに resize という関数を追加し、glutReshapeFunc を使ってそれをウィンドウのリサイズ(拡大縮小)のイベントに対するハンドラに指定します。
【ConsoleApplication1.cpp】 軸座標とビューポート(変更点のみ)
void resize(int w, int h)
{
// ウィンドウ全体をビューポートにする
glViewport(0, 0, w, h);
// 変換行列の初期化
glLoadIdentity();
// スクリーン上の表示領域をビューポートの大きさに比例させる
glOrtho(-w / 200.0, w / 200.0, -h / 200.0, h / 200.0, -1.0, 1.0);
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
glutCreateWindow(argv[0]);
glutDisplayFunc(display);
glutReshapeFunc(resize);
init();
glutMainLoop();
return 0;
}
実行結果)
[解説]
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
ビューポートを設定します。ビューポートとは、開いたウィンドウの中で、実際に描画される領域の事を言います。正規化デバイス座標系の2点(-1, -1), (1, 1)を結ぶ線分を対角線とする矩形領域がここに表示されます。
最初の2つの GLint 型(int 型と等価)の引数 x, y にはその領域の左下隅の位置、後ろの2つの GLsizei 型(int 型と等価)の w と h にはそれぞれ幅と高さをデバイス座標系の値、すなわちディスプレイ上の画素数で指定します。
関数 resize の引数 w, h にはそれぞれウィンドウの幅と高さが入っていますから、glViewport(0, 0, w, h) はリサイズ後のウィンドウの全面を表示領域に使うことになります。
void glLoadIdentity(void)
変換行列を初期化します。座標変換の合成は行列の積で表されますから、この関数を使って変換行列に初期値として単位行列を設定します。
void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
ワールド座標系を正規化デバイス座標系に平行投影(orthographic projection:正射影)する行列を変換行列に乗じます。
引数には左から表示領域の左端の位置、右端の位置、下端の位置、上端の位置、前方面の位置、後方面の位置を指定します。これは、ビューポートに表示される空間の軸座標を設定します。
void glutReshapeFunc(void (*callback)(int w, int h))
引数にはウィンドウがリサイズされたときに実行する関数のポインタを設定します。この関数の引数にはリサイズ後のウィンドウの幅と高さが渡されます。
resize の処理によって、プログラムは glViewport で指定した領域に glOrtho で指定した領域内の図形を表示するようになります。このとき glOrtho で指定する領域の大きさをビューポートの大きさに関わらずに一定に保つことができます。
ビューポートの大きさは開いたウィンドウの大きさに一致させていますから、ウィンドウをリサイズしても表示内容の大きさを一定に保つことができます。
図形はワールド座標系と呼ばれる空間にあり、その2点(l, b), (r, t)を結ぶ直線を対角線とする矩形領域を、2点(-1, -1), (1, 1)を対角線とする矩形領域に投影します。この投影された座標系を正規化デバイス座標系と呼びます。この正規化デバイス座標系の正方形領域内の図形がデバイス座標系(ディスプレイ上のウィンドウ)のビューポートにされますので、結果的にワールド座標系から glOrtho で指定した矩形領域を切り取ってビューポートに表示することになります。
ワールド座標系から切り取る領域は CG 用語的にはウィンドウと呼ばれ、ワールド座標系から正規化デバイス座標系への変換はウィンドウウィング変換と呼ばれます。しかし、ウィンドウシステム(X Window や MS Windows)においてはウィンドウはアプリケーションプログラムがディスプレイ上に作る表示領域の事を指すので、ここの説明ではこれを座標軸と呼んでいます。また、正規化デバイス座標系からデバイス座標系への変換はビューポート変換と呼ばれます。
glOrtho では引数として左端位置, 右端位置, 下端位置, 上端位置の他に前方向位置と後方向位置も指定する必要があります。実は、OpenGL は2次元座標図形の表示においても内部的に3次元の処理を行っており、ワールド座標系は奥行き(Z)方向にも軸を持つ3次元空間になっています。前方向位置と後方向位置には、それぞれこの空間の前方向(可視範囲の手前側の限界)と後方向(可視範囲の遠方の限界)を指定します。前方位置より手前にある面や後方位置より遠方にある面は表示されません。
2次元図形は奥行き(Z)方向が 0 の3次元図形として取り扱われるので、ここでは前方向位置を -1.0 とし、後方位置を 1.0 にしています。
glOrtho を使わなければ変換行列は単位行列のままなので、ワールド座標系と正規化デバイス座標系は一致し、ワールド座標系の2点 (-1, -1), (1, 1) を対角線とする矩形領域がビューポートに表示されます。ビューポート内に表示する空間の座標軸が変化しないため、この状態でウィンドウのサイズを変化させると、それに応じて表示される図形のサイズも変わります。初期状態はこのようになっています。
表示図形のサイズをビューポートの大きさに関わらず一定にするには、glOrtho で指定する領域の大きさをビューポートの大きさに比例するように設定します。たとえばワールド座標系の座標軸が上記と同様に l, r, t, b, n, f で与えられているとします。もともとのウィンドウの大きさが W×H、リサイズ後のウィンドウの大きさが w×h なら、glOrtho(l * w / W, r * w / W, b * h / H, t * h / H, n, f) とします。
先ほどのプログラムではワールド座標系の2点 (-1, -1), (1, 1) を対角線とする矩形領域を 200 × 200 の大きさのウィンドウに表示した時の表示内容が常に保たれるように設定しています。
プログラムの起動時に開くウィンドウの位置やサイズを指定したいときには glutInitWindowPosition 及び glutInitWindowSize を使います。これらを使わなければ、プログラムが起動したときに開かれるウィンドウのサイズはウィンドウマネージャの設定に従います。
【ConsoleApplication1.cpp】 位置やサイズを指定してウィンドウを開く(変更点のみ)
int main(int argc, char* argv[])
{
glutInitWindowPosition(100, 100);
glutInitWindowSize(320, 240);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
glutCreateWindow(argv[0]);
glutDisplayFunc(display);
glutReshapeFunc(resize);
init();
glutMainLoop();
return 0;
}
実行結果)
[解説]
void glutInitWindowPosition(int x, int y)
新たに開くウィンドウの位置を指定します。これを指定しないときは、ウィンドウマネージャによってウィンドウの開く位置をを決めます。
void glutInitWindowSize(int width, int height)
新たに開くウィンドウの幅と高さを指定します。これを指定しないときは、300×300 のウィンドウを開きます。
X Window の場合、-geometry オプションによってコマンドラインからウィンドウの開く位置やサイズを指定できます。これは glutInit によって処理されるので、-geometry オプションによる指定を有効にするには、glutInitWindowPosition と glutInitWindowSize を glutInit より前に置き、無効にするには後に置きます。