OpenGLとは、Silicon Graphics社が中心となって開発した、グラフィック関連の処理を担ってくれるプログラミングインターフェースである。グラフィック関連のインターフェースには有名なものとしてDirectXなどがあるが、DirectXとは違い、OpenGLはハードウェアやOSに依存しない形に改良されているため、同じコードでWindows上でもLinux上でも動作させることができる。公式Webページは「http://www.opengl.org/」である。OpenGLの実体はDLLとヘッダファイルであり、一般的なプログラムを作成するときにライブラリファイルをリンクさせることで、OpenGLが提供するAPI(関数群)を利用することができる。
そして、今回のサンプルプログラムは「xi.cpp」である。
ちなみに、このテキストでは、OpenGLに関する基本的なことを解説していない。OpenGLに関する入門サイトは「GLUTによる「手抜き」OpenGL入門」がお勧めである。もし分からなければここを参照してほしい。
2次元平面において、任意の円を描画するのに必要な情報は「円の中点」と「半径」である。

これは3次元空間においても同じである。3次元空間の場合は球体となるが、球体の中心点の座標と、その球体の半径を得ることで、3次元空間に任意の球体を決定できる。
そして、正方形とは、各点が2次元平面に描かれた円上に存在するもっとも大きな長方形である。同じく、立方体とは、各点が3次元空間に描かれた球体の中に納まるもっとも大きな直方体であり、各頂点は球体の表面に接触する。よって「立方体の中心点から頂点への距離」は、「球体の半径」と等しいことになる。つまり「傾き(角度)」を考えなければ、任意の球体の決定は、任意の立方体の決定となる。
これらの理由から、3次元空間における立方体は、中心点の座標と、中心点から頂点への距離のみで表すことが可能であることが分かる。ただ、OpenGLで立方体を描画する場合、各頂点を指定する順番がややこしい問題となってくるので、サンプルプログラムでは、立方体の情報を、中心点の座標と頂点への距離ではなく、「中心点の座標」と「基点となる頂点の座標」で示している。
また、実際に3次元空間に立方体を描画するためには、立方体の8つの頂点を指定する必要がある。よって、立方体の中心点から、この8つの頂点を計算する。

上記の図は、1辺が1の長さを持つ立方体である。1辺が1であるため、この立方体の中心点の座標は(0.5, 0.5, 0.5)に存在することが分かる。よって、この中心点(0.5, 0.5, 0.5)から8つの頂点を求める方法を考える。これは至って簡単である。すべての頂点は、中心点から、(0.5, 0.5, 0.5)の距離に存在する。よって、中心点にその距離を加算することで求められる。
頂点G: (x, y, z) = (0.5, 0.5, 0.5) + ( 0.5, 0.5, 0.5) = (1.0, 1.0, 1.0) 頂点H: (x, y, z) = (0.5, 0.5, 0.5) + ( 0.5, 0.5, -0.5) = (1.0, 1.0, 0.0) 頂点F: (x, y, z) = (0.5, 0.5, 0.5) + ( 0.5, -0.5, 0.5) = (1.0, 0.0, 1.0) 頂点H: (x, y, z) = (0.5, 0.5, 0.5) + (-0.5, 0.5, 0.5) = (0.0, 1.0, 1.0) 頂点B: (x, y, z) = (0.5, 0.5, 0.5) + ( 0.5, -0.5, -0.5) = (1.0, 0.0, 0.0) 頂点E: (x, y, z) = (0.5, 0.5, 0.5) + (-0.5, -0.5, 0.5) = (0.0, 0.0, 1.0) 頂点D: (x, y, z) = (0.5, 0.5, 0.5) + (-0.5, 0.5, -0.5) = (0.0, 1.0, 0.0) 頂点A: (x, y, z) = (0.5, 0.5, 0.5) + (-0.5, -0.5, -0.5) = (0.0, 0.0, 0.0)
3次元空間の中に任意の1点と基点となる1点を決定すれば、立方体を描画できる。
傾き(角度)を考えないのならば、「中心点の座標」と「中心点から頂点までの距離」だけで立方体の描画を行うことができる。しかし、立方体に傾きを取り入れると、「中心点の座標」「中心点から頂点までの距離」の他にもうひとつ「回転の角度」が必要になる。

