traP Member's Blog

D言語でOpenGL†入門†2

sobaya007
このエントリーをはてなブックマークに追加

続きです。前回で色をつけた三角形を描きました。ここから3次元っぽい話をしていきます。

座標変換さいしょ

import derelict.opengl3.gl;
import derelict.glfw3.glfw3;

void main() {
	//======Derelict側の初期化=====
	DerelictGL.load();
	DerelictGLFW3.load();

	//=====GLFWの初期化=====
	glfwInit();

	//=====ウインドウの初期化=====
	const int window_width  = 800;
	const int window_height = 600;
	//末尾のnullはそれぞれフルスクリーン、ウインドウの共有をするときに使う(らしい?)
	GLFWwindow *window = glfwCreateWindow(window_width, window_height, "Hello, D World!", null, null);

	//OpenGLでの描画対象をwindowに設定
	glfwMakeContextCurrent(window);

	//変換行列を作っておく
	float[] matrix = [
	3.0, 0.0, 0.0, 0.0,
	0.0, 0.5, 0.0, 0.0,
	0.0, 0.0, 1.0, 0.0,
	0.0, 0.5, 0.0, 1.0
	];

	//変換行列の種類を指定
	glMatrixMode(GL_MODELVIEW);
	//変換行列を現在の変換行列に掛ける
	glMultMatrixf(matrix.ptr);

	//=====メインループ=====
	while (!glfwWindowShouldClose(window)) {

		//三角形を描画
		glClear(GL_COLOR_BUFFER_BIT);
		glBegin(GL_TRIANGLES);
		glColor3b(byte.max, 0, 0);
		glVertex3d(-0.5, -0.5, 0.0);
		glColor3f(0, 1.0f, 0);
		glVertex3d( 0.5, -0.5, 0.0);
		glColor3i(0, 0, int.max);
		glVertex3d( 0.0,  0.5, 0.0);
		glEnd();

		//画面を更新
		glfwSwapBuffers(window);

		glfwPollEvents();
	}

	//=====おわり=====
	glfwTerminate();
}

キャプチャ

はいこんなかんじです。OpenGLではある4×4の行列を指定してやると、以降の操作で指定する頂点座標などに勝手にその行列を全部掛けてくれます。

  • glMatrixMode(mode) : 行列のモードを変更する。modeの候補は以下。ここではあまり気にしなくて良い。
    • GL_MODELVIEW : モデルビュー行列モードにする。座標に対して掛ける行列。
    • GL_PROJECTION : 射影変換行列モードにする。座標に対して掛ける行列。
    • GL_TEXTURE : テクスチャ行列モードにする。テクスチャ座標に対して掛ける行列。
    • GL_COLOR : 色行列モードにする。色に対して掛ける行列。
  • glMultMatrix(mat) : 現在のモードの行列に新しい行列を右から掛ける。glMultMatrixdとglMultMatrixfがあり、それぞれ引数にdouble,floatの配列をとる。

まず行列の種類を指定(ここでは気にしない)し、その後行列を配列にしてOpenGLに渡します。今回のプログラムで指定している行列は、

\left( \begin{array}{cccc} 3 & 0 & 0 & 0\\ 0 & 0.5 & 0 & 0.5\\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{array} \right)

です。ソースコードと比べてみるとわかりますが、行列をOpenGLに渡す際は、左上から縦に見ていって順に配列に入れていきます。注意してください。

こうして行列を指定してやると、その後で指定している3つの頂点が自動で変換されます。変換は、指定された行列に、3次元頂点の4つ目の要素に1を加えた4次元ベクトルを掛けるという方法で行われます。例えば、この例では最初の頂点が

\left( \begin{array}{ccc} -0.5 \\-0.5 \\0 \end{array} \right)

なので、まずこれに4番目の要素として1を加え、

\left( \begin{array}{cccc} -0.5 \\-0.5 \\0\\1 \end{array} \right)
とします。これと先ほどの行列の積をとり、
\left( \begin{array}{cccc} 3 & 0 & 0 & 0\\ 0 & 0.5 & 0 & 0.5\\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{array} \right) \times \left( \begin{array}{cccc} -0.5 \\-0.5 \\0\\1 \end{array} \right) = \left( \begin{array}{cccc} -1.5 \\0.25 \\0\\1 \end{array}\right)
を実際に使う頂点にします。他の頂点も同様に、
\left( \begin{array}{cccc} 3 & 0 & 0 & 0\\ 0 & 0.5 & 0 & 0.5\\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{array} \right) \times \left( \begin{array}{cccc} 0.5 \\-0.5 \\0\\1 \end{array} \right) = \left( \begin{array}{cccc} 1.5 \\0.25 \\0\\1 \end{array}\right)
 
