地面の上をロボットが歩いているようなアニメーションを表示するプログラムを作ってみましょう. これまでに作ったプログラムをベースを改造するのが手っ取り早いと思いますが, うまく行かなければ下の手順を参考にしてください. このプログラムは結構長いので, 時間が無ければ Web ページからコピーアンドペーストして構いません. ソースファイル名は prog7.c としてください.
下のプログラムは地面の上に立っている 四角い箱でできたロボットのような物体(箱男)を静止画で表示します. これが地面の上を歩き回るようにしてください. 歩き方は自由ですが, できるだけ自然に歩いているように見えるようにしてください.
#include <stdlib.h> #include <GL/glut.h> #define STEPCYCLE 400 /* 手足のひと振りに要する時のフレーム数 */ #define WALKCYCLE 4000 /* ステージ上を一周するのに要するフレーム数 */ /* * 直方体を描く */ static void myBox(double x, double y, double z) { GLdouble hx = x * 0.5, hz = z * 0.5; GLdouble vertex[][3] = { { -hx, -y, -hz }, { hx, -y, -hz }, { hx, 0.0, -hz }, { -hx, 0.0, -hz }, { -hx, -y, hz }, { hx, -y, hz }, { hx, 0.0, hz }, { -hx, 0.0, hz } }; const static int face[][4] = { { 0, 1, 2, 3 }, { 1, 5, 6, 2 }, { 5, 4, 7, 6 }, { 4, 0, 3, 7 }, { 4, 5, 1, 0 }, { 3, 2, 6, 7 } }; const static GLdouble normal[][3] = { { 0.0, 0.0,-1.0 }, { 1.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0 }, {-1.0, 0.0, 0.0 }, { 0.0,-1.0, 0.0 }, { 0.0, 1.0, 0.0 } }; const static GLfloat red[] = { 0.8, 0.2, 0.2, 1.0 }; int i, j; /* 材質を設定する */ glMaterialfv(GL_FRONT, GL_DIFFUSE, red); glBegin(GL_QUADS); for (j = 0; j < 6; ++j) { glNormal3dv(normal[j]); for (i = 4; --i >= 0;) { glVertex3dv(vertex[face[j][i]]); } } glEnd(); } /* * 腕/足 */ static void armleg(double girth, double length, double r1, double r2) { glRotated(r1, 1.0, 0.0, 0.0); myBox(girth, length, girth); glTranslated(0.0, -0.05 - length, 0.0); glRotated(r2, 1.0, 0.0, 0.0); myBox(girth, length, girth); } /* * 地面を描く */ static void myGround(double height) { const static GLfloat ground[][4] = { { 0.6, 0.6, 0.6, 1.0 }, { 0.3, 0.3, 0.3, 1.0 } }; int i, j; glBegin(GL_QUADS); glNormal3d(0.0, 1.0, 0.0); for (j = -5; j <= 5; ++j) { for (i = -5; i < 5; ++i) { glMaterialfv(GL_FRONT, GL_DIFFUSE, ground[(i + j) & 1]); glVertex3d((GLdouble)i, height, (GLdouble)j); glVertex3d((GLdouble)i, height, (GLdouble)(j + 1)); glVertex3d((GLdouble)(i + 1), height, (GLdouble)(j + 1)); glVertex3d((GLdouble)(i + 1), height, (GLdouble)j); } } glEnd(); } /* * 画面表示 */ static void display(void) { const static GLfloat lightpos[] = { 3.0, 4.0, 5.0, 1.0 }; /* 光源の位置 */ static int frame = 0; /* フレーム数 */ /* STEPCYCLE に指定した枚数のフレームを描画する間に 0→1 に変化 */ double t = (frame % STEPCYCLE) / (double)STEPCYCLE; /* WALKCYCLE に指定した枚数のフレームを描画する間に 0→1 に変化 */ double s = (frame % WALKCYCLE) / (double)WALKCYCLE; /* * 以下の変数に値を設定する */ double ll1 = 0.0; /* 箱男の左足の股関節の角度 */ double ll2 = 0.0; /* 箱男の左足の膝関節の角度 */ double rl1 = 0.0; /* 箱男の右足の股関節の角度 */ double rl2 = 0.0; /* 箱男の右足の膝関節の角度 */ double la1 = 0.0; /* 箱男の左腕の肩関節の角度 */ double la2 = 0.0; /* 箱男の左腕の肘関節の角度 */ double ra1 = 0.0; /* 箱男の右腕の肩関節の角度 */ double ra2 = 0.0; /* 箱男の右腕の肘関節の角度 */ double px = 0.0, pz = 0.0; /* 箱男の位置 */ double r = 0.0; /* 箱男の向き */ double h = 0.0; /* 箱男の高さ */ /* フレーム数(画面表示を行った回数)をカウントする */ ++frame; /* 画面クリア */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* モデルビュー変換行列の初期化 */ glLoadIdentity(); /* 光源の位置を設定 */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* 視点の移動(物体の方を奥に移す)*/ glTranslated(0.0, 0.0, -10.0); /* シーンの描画 */ /* 地面 */ myGround(-1.8); /* 箱男の位置と方向 */ glTranslated(px, h, pz); glRotated(r, 0.0, 1.0, 0.0); /* 頭 */ myBox(0.20, 0.25, 0.22); /* 胴 */ glTranslated(0.0, -0.3, 0.0); myBox(0.4, 0.6, 0.3); /* 左足 */ glPushMatrix(); glTranslated(0.1, -0.65, 0.0); armleg(0.2, 0.4, ll1, ll2); glPopMatrix(); /* 右足 */ glPushMatrix(); glTranslated(-0.1, -0.65, 0.0); armleg(0.2, 0.4, rl1, rl2); glPopMatrix(); /* 左腕 */ glPushMatrix(); glTranslated(0.28, 0.0, 0.0); armleg(0.16, 0.4, la1, la2); glPopMatrix(); /* 右腕 */ glPushMatrix(); glTranslated(-0.28, 0.0, 0.0); armleg(0.16, 0.4, ra1, ra2); glPopMatrix(); glFlush(); } static void resize(int w, int h) { /* ウィンドウ全体をビューポートにする */ glViewport(0, 0, w, h); /* 透視変換行列の指定 */ glMatrixMode(GL_PROJECTION); /* 透視変換行列の初期化 */ glLoadIdentity(); gluPerspective(30.0, (double)w / (double)h, 1.0, 100.0); /* モデルビュー変換行列の指定 */ glMatrixMode(GL_MODELVIEW); } static void keyboard(unsigned char key, int x, int y) { /* ESC か q をタイプしたら終了 */ if (key == '\033' || key == 'q') { exit(0); } } static void init(void) { /* 初期設定 */ glClearColor(1.0, 1.0, 1.0, 1.0); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH); glutCreateWindow(argv[0]); glutDisplayFunc(display); glutReshapeFunc(resize); glutKeyboardFunc(keyboard); init(); glutMainLoop(); return 0; }
このプログラムでは,箱を描く関数 myBox() と,その箱を組み合わせて腕や足を描く関数 armleg() を定義しており,それらを使って箱男を描いています. 後者の armleg() の第2・第3引数の r1, r2 を時間とともに変化させれば,腕や足を振るような動作が表現できます.
動作の開始や停止は, マウスのボタンを使ってコントロールするようにしてください.
動きの作り方については,以下を参考にしてください.
なお,これはあくまで「一例」に過ぎません. 自分の思う方法で歩き方を考えてくれれば結構です. ただし,一応は歩いているように見えるようにしてください. スキップしているというのも可.