(a1, b1)の点を立方体の中心点とし、立方体の頂点(x1. y1)を、角度Q分だけ回転させたのが頂点(x2, y2)だ。頂点(x1, y1)は、立方体の中心点に対して、45度の角度を持っている状態であり、それからQ度だけ回転させるということは、それぞれの頂点に対して、次の関係が成り立つ。
(現在の角度 + Q) = 回転角度
そして、回転角度から、頂点の位置を決定する計算式は、円の半径をL(エル)とすると、次のようになる。
(x2, y2) = (a1 + L * cos(現在の角度 + Q), b1 + L * sin(現在の角度 + Q))
中心点を軸にして、立方体の8つの各頂点に上記の演算を加えることで、傾いた状態の立方体を描画することができる。ただし、立方体の右上の頂点に対しては現在の角度が45度だが、他の頂点では、現在の角度がそれぞれ、135度、225度、315度となる。よって、それぞれの頂点に対して現在の角度を指定しておく必要がある。
上記の計算式から「中心点の座標」と「中心点から頂点までの距離」そして「回転角度」で、任意の立方体を3次元空間に描画できることが分かる。ただし、3次元空間の場合、回転角度は2次元である。
球体の表面の任意の1点は、(x, y)の2つの角度(つまり2次元空間と同じ)で表すことができる。つまり、最終的には、「中心点の座標」と「中心点から頂点までの距離」、そして、「2次元の回転角度」が必要となる。ただし、サンプルプログラムでは、分かりやすさを重視するために、回転角度を1次元としている。よって、1次元方向への回転にしか対応していない。
立方体が、ある頂点を軸に円運動を行う場合の立方体の中心点の移動軌跡を調べる。立方体だと分かりにくいので、2次元の正方形を利用して解説する。

(a1, b1)の点を立方体の中心点とする。この状態で、点oを中心として、サイコロのように立方体を転がすと、立方体の中心点は、点oを中心とした半径√2(x/2)の円運動を行うことが分かる。これにより、中心点の移動は次の計算式で表すことができる。
(a, b) = (o1 + √2(x/2) * cos(r), o2 + √2(x/2) * sin(r))
点(a, b)は、点oを中心とした半径√2(x/2)の円上の任意の座標である。上記の式に任意の角度rを与えることによって、この円上の任意の座標(a, b)を求めることができる。
ただし、上記の図より、点(a1, b1)は点oに対して、あらかじめ45度の角度に位置している状態なので、それから90度移動した位置が、立方体をサイコロのように移動した後の位置(a2, b2)となる。このような動作から、(a2, b2)を求める計算式を次に示す。実際にはこのような計算式を用いることになるだろう。
(a2, b2) = (o1 + √2(x/2) * cos(45 + Q), o2 + √2(x/2) * sin(45 + Q))
また、上記の図を見ても分かる通り、最初の角度が45度であり、最後の角度が135度であることから、Qは(45 < r <= 135)の範囲で変動することになる。
つまり、サイコロ移動時のプログラムは次の計算処理を繰り返すことになる。
(a, b) = (o1 + √2(x/2) * cos(45 + 1), o2 + √2(x/2) * sin(45 + 1)) (a, b) = (o1 + √2(x/2) * cos(45 + 2), o2 + √2(x/2) * sin(45 + 2)) (a, b) = (o1 + √2(x/2) * cos(45 + 3), o2 + √2(x/2) * sin(45 + 3)) ... ... ... (a, b) = (o1 + √2(x/2) * cos(45 + 89), o2 + √2(x/2) * sin(45 + 89)) (a, b) = (o1 + √2(x/2) * cos(45 + 90), o2 + √2(x/2) * sin(45 + 90))
そして、その時々の(a, b)の座標が、立方体の中心点となる。その中心点を元に、立方体の各頂点を計算し描画すれば、位置だけを模倣したサイコロ移動が実現できる。この移動に関しての詳しい動作は、サンプルプログラムを実行し、上下キーにて移動を行って確認して欲しい。
立方体をサイコロのように移動させるには、位置を移動した後、任意の角度を立方体に与えて描画しなければならない。例えば、立方体の中心点をQ分だけ移動したら、立方体の各頂点もまた、Q分だけ傾かなければならない。