\left( \begin{array}{cccc} 3 & 0 & 0 & 0\\ 0 & 0.5 & 0 & 0.5\\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{array} \right) \times \left( \begin{array}{cccc} 0 \\ 0.5 \\0\\1 \end{array} \right) = \left( \begin{array}{cccc} 0 \\0.75 \\0\\1 \end{array}\right)
となるので、結局
\left( \begin{array}{cccc} -1.5 \\0.25 \\0\\1 \end{array}\right), \left( \begin{array}{cccc} 1.5 \\0.25 \\0\\1 \end{array}\right), \left( \begin{array}{cccc} 0 \\0.75 \\0\\1 \end{array}\right)

を頂点とする三角形を描画しているというわけです。これを利用して色々なものを描いてみましょう。

import derelict.opengl3.gl;
import derelict.glfw3.glfw3;

void main() {
	//======Derelict側の初期化=====
	DerelictGL.load();
	DerelictGLFW3.load();

	//=====GLFWの初期化=====
	glfwInit();

	//=====ウインドウの初期化=====
	const int window_width  = 800;
	const int window_height = 600;
	//末尾のnullはそれぞれフルスクリーン、ウインドウの共有をするときに使う(らしい?)
	GLFWwindow *window = glfwCreateWindow(window_width, window_height, "Hello, D World!", null, null);

	//OpenGLでの描画対象をwindowに設定
	glfwMakeContextCurrent(window);

	//変換行列を作っておく
	float[] m0 = [
	0.2, 0.0, 0.0, 0.0,
	0.0, 1.0, 0.0, 0.0,
	0.0, 0.0, 1.0, 0.0,
	0.0, 0.0, 0.0, 1.0
	];
	float[] m1 = [
	1.0, 0.0, 0.0, 0.0,
	0.0, 1.0, 0.0, 0.0,
	0.0, 0.0, 1.0, 0.0,
	1.0, 0.0, 0.0, 1.0
	];
	float[] m2 = [
	3.0, 0.0, 0.0, 0.0,
	0.0,-1.0, 0.0, 0.0,
	0.0, 0.0, 1.0, 0.0,
	0.0, 0.0, 0.0, 1.0
	];

	//変換行列の種類を指定
	glMatrixMode(GL_MODELVIEW);
	
	void DrawTriangle() {
		glBegin(GL_TRIANGLES);
		glColor3b(byte.max, 0, 0);
		glVertex3d(-0.5, -0.5, 0.0);
		glColor3f(0, 1.0f, 0);
		glVertex3d( 0.5, -0.5, 0.0);
		glColor3i(0, 0, int.max);
		glVertex3d( 0.0,  0.5, 0.0);
		glEnd();
	}

	//=====メインループ=====
	while (!glfwWindowShouldClose(window)) {

		//三角形を描画
		glClear(GL_COLOR_BUFFER_BIT);
		
		glViewport(0, 300, 400, 300);
		glLoadIdentity();
		DrawTriangle();
		
		glViewport(400, 300, 400, 300);
		glMultMatrixf(m0.ptr);
		DrawTriangle();
		
		glViewport(400, 0, 400, 300);
		glMultMatrixf(m1.ptr);
		DrawTriangle();
		
		glViewport(0, 0, 400, 300);
		glMultMatrixf(m2.ptr);
		DrawTriangle();

		//画面を更新
		glfwSwapBuffers(window);

		glfwPollEvents();
	}

	//=====おわり=====
	glfwTerminate();
}

キャプチャ

  • glLoadIdentity() : 現在の行列を単位行列に直す
  • glViewport(x,y,w,h) : 描画範囲をピクセル単位で指定する。(x,y)は描画する矩形領域の左下の座標で、w,hはその矩形の幅と高さ。原点はウインドウの左下。

