削除された内容 追加された内容
M 英語版へのリンクを追加しました。
変換行列の説明を追加しました。
7 行
ここまでで2Dのゲームプログラミングを見てきました.ここまででもある程度高度な数学が現れています.例えば,[[w:ベクトル]]や[[w:三角比]]はその例でどちらも[[高等学校数学]]で扱われる題材で、割合高度な数学といえます.残念ながら3Dプログラミングでは更に高度な数学が必要となります.
-->
3Dプログラミングでは4x4までの[[w:行列]]の演算が必要になります.2x2までの行列演算については[[高等学校数学C]]を参照してください.また、任意の階数の行列演算については[[線型代数学]]を参照してください.具体的には、逆行列を求める演算([[w:クラメルの公式]]、[[w:ガウス消去法]]など)までを学習しておくとよいでしょう。<!-- ほとんど全部だが ... 。 -->
 
また、ゲームによっては初等的な力学の法則を扱う場合があります.残念ながら3次元空間での一般的な力学運動は[[高等学校物理]]の範囲を超えます.このような内容を習得するには,[[古典力学]]を読む必要があります.
63 行
実際に何らかの機器を用意した時,その機器に実際に計算をさせるための手法が必要になります.この手法は環境によって少し異なっています.各社が発売している[[w:グラフィックボード]]の[[w:デバイスドライバ]]には,それらの機器を利用者のプログラムから利用するための一連の関数が用意されています.関数群には通常2つがあり、これらはそれぞれ[[w:DirectX]]、[[w:OpenGL]]と呼ばれます.[[w:Windows]]からは両方の関数群が使えることが普通です.一方,他の環境([[w:Mac OS X]]、[[w:Linux]]など)からはOpenGLしか使えません.これらの関数群は互いに互換性が無いので,実際に使用するときは注意が必要です.ここではOpenGLについて扱います.<!-- もちろんDirectXも扱った方がよいのだが ... 。 -->
 
ここで,グラフィックボードなどの機器が用意できないなどの理由で全ての計算をCPUに行わせる場合のことを述べます.この場合は基本的にグラフィックボードが提供する機能を1つ1つプログラムしていくことになります.残念な事に,この場合のプログラムは更に高度([[w:3D]]レンダリング、[[w:レイトレーシング]]などを参照)になり,自力で全てを実装することは困難です.また、必要となるプログラムの量も多いため,いずれにしろそれらを書くことは時間がかかる作業となります.実際には,このような場合には[[w:en:Mesa 3D]]などのライブラリを使うのが1つの手段です.Mesaは、OpenGLが提供する機能を実際にプログラムしたもので、1つ1つプログラムする手間が省けます.Mesaは[[w:オープンソース]]ライセンスで配布されているので自由に手に入れて使うとよいでしょう.<!-- Mesaの作者はすごい。 -->
 
