見出し画像

【降臨】【正十二面体】【第1回c言語】

お待たせしましたプログラミング回!毎週木曜と言っておきながら火曜更新です。気分屋です。
本記事は、長いです。目次を参考に気になるところや、知りたいところだけを見てみるという読み方を推奨させていただきます。
第0回にて掲げた目標は、アフィン変換とキーイベントをマスターしたいということでありました。
また、コードは、いくつか重要な部分を抜き出して、紹介することにします。(コードが長くなってしまったので。)→(反省会もいつかやって、コードを短くしたり、簡潔にしたりしたいな)


正十二面体の作画

初めは、正五角形を一つ作ってアフィン変換で正十二面体をつくれたらなと考えたのですが、初期の段階でうまく表示されなくなってしまったので(投稿日も迫ってきて焦ったので)、とりあえず、いったん、すべて座標にして考えようと企みました。正十二面体の面の数が12であることと、正六面体、つまりキューブが、12個の辺を持つことに注目して、座標を考えました。キューブの一つの辺に正十二面体の一つの面が次の図のように対応します。

正六面体と正十二面体の一つの面


他の面も考えると、

正六面体と正十二面体


しばらく眺めていると、次の一区画を六個考えればいいことに気づきます。

一区画(赤)

一区画に六個の座標が存在しますが、それらのx,y,z座標の値を入れ替えたものと、それらのx、y、zの符号を変えたものを考えれば、正十二面体は完成しますね。面が、五角形になるようにGL_POLYGONで、書いていくと、正十二面体は完成します。

以下コードの一部(一つ目の五角形作画)

#include<GL/glut.h>
#include<math.h>

GL_float AngleX=0.0;
GL_float AngleY=0.0;
GL_float AngleZ=0.0;

void drawXYZ(void) {
	glBegin(GL_LINES);
	glColor3f(1.f, 1.f, 0.f);//x黄
	glVertex3f(0.f, 0.f, 0.f);
	glVertex3f(200.f, 0.f, 0.f);

	glColor3f(0.f, 1.f, 1.f);//y水
	glVertex3f(0.f, 0.f, 0.f);
	glVertex3f(0.f, 200.f, 0.f);

	glColor3f(1.f, 0.f, 1.f);//z紫
	glVertex3f(0.f, 0.f, 0.f);
	glVertex3f(0.f, 0.f, 200.f);
	glEnd();

	glColor3f(1.f, 1.f, 1.f);
}

void drawDodecahedron(void) {
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);


	//1
	glBegin(GL_POLYGON);
	glColor3f(1.f, 0.f, 0.f);
	glVertex3f(-0.3708f, 0.f, -0.975f);
	glColor3f(1.f, 0.f, 0.f);
	glVertex3f(-0.6f, -0.6f, -0.6f);
	glColor3f(1.f, 0.f, 0.f);
	glVertex3f(0.f, -0.9756f, -0.3708f);
	glColor3f(1.f, 0.f, 0.f);
	glVertex3f(0.6f, -0.6f, -0.6f);
	glColor3f(1.f, 0.f, 0.f);
	glVertex3f(0.3708f, 0.f, -0.975f);
	glEnd();

~コード解説(基本情報)~
・drawXYZ(void)で各軸を描画。
・GL_LINESで線を引くことができます。
・glColor3fの座標はRGB三原色を表しています。、glColor3f(red,green,blue)       となっていて、たとえばglColor3f(1.0,1.0,1.0)だと、白色になります。
・glVertex3fは、glVertex3f(x座標,y座標,z座標)という風に使うので、
 x軸を引きたいときは、
  glVertex3f(0.f,0.f,0.f);
  glVertex3f(200.f,0.f,0.f);
 と、書いたのです。
 二次元で描画するときは、glVertex2fでいいのです。
・GL_POLYGONでは、多角形を描画できます。
 今回は五角形の描画を希望したので、座標も5個です。
・座標計算は、せっかくmath.hをインクルードしたので、M_PI(π)を使って計算するのがより良いと思います。
※「   }」を閉める前に、glFlush();で終わりを示すことを忘れないようにしてください。
※コードを全て載せると長くなりすぎてしまうの今回は、五角形一つ分までしか載ってませんが、同じことを繰り替えせば、正十二面体は書けます。


アフィン変換

先ほど作った正十二面体を、キーボードを押すとx、y、z軸周りにそれぞれ回転するようにしたい。ここで求められる作業は、アフィン変換と、キーイベント処理であるが、まずはアフィン変換を考えたい。
そもそもアフィン変換とは、描画した図形や、視点などの場所を変える時に用いるものです。

以下、コードである。

void display(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
	glRotatef(AngleX, 1.0, 0.0, 0.0);
	glRotatef(AngleY, 0.0, 1.0, 0.0);
	glRotatef(AngleZ, 0.0, 0.0, 1.0);
	drawDodecahedron();
	glPopMatrix();

	drawXYZ();
	glFinish();

}

