このプログラムは大きさ3.0の正八面体が x=-4.0〜4.0、y=-3.2〜3.2、z=-5.0〜5.0の範囲をくるくる回りながら動き、 境界に達すると移動方向、回転方向を逆にするというものです。
実行結果をMPEGにした物をここに置きました。 実際のプログラムは無限ループですが、MPEGアニメーションは1000ステップで切っています。 このMPEGアニメーションはあまり綺麗でないです。 本当はこんな絵が描かれます。 なお、実行画面をファイルに落す方法はこちらにあります。
[この章の目次に戻る]
2. プログラムの解説
OpenGLによるプログラミング方法と命令を簡単に解説してみました。
私が必要と思われる最小限のことだけを書いた上に
意訳している部分が多いので本来の意味は「青本」(OpenGL Reference Manual)
やマニュアルのウェブサイトを見て調べて下さい。
何が書いてあるのか意味不明なところも多いですが、
一度はこれらを読んでみることをお勧めします。
[この章の目次に戻る]
2.1 プログラムの流れ
OpenGLによるプログラミングはほとんどほぼ同じ手順で行えます。
私の書いた簡単(でもないですが)な例を見ながらまずプログラムの流れを掴ん
で下さい。
まず、main文から見て行きます。 OpenGLに関する命令は
110: glutInit(&argc, argv); 111: glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); 112: glutInitWindowPosition(0, 0); 113: glutInitWindowSize(WSIZE_X, WSIZE_Y); 114: glutCreateWindow(argv[0]); 115: myInit(); 116: 117: glutDisplayFunc(myDisplay); 118: glutReshapeFunc(myReshape); 119: glutIdleFunc(myIdle); 120: glutMainLoop(); |
glutで始まる関数はOpenGL(GLUT)で用意されているものです。 OpenGLで提供されている関数はgl、glu、glutで始まり、 定数はGL_で始まり、変数型はGLで始まります。
では、110行目から順に見て行きましょう。
110: glutInit(&argc, argv); 111: glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); |
112: glutInitWindowPosition(0, 0); 113: glutInitWindowSize(WSIZE_X, WSIZE_Y); 114: glutCreateWindow(argv[0]); |
115: myInit(); |
117: glutDisplayFunc(myDisplay); 118: glutReshapeFunc(myReshape); 119: glutIdleFunc(myIdle); 120: glutMainLoop(); |
glutDisplayFunc(myDisplay); |
glutReshapeFunc(myReshape); |
glutIdelFunc(myIdle); |
glutMainLoop(); |
OpenGLプログラムの全体の流れは理解できたでしょうか? ここで簡単にプログラムの命令も説明しましたが、以下の節でもう少し詳しく 解説します。
[この章の目次に戻る]
2.2 どの範囲をどこから見るか
これはものすごく重要であるのにも関わらず、どういう訳だか解説本ではあま
りきちんと説明がなされていません。
glMatrixMode(GL_PROJECTION); glOrtho(left, right, bottom, top, near, far); gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz); |
glOrtho();とgluLookAt();はイニシャライズ関数とglutReshapeFunc()で呼び出される関数との両方で実行するようにしましょう。
[この章の目次に戻る]
2.3 光
例ではmyInit();で光(ライト)に関する定義をしています。
光に関しては
glLightfv(light, pname, param); |
pnameは光源パラメータを指定します。色々と指定できますが、
pname | 内容 | param |
---|---|---|
GL_AMBIENT | 環境光 | RGBA |
GL_DIFFUSE | 拡散光 | RGBA |
GL_SPECULAR | 鏡面光 | RGBA |
GL_POSITION | ライトの位置 | XYZW |
この4つの光源パラメータに対応するparamはGLfloat型の4成分ベクトル(配列)です。
環境光、拡散光、鏡面光はRGBAで指定します。
RGBAは赤、緑、青、アルファ値で、0.0〜1.0の値をとります。
アルファ値を指定する必要がなければ1.0にしておけば良いでしょう。
色は配列変数を使わなくても、GLfloat型以外でも指定できます(その場合、glMaterialに続く2文字が変わります)。
ライトの色は白またはグレイにしておけば良いでしょう。
ライトの位置は4番目の値が0.0以外ならば最初の3成分はライトの座標を指定します。 最期の値が0.0ならば最初の3成分の方向からの平行光線となります。
最期に
glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); |
glMaterixfv(face, pname, param); |
faceは物体の面を指定します。 GL_FRONTが表、GL_BACKが裏、GL_FRONT_AND_BACKが両面です。
pnameは光源パラメータを指定します。色々と指定できますが、
GL_AMBIENT_AND_DIFFUSEで環境光と拡散光による色をまとめて指定してしまえば良いでしょう。
他に、GL_AMBIENT(環境光)、GL_DIFFUSE(拡散光)、GL_SPECULAR(鏡面光) 、GL_EMISSION(放射光)、GL_SHININESS(鏡面光の指数)、GL_COLOR_INDEXES(環境光、拡散光、鏡面光の色指標)が指定できます。
paramはRGBAで記述した配列変数(GLfloat型)です。
[この章の目次に戻る]
2.5 物体の描画
ここに挙げた例ではglutで用意されている球を描く関数を使わせてもらいましたが、
OpenGLには線や面を描く命令が用意されています。
ここでは面を描く命令の説明をします。
線を描く命令もありますが、
3次元では線は意味をなさないのでここでは説明しません。
(x0, y0, z0)、(x1, y1, z1)、(x2, y2, z2)の3点を通り、 法線ベクトルが(nx, ny, nz)の三角形を描く場合は、
glBegin(GL_TRIANGLES); glNormal3f(nx, ny, nz); glVertex3f(x0, y0, z0); glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2); glEnd(); |
四角形の場合はglBegin(GL_QUADS);になります。
また、glVertex3f()の数が4つになります。
4つの頂点からどうやって1つの平面を描いているのか謎です。
暇な方はソースを読んで下さい。
[この章の目次に戻る]
2.6 物体の位置
決まった場所に描いた物体を別の場所に移動させたり、
拡大・縮小することができます。
glTranslatef()、glRotatef()、glScale()がその命令です。
glTranslatef(x, y, z); |
glRotatef(θ, x, y, z); |
glScale(x, y, z); |
これらの命令は重ね合わせることができます。 例えば、
glRotatef(30.0, 0.0, 0.0, 1.0); glTranslatef(1.0, 2.0, 3.0); gltranslatef(4.0, 5.0, 6.0); |
これらの命令は普通
glPushMatrix(); |
glPopMatrix(); |
[この章の目次に戻る]
3. コンパイルと実行方法
以下のようなMakefileを作ってmakeすると簡単です。
CC = cc CFLAGS = -lm INCDIR = /usr/local/include LIBDIR = /usr/local/lib XLIBDIR = /usr/X11R6/lib XLIBS = -L$(XLIBDIR) -lX11 -lXext -lXmu -lXt -lXi -lSM -lICE GL_LIBS = -L$(LIBDIR) -lglut -lMesaGLU -lMesaGL $(XLIBS) TARGET = prg .c: $(CC) -o $(TARGET) -I$(INCDIR) $(CFLAGS) $< $(GL_LIBS) all: $(TARGET) |