先ずは OpenGL 描画を行うためのウィンドウを作成してみます。
【ConsoleApplication1.cpp】 空のウィンドウを開く(不要行削除)
int main(int argc, char* argv[])
{
std::cout << "Hello World!\n"; // 削除
}
【ConsoleApplication1.cpp】 空のウィンドウを開く
#include <iostream>
#include <GL/glut.h>
void display()
{
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutCreateWindow(argv[0]);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
実行結果)
[解説]
void glutInit(int *argcp, char **argv)
GLUT 及び OpenGL 環境を初期化します。
引数には main の引数をそのまま渡します。
この関数によって引数の内容が変更されるので、プログラム自身で処理すべき引数がある場合にはこの後で処理します。
int glutCreateWindow(const char *title)
ウィンドウを開きます。
引数の title はウィンドウの名前の文字列でタイトルバーなどに表示されます。以降の OpenGL による描画などは、開いたウィンドウに対して行われます。
戻り値は開いたウィンドウの識別子です。
void glutDisplayFunc(void (*callback)(void))
引数の callback は開いたウィンドウ内に描画する関数へのポインタです。ウィンドウが開かれたり、他のウィンドウによって隠されたウィンドウが現れたりしてウィンドウを再描画する必要があるときにこの関数が実行されます。
void glutMainLoop(void)
この関数を呼び出すことで、プログラムはイベントの待ち受け状態になります。
つまりこのプログラムでは、下記の処理が行われています。
初期化する。
ウィンドウを開く。
描画関数を設定する。
イベントが発生するのを待つ。
通常の標準出力を使ったプログラムなどと違い、中心となる処理(ここでは display)を実行するタイミングが、ソースプログラムを見ただけではわかりません。
最初に display が実行されるのは、初めてウィンドウが開いたとき、すなわち glutMainLoop が glutCreateWindow の指示を受けてウィンドウの生成が完了したときになります。また、その後も、このウィンドウが他のウィンドウに隠されて再び現れたときやウィンドウのサイズを変更したときなど、ウィンドウの再描画が必要になったときに実行されます。
先ほどのサンプルでは display の中身に何も記述していないため、display が呼び出されても何も起こりません。試しにウィンドウを移動したり、他のウィンドウで隠してみたり、ウィンドウのサイズを変更してみたりすると、ウィンドウの表示がおかしくなることがあります。
このように複数のオーバーラップ可能なウィンドウが使えるシステムに対応したプログラムでは、処理の流れは時間軸に沿って「プログラムの開始から終了へ」ではなく、何かこと(事象)が起こるたびに「プログラムの各部がランダムに」実行されます。従ってそのプログラミングスタイルも「事象」に対して、その対処方法を登録していく事になります。この事象をイベントと呼び、対処方法の手続きをハンドラと呼びます。
今度は開いたウィンドウを塗りつぶしてみます。
【ConsoleApplication1.cpp】 ウィンドウを塗りつぶす(変更点のみ)
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
}
void init()
{
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
glutCreateWindow(argv[0]);
glutDisplayFunc(display);
init();
glutMainLoop();
return 0;
}
実行結果)
[解説]
void glutInitDisplayMode(unsigned int displayMode)
ディスプレイ表示モードを設定します。モードに GLUT_RGBA を指定した場合には、色を RGB(光の三原色:赤緑青)で指定しできます。GLUT_INDEX で指定すると効率の良い表示を行うことができますが、指定がそれなりに面倒なので、ここではお任せで使える RGBA モードを使用します。
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
glClearColor(GL_COLOR_BUFFER_BIT) でウィンドウを塗りつぶすときの色を指定します。
red, green, blue はそれぞれ「赤」,「緑」,「青」の成分の強さを表す GLclampf 型(float 型と等価)の値で0~1の値を持ちます。1が最も明るく、この3つに (0, 0, 0) を指定すると「黒」、(1, 1, 1) を指定すると「白」になり、先ほどのサンプルではウィンドウは「青」で塗りつぶされます。
最後の alpha はアルファ値と呼ばれ、不透明度として扱われます。0で透明、1で不透明です。
void glClear(GLbitfield mask)
ウィンドウを塗りつぶします。mask には塗りつぶすバッファを指定します。
OpenGL が管理する画面上のバッファ(メモリ)には下記のようなものがあり、mask に GL_COLOR_BUFFER_BIT を指定したときにはカラーバッファだけが塗りつぶされます。
カラーバッファ
色を格納します。
デプスバッファ
陰面処理に使用します。
ステンシルバッファ
凝ったことをするときに使用します。
オーバーレイバッファ
カラーバッファの上に重ねて使います。
void glFlush(void)
まだ実行していない OpenGL 命令を全部実行します。OpenGL は関数呼び出しによって生成される OpenGL の命令をその都度実行するのではなく、いくつかため込んでおいてまとめて実行します。このため、ある程度の命令がたまらないと、関数を呼び出しても実行が開始されない場合があります。glFlush はそのような状況でまだ実行されていない残りの命令の実行を開始します。頻繁に glFlush を呼び出すと描画速度が低下します。
glClearColor は、プログラムの実行中に背景色を変更することがなければ、最初に一度だけ設定すれば充分です。そこで、このような初期化処理を行う関数は、glMainLoop の前に初期化する関数 init にまとめておくようにしています。
glFlush の代わりに glFinish を使う場合もあります。glFlush がまだ実行されていない OpenGL の命令の実行開始を促すのに加えて、glFinish はそれが全て完了することを待ちます。
gl* で始まる(glu* や glut* で始まらない)関数は、OpenGL の API です。