===3Dに必要な数学===
ここまでで2Dのゲームプログラミングを見てきました .。ここまででもある程度高度な数学が現れています .。例えば,[[w:ベクトル]]や[[w:三角比]]はその例でどちらも[[高等学校数学]]で扱われる題材で、割合高度な数学といえます .。残念ながら3Dプログラミングでは更に高度な数学が必要となります .。▼
<!--
▲ここまでで2Dのゲームプログラミングを見てきました.ここまででもある程度高度な数学が現れています.例えば,[[w:ベクトル]]や[[w:三角比]]はその例でどちらも[[高等学校数学]]で扱われる題材で、割合高度な数学といえます.残念ながら3Dプログラミングでは更に高度な数学が必要となります.
-->
3Dプログラミングでは4x4までの[[w:行列]]の演算が必要になります.2x2までの行列演算については[[高等学校数学C]]を参照してください.また、任意の階数の行列演算については[[線型代数学]]を参照してください.具体的には、逆行列を求める演算([[w:クラメールの公式]]、[[w:ガウス消去法]]など)までを学習しておくとよいでしょう。<!-- ほとんど全部だが ... 。 --> ▼
▲3Dプログラミングでは4x4までの[[w:行列]]の演算が必要になります .2x2。2x2までの行列演算については[[高等学校数学C]]を参照してください .。また、任意の階数の行列演算については[[線型代数学]]を参照してください .。具体的には、逆行列を求める演算([[w:クラメールの公式]]、[[w:ガウス の消去法]]など)までを学習しておくとよいでしょう。 <!-- ほとんど全部だが ... 。 -->
また、ゲームによっては初等的な力学の法則を扱う場合があります.残念ながら3次元空間での一般的な力学運動は[[高等学校物理]]の範囲を超えます.このような内容を習得するには,[[古典力学]]を読む必要があります. ▼
▲また、ゲームによっては初等的な力学の法則を扱う場合があります .。残念ながら3次元空間での一般的な力学運動は[[高等学校物理]]の範囲を超えます .。このような内容を習得するには,[[古典力学]]を読む必要があります .。
いずれにしても、3Dプログラミングに必要な数学は[[w:理系]]の大学1, 2年程度で習う内容です.<!-- 大卒でも知らない人も多い.もっとも知る必要もないのだが ... 。偏差値よりも個人の専門性がその人の能力を知る上で重要だということか。 -->
いずれにしても、3Dプログラミングに必要な数学は[[w:理系]]の大学1, 2年程度で習う内容です。
===3Dプログラミング===
[[w:3D]] (3 Dimension) プログラミングは3つの数値で表される座標を持つ物体を扱うプログラミング技法です.。普通コンピュータで画面表示として用いられている[[w:ディスプレイ]]は、厚みのある事柄を表示することはできません.。これは、ディスプレイの表示方法が2つの座標(縦と横)で表される表示方法であることによります.。一方,計算機上のデータとしては,3つの数値を組として扱うことは,2つの数値を組として扱うことと比べて格別に難しいことではありません。そのため、ある物体が存在する位置を3次元の座標で表し,その座標を操作することで物体の3次元中での移動などを扱うことができます。この時,最終的に操作の結果を表示するには2次元のディスプレイを扱う必要があるため,3次元の座標を2次元の座標に変換する方法が必要になります.。
3次元の座標から2次元の座標を引き出す方法として,射影行列([[線型代数学|線形代数]]などを参照)を利用する方法があげられます.。射影行列<math>P_i</math>は,
:<math>\sum _i P_i = 1</math>
\sum _i :<math>P_i ^2 = 1P_i</math>
</math>
:<math>
P_i ^2 = P_i
</math>
(和を取らない)を満たす行列のことです。ここでは、z軸方向を厚みの方向として取り,射影行列として,
:<math>P_1 = \begin{pmatrix}
P_1 =
\begin{pmatrix}
1&0&0\\
0&1&0\\
\end{pmatrix}
</math>
:<math> P_2 = \begin{pmatrix}
P_2 =
\begin{pmatrix}
0&0&0\\
0&0&0\\
\end{pmatrix}
</math>
を取ります.。この2つの行列は
<math>P_1 + P_2 = 1, P_1 ^2 = P_1, P_2 ^2 = P_2 </math>▼
<math>
を満たします .。ここで、ある点<math>\vec x</math>の3次元座標を ▼
▲P_1 + P_2 = 1, P_1 ^2 = P_1, P_2 ^2 = P_2
:<math>\vec x = (x, y, z)</math>
▲を満たします.ここで、ある点<math>\vec x</math>の3次元座標を
:<math>
</math>
とした時,その点の2次元座標を
▲:<math>P_1 \vec x = (x, y, z0) </math>
:<math>
のx, y成分で表します .。これはあらゆる3次元座標にただ1つの2次元座標を与える射影で,3次元座標から2次元座標を取り出す1つの方法ということになります .。▼
P_1 \vec x = (x, y, 0)
</math>
▲のx, y成分で表します.これはあらゆる3次元座標にただ1つの2次元座標を与える射影で,3次元座標から2次元座標を取り出す1つの方法ということになります.
ここまでで3次元座標から2次元座標を取り出す方法を述べました.。ここからは、実際にこの方法をプログラムする方法を述べます.。上の方法を扱うには、行列の計算と、ベクトルを扱う方法が必要です.。実際にはこれらの方法はいろいろなライブラリで扱われており(例えば,[[w:en:GSL]](GNU Scientific Library)など),実際のプログラミングではそれらを用いるとよいでしょう。もちろん自力で実装することもできます.。
上で述べた通り,行列やベクトルを扱う演算はプログラムで扱うこともできますが,実際にはそれはあまり行われません.。それはそのような計算は[[w:CPU]]にとって手間がかかることが多く,全てをCPUを使って計算しようとすると多くの時間がかかるからです。<!-- 実際には筆者の環境でmesaを使ってglxgearsを実行してみたところ、座標の計算というより扱うウィンドウの書き換えで時間を取っている。そのため、"行列の計算で"というのは語弊がある。 -->このような計算を扱うためには,[[w:グラフィックボード]]、[[w:ビデオカード]]と呼ばれる機器を使うことが推奨されます.。これらは、3次元の計算をするために特化した演算装置(CPUに対して,[[w:GPU]]と呼ばれることがある)を持っており,CPUを使って計算を行うよりも速く演算を行うことができます.。ただし、そのような機器がない環境でも全ての計算をCPUを使って行うことで3Dの描画を行うことは可能です.。
ここまでで、3Dの計算を扱うために専用の機器を使えることを述べました.。ここからは実際にそのような機器を使う方法について述べます.。また、そのような機器がなくとも,同じプログラムを使ってCPUに計算をさせる方法があることについても述べます.。
実際に何らかの機器を用意した時,その機器に実際に計算をさせるための手法が必要になります.。この手法は環境によって少し異なっています.。各社が発売している[[w:グラフィックボード]]の[[w:デバイスドライバ]]には,それらの機器を利用者のプログラムから利用するための一連の関数が用意されています.。関数群には通常2つがあり、これらはそれぞれ[[w:DirectX]]、[[w:OpenGL]]と呼ばれます.。[[Windows入門|Windows]]からは両方の関数群が使えることが普通です.。一方,他の環境([[w:Mac OS X]]、[[w:Linux]]など)からはOpenGLしか使えません.。これらの関数群は互いに互換性が無いので,実際に使用するときは注意が必要です.。ここではOpenGLについて扱います.。<!-- もちろんDirectXも扱った方がよいのだが ... 。 -->
ここで,グラフィックボードなどの機器が用意できないなどの理由で全ての計算をCPUに行わせる場合のことを述べます.。この場合は基本的にグラフィックボードが提供する機能を1つ1つプログラムしていくことになります.。実際には,このような場合には[[w:en:Mesa 3D]]などのライブラリを使うのが1つの手段です.Mesa。Mesaは、OpenGLが提供する機能を実際にプログラムしたもので、1つ1つプログラムする手間が省けます.Mesa。Mesaは[[w:オープンソース]]ライセンスで配布されているので自由に手に入れて使うとよいでしょう.。<!-- Mesaの作者はすごい。 -->
ここまでで3Dの計算を行う方法について述べました.。ここでは実際にOpenGLのプログラムを扱います.OpenGL。OpenGLのチュートリアルはWeb上で公開されているので,紹介します.。(OpenGL Redbook http://fly.srk.fer.hr/~unreal/theredbook/ ) また、OpenGLの詳細な規定は、http://www.opengl.org/documentation/specs/ で入手できます。
OpenGLを利用して計算を行うのですが、OpenGLが行うのはあくまで計算であり,画面への描画では無いことに注意が必要です.。画面への描画はOpenGLでの計算結果に従って描画を扱うプログラムが行います.Linux。Linuxでは描画は[[w:X Window System]]が行い,Mac OS Xでは、[[w:quartz]]が行います.。そのため、結果の描画を行うためには,描画を行うプログラムに,OpenGLを利用することを知らせる必要があります.。
残念な事にこの作業を行う方法はウィンドウシステムによって異なっています.X。Xの場合にはglX関数を使い,Xに指示を出しますが、この方法は他のプラットフォームでは使えません.。幸いにもこのような作業を様々なプラットフォームで使う方法として,[[w:GLUT]]、[[w:SDL]]などを利用することができます.。ここでは、SDLを利用する方法について述べます.。
GLUTは本来GLの利用を簡単にするために作られました.。一方,SDLは本来ゲームプログラミングを行うことを目的として作られたライブラリです.。しかし、いずれにしろ3Dプログラミングを行うためにはOpenGLの利用は必須なので,SDLもGLUTに近い機能を持っています.。どちらもWindows, Mac OS X, Linux上でOpenGLの動作を始めることができます.。<!-- Windowsについては筆者は未確認。 -->
SDLでOpenGLを利用するときには
<syntaxhighlight lang=c line>
2:#include <GL/glSDL.h>
▲ 1:#include < SDLGL/gl.h>
3:int main(){
4: if (NULL == SDL_SetVideoMode(200, 200, 0, SDL_OPENGL)) ▼
{
if (SDL_SetVideoMode(200, 200, 0, SDL_OPENGL) == NULL)
7: SDL_GL_SwapBuffers(); ▼
▲ 7: SDL_GL_SwapBuffers();
9:}
とします。<!-- SDL_Init? -->ここではこのプログラムについて順に説明していきます。まず、OpenGLを使わなくとも、SDLを利用する場合でも、1, 3, 4, 5, 8, 9行は必要となります。ただし,4行目は、 ▼
}
</syntaxhighlight>
▲ 5:if (surf = SDL_SetVideoMode(200, 200, 0, 0))
▲とします。<!-- SDL_Init? -->ここではこのプログラムについて順に説明していきます。まず、OpenGLを使わなくとも、SDLを利用する場合でも、1, 3, 4, 5, 86, 9 , 10行は必要となります。ただし,4行目は、
<syntaxhighlight lang=c start="5" line>
▲ 4: if ( NULLsurf == SDL_SetVideoMode(200, 200, 0, SDL_OPENGL0))
</syntaxhighlight>
などと書き換える必要があります。
#* 1行目は,SDLを利用する際に必要となるヘッダファイルです。これは常に必要になります。
#* 2行目はOpenGLの関数を利用する時に必要となるヘッダです。この部分は例え,GLUTを利用するとしても,変わりはありません。それは、SDLであるにしろ,GLUTであるにしろどちらもウィンドウシステムからOpenGLの領域を受け取ることに変わりはないからです。そのため、利用するヘッダも、2つのライブラリで共通のものを利用します。
#* 3, 84, 9, 10行目はほとんど全てのCプログラムで必要になります。
#4* 5行目でOpenGLの領域をウィンドウシステムから受け取っています。SDL_SetVideoModeは本来のSDLでは書き換えられたプログラムの5行目のように描画を行うSDL_Surfaceという構造体を返します。しかし、4つめの引数にSDL_OPENGLの[[w:フラグ]]を受け取った時には,OpenGLの領域を初期化するように動作が変化します。仮にこの動作が失敗した場合には,この関数はNULLを返すので,その場合にはここでプログラムを終了するのが普通です。
#6* 7行目以降でOpenGLの関数を書きます。ここについては後で述べます。
#7* 8行目ではSDL_GL_SwapBuffersという命令関数を使っています。この命令関数はOpenGLの命令関数で描画した図形を、実際にウィンドウに描画する命令関数です。この方法は[[w:ダブルバッファ]]を簡単に実現してくれます。GLで描画された内容が実際に表示されるのは,この関数以降です。また、実際に描画を行ってもこのままではすぐにプログラムが終了してしまうので,描画結果を見るためには,whileループかsleepなどの関数を利用して,プログラムの実行を続ける必要があります。実際にはSDLはSDL_Delayというプログラムの実行を一時的に遅らせる関数を持っているので,この関数を使うのもよいでしょう。
ここからは、実際にOpenGLの 命令関数を書いていきます。ここで書かれるプログラムは上のプログラムの 67行目以降に書かれるものです。 ▼
<syntaxhighlight lang=c>
1: glBegin(GL_LINE_LOOP); ▼
</syntaxhighlight>
▲ここからは、実際にOpenGLの命令を書いていきます。ここで書かれるプログラムは上のプログラムの6行目以降に書かれるものです。
▲ 1:glBegin(GL_LINE_LOOP);
このプログラム(tri.cとする)をコンパイルするには,いくつかのコンパイラオプションが必要です。コンパイラが[[w:GCC]]なら、
$gcc -Wall tri.c -L libGLのある場所 -I GL/gl.hのある場所 -lGL $(sdl-config --cflags --libs)
|