ちょっとやることが増えましたが、1つずつ見ていきましょう。まず行列を3つ定義します。行列は、

\left( \begin{array}{cccc} 0.2 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right), \left( \begin{array}{cccc} 1& 0 & 0 & 1\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right), \left( \begin{array}{cccc} 3 & 0 & 0 & 0\\ 0 & -1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right),

です。次に、三角形の描画は同じことを複数回することになるので関数化します。

最後に三角形を4回描画していきます。

まずglViewportで描画領域を設定します。描画領域を指定すると、領域の左下の座標が(-1, -1),右上の座標が(1, 1)になります。この例では、画面を4分割して

キャプチャ

の順に描画しています。

最初の三角形はglLoadIdentityの直後なので、行列は単位行列となっており、書いてある通りの座標で描画されます。

2つ目は

\left( \begin{array}{cccc} 0.2 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right)

の行列を掛けているため、x座標だけ0.2倍された座標を使います。だから②はもとの三角形をx方向に0.2倍したものです。

3つ目は

\left( \begin{array}{cccc} 0.2& 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right) \times \left( \begin{array}{cccc} 1 & 0 & 0 & 1\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right) = \left( \begin{array}{cccc} 0.2 & 0 & 0 & 0.2\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right)

を使うため、x座標を0.2倍して+0.2した座標が使われます。だから③は②の三角形をx方向に0.2だけ動かしたものになります。

4つ目は、

\left( \begin{array}{cccc} 0.2& 0 & 0 & 0.2\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right) \times \left( \begin{array}{cccc} 3 & 0 & 0 & 0\\ 0 & -1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right) = \left( \begin{array}{cccc} 0.6 & 0 & 0 & 0.2\\ 0 & -1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 \end{array}\right)

を使うため、もとの三角形をx方向に0.6倍,y方向に-1倍し、x方向に0.6だけずらしたものになります。

 

OpenGLにはglTransformやglRotateやglScaleといった関数があり、それぞれ以降に指定した座標を平行移動、回転、拡大縮小する関数ですが、これらは結局そういう動作をする行列をglMultMatrixしただけのものです。実用性としてはglTransformなどの方が良いですが、結局はglMultMatrixなんだよ、ということは覚えておいてください。最後に、上の例をglMultMatrixを使わないで書き換えたものを載せておきます。

import derelict.opengl3.gl;
import derelict.glfw3.glfw3;

void main() {
	//======Derelict側の初期化=====
	DerelictGL.load();
	DerelictGLFW3.load();

	//=====GLFWの初期化=====
	glfwInit();

	//=====ウインドウの初期化=====
	const int window_width  = 800;
	const int window_height = 600;
	//末尾のnullはそれぞれフルスクリーン、ウインドウの共有をするときに使う(らしい?)
	GLFWwindow *window = glfwCreateWindow(window_width, window_height, "Hello, D World!", null, null);

	//OpenGLでの描画対象をwindowに設定
	glfwMakeContextCurrent(window);

	//変換行列の種類を指定
	glMatrixMode(GL_MODELVIEW);

	void DrawTriangle() {
		glBegin(GL_TRIANGLES);
		glColor3b(byte.max, 0, 0);
		glVertex3d(-0.5, -0.5, 0.0);
		glColor3f(0, 1.0f, 0);
		glVertex3d( 0.5, -0.5, 0.0);
		glColor3i(0, 0, int.max);
		glVertex3d( 0.0,  0.5, 0.0);
		glEnd();
	}

	//=====メインループ=====
	while (!glfwWindowShouldClose(window)) {

		//三角形を描画
		glClear(GL_COLOR_BUFFER_BIT);

		glViewport(0, 300, 400, 300);
		glLoadIdentity();
		DrawTriangle();

		glViewport(400, 300, 400, 300);
		glScalef(0.2, 1, 1);
		DrawTriangle();

		glViewport(400, 0, 400, 300);
		glTranslatef(1, 0, 0);
		DrawTriangle();

		glViewport(0, 0, 400, 300);
		glScalef(3, -1, 1);
		DrawTriangle();

		//画面を更新
		glfwSwapBuffers(window);

		glfwPollEvents();
	}

	//=====おわり=====
	glfwTerminate();
}

 

このエントリーをはてなブックマークに追加

コメントを残す

メールアドレスが公開されることはありません。