ここでは、OpenGLの基礎知識、二次元図形の描き方、glutを使ったウインドウの作り方を解説します。
OpenGLの大きな特色として、"State Machine"であることがあげられます。どういうことかと言うと、OpenGLは与えられた状態を覚えていて、コマンドが発行されると、覚えている状態をつかって実行します。例えば、色を一度指定すると、もう一度指定しなおすまでずっと同じ色で描画を行います。
毎回いろいろ指定しなくてよいので楽だしオーバーヘッドも少ないのですが、状態を意識していないと痛い目にあったりします。
How to buildでも触れたように、このページで扱うサンプルプログラムは3つのライブラリを使います。
OpenGLのAPIの命名則について説明します。例えば、頂点を定義するglVertex〜というAPIがありますが、この仲間は以下のようにいっぱいあります。
glVertex2d, glVertex2f, glVertex2i, glVertex2s, glVertex3d, glVertex3f, glVertex3i, glVertex3s, glVertex4d, glVertex4f, glVertex4i, glVertex4s, glVertex2dv, glVertex2fv, glVertex2iv, glVertex2sv, glVertex3dv, glVertex3fv, glVertex3iv, glVertex3sv, glVertex4dv, glVertex4fv, glVertex4iv, glVertex4sv
このような命名規則はOpenGLライブラリだけで、GLUやglutには適用されません。
なぜ、わざわざ座標系の話をするかというと、OpenGLの座標の取り方が、普通のコンピューターで図形を扱う時と異なるためです。
コンピューターの画面表示は、普通、x軸を画面の左から右に向かって、y軸を上から下にとります。3D処理をするときはさらにz軸を画面手前から奥に向かう方向で取ることが多いと思います。
しかし、OpenGLでは、図のように、y軸が画面下から上、z軸が画面奥から手前になるように取ります。数学で座標系を描くときによくやる取り方ですが、プログラムを書く人種にはちょっとなじみが薄い取り方かも知れません。
OpenGLの基礎で、"State Machine"だと書きましたが、図形を描くのもState Machineの特色が出ています。
具体的に図形を描くには、下のようにglBegin()で、今から何を書くか指定し、次にglVertex〜を何度か呼んで頂点を指定して、glEnd()を呼ぶという手順を取ります。1つの図形を書くために3つも関数を呼ぶのはめんどくさいようにも思えますが、使ってみると複雑な図形でもプログラムをすっきりと書け、便利です。
glBegin(GL_TRIANGLES);
glVertex2f( 0.0, 0.5);
glVertex2f(-0.5, -0.5);
glVertex2f( 0.5, -0.5);
glEnd();
OpenGLではglBegin()の引数を変えることによって、いろいろな図形を描くことができます。以下の表にまとめてみました。
glBegin()の引数と解説 |
説明の図 |
|---|---|
GL_LINES独立した直線を描きます。 |
|
GL_LINE_STRIP連続した直線を描きます。 |
|
GL_LINE_LOOP連続した直線を描きますが、最後に指定した点と最初に指定した点も直線で結ばれます。 |
|
GL_TRIANGLES独立した三角形を描きます。三角形の内側は塗り潰されます。 |
|
GL_TRIANGLE_STRIP連続した三角形を描きます。 |
|
GL_TRIANGLE_FAN連続した三角形で扇形を描きます。 |
|
GL_QUADS独立した四角形を描きます。 |
|
GL_QUAD_STRIP連続した四角形を描きます。 |
|
GL_POLYGON任意の数の頂点を持った多角形を描きます。ただし、GL_POLYGONは遅いので、GL_TRIANGLE_STRIPなどを使って多角形を描いた方がよいという話も聞いたことがあります。 |
|
GL_POLYGONを使うときの注意
GL_POLYGONやGL_QUADSを使う場合、図のような内側にへこんだ図形は正しく描くことができません。このような図形を描くときは、三角形に分割して、GL_TRIANGLE_STRIPなどを使って描く必要があります。
sample01.cを見ながらglut APIの説明をします。今回のサンプルプログラムで使っているAPIは以下の7つです。
void glutInit(int* argc, char** argv)argcとargvは、main()の引数として受け取ったものをそのまま渡してやります。
void glutInitDisplayMode(unsigned int mode)GLUT_RGB(色情報をもつ)を設定します。3D表示をするときには、GLUT_DEPTH(奥行き情報を持つ)も設定したりします。void glutInitWindowSize(int width, int height)int glutCreateWindow(char *name)nameには、ウインドウのタイトルを指定します。返値として、ウインドウの識別子が返ってきますが、このサンプルでは1つのウインドウしか使わないので無視しています。void glutDisplayFunc(void (*func)(void))void glutReshapeFunc(void (*func)(int width, int height))void glutMainLoop()main()
main()関数の中は、glutでウインドウを表示するで解説した、glutの初期化処理やウインドウ作成の処理を行っています。
reshape_func()
次に、reshape_func()です。この関数は、ウインドウの大きさが変化した時に呼ばれて、引数にウインドウの幅と高さが渡されます。ここでは、glViewport()を呼ぶようにしています。
void glViewport(GLint x,GLint y, GLsizei w, GLsizei h)glViewport(0, 0, width/2, height/2)のようにすると、ウインドウの左上1/4に表示されるようになります。このAPIを呼ばないと正しく表示されないことがあるので注意しましょう。display_func()
そうしたら、最後にdisplay_func()の解説をします。まず最初に、glClear()を呼んで画面を消去します。そこに、図形の描き方で説明したように、図形を書き込んでいきます。そして、最後にglFlush()を呼ぶと結果が画面に反映されます。
void glClear(GLbitfield mask)GL_COLOR_BUFFER_BITは、色情報を消去することを示します。void glColor3f(GLfloat red, GLfloat green, GLfloat blue)void glBegin(GLenum mode)glEnd()と対になっていなければなりません。void glVertex2f(GLfloat x, GLfloat y)void glEnd(void)glBegin()と対で呼ばれなければなりません。void glFlush(void)