最初に中心点を移動させ、そこに立方体を移動している。中心点は角度Q分だけ移動している。そして次に、同じQ分だけ、立方体を回転させる。立方体の各頂点をQ分だけ回転させることで、最終的に、頂点oを中心として立方体全体を回転させたように見える。
つまり、中心点の移動と、立方体そのものの回転を、Q分だけ行うことで、サイコロ移動を実現しているわけである。この動作については、サンプルプログラムを実行し、左右のキー入力で立方体を動かすことで確認することができる。
最後にサンプルプログラムで定義している関数の簡単な解説を行う。ソースコードの先頭から順番に関数を見ていくことにする。
----- キーボード入力に関する関数 int inputdata(int in); void spkeyboard(int key, int x, int y); -----
まずは、キーボード入力に関する関数が定義されている。spkeyboardはスペシャルキー入力を取得する関数で、main関数内でglutSpecialFunc関数にて登録されている。inputdata関数は現在のキー入力情報を保持する関数である。
----- 角度を変換する関数 GLdouble DegToRad(GLdouble Deg); GLdouble RadToDeg(GLdouble Rad); -----
これらは角度を変換する関数群である。角度の表記は、DegreeとRadianがあるため、それらを相互に変換するために作成した。DegToRad関数はDegreeからRadianへ、RadToDeg関数はRadianからDegreeへ変換する。
----- 3次元座標に対する演算処理 void Copy3d(GLdouble *dest, GLdouble *src); GLdouble *Add3d(GLdouble *a, GLdouble *b); GLdouble *Sub3d(GLdouble *a, GLdouble *b); -----
これらは3次元座標に対する演算処理を行う関数群である。座標配列に対し、Copy3d関数は代入、Add3d関数は加算、Sub3d関数は減算を、それぞれ行う。
----- 立方体の生成と描画を行う関数
GLdouble *Add3da(GLdouble *a, GLdouble x, GLdouble y, GLdouble z);
void DrawCube(GLdouble **vertex);
GLdouble *RotatePoint(GLdouble *o, GLdouble x, GLdouble r, int flag);
void RotatePoints(GLdouble **vertex, int *num,
GLdouble *o1, GLdouble *o2, GLdouble x, GLdouble r, int flag);
void MakeCube(GLdouble **vertex, GLdouble *center, GLdouble *corner);
void RotateCube(GLdouble **vertex,
GLdouble *center, GLdouble *corner, GLdouble *r, int flag);
-----
これらは立方体の生成と描画を行う関数郡である。Add3da関数はAdd3d関数の改造版であり、実質的な動作は変わらない。DrawCube関数は、ディスプレイに立方体を描画する。RotatePointとRotatePointsは、それぞれ回転を扱う関数であるが、RotatePoint関数がひとつの頂点に対して処理を行うのに対し、RotatePointsは立方体全体に対して処理を行う。ちなみに、RotatePointはRotatePointsから呼び出されている。
MakeCubeは、実際に立方体を生成する関数であり、RotateCubeは、生成された立方体に回転を与える関数である。
----- 立方体の中心点の移動を行う関数
GLdouble *OperationXi(int direct,
GLdouble *center, GLdouble *corner, GLdouble r);
-----
OperationXiは、立方体の中心点の移動を行う関数である。あくまでも中心点に対してのみであり、立方体には関わらない。
----- ディスプレイへの描画関数 void display(void); -----
display関数はディスプレイへの描画を受け持つ。glutDisplayFunc関数にて登録されている。
----- アクションを待っている時に処理する関数 void sleepf(float intv) void idle(void); -----
idle関数にはシステムがアクションを待っている時に処理するコードを記述する。glutIdleFunc関数にて登録されている。sleepf関数は、処理のウェイトを行っている。
----- リサイズ処理 void resize(int w, int h); -----
resize関数にはウィンドウがリサイズされた時に呼びだされる処理を記述している。glutReshapeFunc関数にて登録されている。
----- エントリーポイント&初期化関数 void init(void); int main(int argc, char *argv[]); -----
main関数とinit関数ではシステムの初期化を行っている。init関数はmain関数から呼び出される。
アルゴリズムの基本的な部分はほぼ解説したが、もし詳細な部分を知りたい場合は、サンプルプログラムのソースコードを参照してもらいたい。理論として理解しているのと、実際に実装するのとではやはり違いがあるので、ソースコードも正確で分かりやすく書くように心がけた。また、不必要な部分はなるべく省いているため、約500行程度の収まっており、読みやすさを保つために、コメントもなるべく多くつけている。ぜひとも読んでもらいたい。