ここまでで3Dの計算を行う方法について述べました.ここでは実際にOpenGLのプログラムを扱います.OpenGLのチュートリアルはWeb上で公開されているので,紹介します.(OpenGL Redbook http://fly.srk.fer.hr/~unreal/theredbook/ )また、OpenGLの詳細な規定は、http://www.opengl.org/documentation/specs/ で入手できます。
118 行
ここまでで、上のプログラム中で座標(0,0,0),(0,1,0),(0,0,1)が指定され,これらが直線でつながれることがわかりました。これは3次元中で3角形を描くことがわかります。次の問題は,この三角形がどのようにディスプレイに投影されるかです。
 
OpenGLは3次元中に指定された座標の2次元中での位置を定めるために2つの行列を利用します。ここで、OpenGLでは行列を"扱う"場所は1つしか用意しなくてもいいことが定められています。もちろん、値を保存しておく場所は複数あるのですが,これらの値を変更するのに使う領域は1つです。そのため、これらの行列を使い分けるには,これらを所定の場所に呼び出す必要があります。ここで行列を指定するには,glMatrixMode()関数を使います。この関数は1つの引数を取り、その値で行列を指定します。2次元中での位置を指定するのに使う行列はそれぞれGL_MODELVIEW, GL_PEROJECTIONで指定されます。これらの行列の初期値は4x4の[[w:単位行列]]です。また、実際に使われるのはこれらの積であるので,値を変更するのは片方の行列だけでもあらゆる変換を指定することは可能です。<!-- 意味があるかはわからないが ... 。 -->
 
ここで2つの行列のそれぞれについてその性質を述べます。GL_MODELVIEW行列はそれぞれの頂点の座標を変更するのに利用します。実際には大抵の場合、この行列は
glRotatef(), glScalef(), glTranslatef()
の3つの関数から利用されます。 これらの関数は順に,座標を"回転", "拡大縮小", "平行移動"します。これらの関数はそれぞれ対応する変換を行う行列を生成し、選択されている行列を変換します。ここではそれらの行列の詳細について解説します。対応する行列についてはそれぞれのmanを参照してください。(OpenGLのspecification http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/ )
 
*例
translateの生成する行列は、
:<math>
\begin{pmatrix}
1&0&0&x\\
0&1&0&y\\
0&0&1&z\\
0&0&0&1
\end{pmatrix}
</math>
で与えられます。この行列は、元の頂点の3次元座標が<math>\vec v</math>で表されるときの同次座標<math>(v_1, v_2, v_3, 1)</math>を、<math>(v_1+x, v_2+y, v_3+z, 1)</math>に変換しますが、これはまさに平行移動によって引き起こされる座標変換に対応しています。
 
scaleの生成する行列は、
:<math>
\begin{pmatrix}
x&0&0&0\\
0&y&0&0\\
0&0&z&0\\
0&0&0&1
\end{pmatrix}
</math>
です。この変換は3次元座標<math>(v_x,v_y,v_z)</math>を、<math>(xv_x,yv_y,zv_z)</math>に変換します。これはx,y,zの値によってそれぞれの座標を拡大縮小する変換に対応します。
 
最後に、rotateで得られる行列を紹介します。
:<math>
\begin{pmatrix}
n_x^2(1-c) + c&n_xn_y(1-c)-n_zs&n_xn_z(1-c)+n_ys &0\\
n_xn_y(1-c) + n_zs&n_y^2(1-c)+c&n_yn_z(1-c)-n_xs &0\\
n_xn_z(1-c) - n_ys&n_yn_z(1-c)+n_xs&n_z^2(1-c)+c&0\\
0&0&0&1
\end{pmatrix}
</math>
この変換の導出は少し厄介です。まず、原点から始まり、回転の方向を向いている単位ベクトルを<math>\vec n</math>とし、ある頂点の座標を表すベクトルを<math>\vec a</math>とします。このとき、この回転で変化するのは、<math>\vec a</math>のうち、<math>\vec n</math>に平行でない成分のみです。
 
<math>\vec n</math>に平行な成分は、
:<math>
\vec a_{\parallel} = (\vec a \cdot \vec n) \vec n
</math>
で表されるので、実際に回転によって変化する成分は、<math>\vec a</math>から上の分を取り除いたベクトルです。このベクトルを<math>\vec a_{\perp}</math>と呼びます。このベクトルは回転によって変化しますが、これを表すには、<math>\vec a_{\perp}</math>と<math>\vec n</math>の両方に直交するベクトルが必要です。このベクトルを、
:<math>\vec b = \vec n \times \vec a_{\perp}</math>
と取ります。このとき、回転する角度を<math>\theta</math>とすると、後のベクトルは、<math>\vec r = \cos \theta \vec a _{\perp}+\sin \theta \vec b</math>と書けます。
 
結局得られるベクトルは、
:<math>\vec a_{\parallel}+\vec r</math>
となります。ここからは、この結果が上の式と一致することを示すため、この式を整理していきます。まず、<math>\vec a_{\parallel}</math>について、3x3行列
:<math>
N = \vec n {}^t\vec n =
\begin{pmatrix}
n_x^2&n_xn_y&n_zn_x\\
n_xn_y&n_y^2&n_yn_z\\
n_zn_x&n_yn_z&n_z^2
\end{pmatrix}
</math>
を定義すると、<math>\vec a_{\parallel} = N\vec a</math>が成り立ちます。また、<math>\vec b</math>について、3x3行列
:<math>
R =
\begin{pmatrix}
0&-n_z&n_y\\
n_z&0&-n_x\\
-n_y&n_x&0
\end{pmatrix}
</math>
を定義すると、<math>\vec b = R\vec a</math>が成り立ちます。このことから結果の式について、
:<math>\vec a_{\parallel}+\vec r</math>
:<math>= N\vec a +\cos \theta (\vec a - N\vec a) + \sin \theta R\vec a</math>
が得られますが、これを整理すると求めていた行列x<math>\vec a</math>が現れます。
 
 
次に、GL_PROJECTION行列について述べます。OpenGLでは、頂点の座標についてこれらの行列をかけた後(-1, -1, -1), (1,1,1)で指定される立方体内にある図形を描きます。このため、例えば(0,0,10)にある図形は一般には描かれません。しかし、図形を3次元中に描くときの事情で、これらの位置を自由に指定できないと不便です。GL_PROJECTION行列は"描画される範囲を広げる"目的で利用されます。また、この行列は、"遠くにあるものほど小さく見える"という状況を扱うためにも利用されます。