~解説~
⦅・display(void)内では、画面に表示したいものを記しておきます。⦆
⦅・glCrearとは、初期化を行うときに用います。前回の描画内容を削除します。正確な描画のために、重要なひと手間です。今回は、COLOR_BUFFERとDEPTH_BUFFER(三次元空間の描画を行うので。&後の工程でがっつり出てきます。)を含むので、これら二つを初期化します。⦆
・アフィン変換を行うコードは、glPushMatrix();で始め、glPopMatrix();で終わるのが良いでしょう。アフィン変換は、行われる順番が特徴的で不思議です。下から上に行われていきます。今回の様にx、y、z軸にたいしてそれぞれを回すだけのときなどはあまり関係ないのですが、例えば、
 glRotatef(?,0.0,1.0,0.0);
 glTranslatef( ? , ? , ? );
のような場合は、描画した図形の動きとしては、(?,?,?)に平行移動してからy軸まわりに?回転します。コード書く順番を逆にしてしまっただけでも全く違う場所に図形が移動していることがあります笑笑。
glRotatef(X,1.0,0.0,0.0);は、x軸まわりにX度まわすという意味で、glRotatef(回転させたい角度 , x軸周りか , y軸周りか , z軸周りか)というシステムになっていて、「?軸周りか」の場所には、Yesなら1.0、Noなら0.0を入れるのです。
glRotatef内で、AngleXなどと置いてるのは、キーイベントで、キーを押すたびに回転させたいからです。(詳しくは次項へ)
何をアフィン変換で場所を変えたのかを書かないといけませんよね。今回は、drawDodecahedron()です。

キーイベント

void specialKeys(int key, int x, int y) {
	switch (key) {
	case GLUT_KEY_LEFT:  // 左矢印キー
		AngleY -= 15.0;
		glutPostRedisplay();
		break;

	case GLUT_KEY_RIGHT:  // 右矢印キー
		AngleY += 15.0;
		glutPostRedisplay();
		break;

	case GLUT_KEY_UP:  // 上矢印キー
		AngleX -= 15.0;
		glutPostRedisplay();
		break;

//~途中省略~

	default:
		break;
	}
}

~解説~
・先ほどAngleX~Zでおいた文字に代入する値を、キーを押すたびに15度ずつ増やすようにしたい。また、回しすぎてしまったときにすぐに反対側に回して戻りたいので、+とーを考えました。
・今回の様に、~したら…..するように動かしたいときに使うコード文は、一番思いつきやすく簡単なのはifであると思います。しかし、今回はX~Z軸の三軸、かつ+ーも含めて、全六パターンキーを設定したいので、switch~case文を使いました。(指令分の枝分かれが多いときはswitch~caseが重宝されます。)break;を使うことで、一つのアクションに、一つの結果が出力されます。使わないと、次のcase内のアクションが続いてしまったりします。全く違うキーを押してしまった場合は、defalt;で、すぐにbreakするようにしてあるので、何も起こりません。
・glutPostRedisplay();は、再描画をおこなうときに使用します。glutDisplayFuncで指定された描画(さっきのアフィン変換のくだりのところ)が呼び出されます。キーイベントやマウスクリックイベントでよく使います。

残り(視点や見る方向を定める関数とmain関数)のコード

以下は今回のメインではないので参考程度に…(実はちょっと疲れちゃったのもありますんまへん。)

void reshape(int w, int h) {
	glViewport(0, 0, w, h);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(30.0, double(w) / h, 0.1, 200.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(1.0, 2.5, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

}
void otherInit(void) {
	glClearColor(1.f, 1.f, 1.f, 1.f);
	glClearDepth(1.f);
	glEnable(GL_DEPTH_TEST);
}
int main(int argc, char* argv[]) {
	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_RGB);

	glutInitWindowSize(640, 640);
	glutCreateWindow("Dodecahedron");

	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutSpecialFunc(specialKeys);

	otherInit();

	glutMainLoop();

	return 0;

}

・glLookAtは次のような仕組みです。glLookAt(視点のx座標 , 視点のy座標 , 視点のz座標 , 視点の先のx座標 , 視点の先のy座標 , 視点の先のz座標 , ベクトルx, ベクトルy , ベクトルz)となっています。ベクトルは、上向きを指定するためのもので、たとえばy軸を上向きにしたければ、….., 0.0 , 1.0 , 0.0)のように上向きの軸に1.0と書くと良いのであります。

以上の様に書いていくと、次のようにウィンドウに表示されます。

矢印キー等を押すと回ります。

実は矢印キー等を押すと軸に対して回転するようになっていますが、記事上ではお見せできないのが残念です。


反省

誰がどう考えても今回の記事は分量が長すぎである。スライドが長すぎる人の話は集中できないと、授業中に心の中で文句言ってるくせに同じことをしてしまった。大反省である。もしここまで読んでくれた方がいらっしゃったのならありがたいです。第二回目以降はもっとわかりやすい説明が書けるように努力します。
アフィン変換は、回転しかさせてないじゃないかと言われればもうその通りなので…マスターするまでの道のりは遠いかもしれません涙。
また、正十二面体を書くコードが長すぎでした。もっと簡潔にコンパクトに書けないかと、反省する会を今後設けるのもありかもしれないですね。

次回 : 【第二回】正二十面体を正十二面体の中に描画する。

この記事が気に入ったらサポートをしてみませんか?