2015年12月4日 | ブログ記事

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ではある4x4の行列を指定してやると、以降の操作で指定する頂点座標などに勝手にその行列を全部掛けてくれます。

まず行列の種類を指定(ここでは気にしない)し、その後行列を配列にしてOpenGLに渡します。今回のプログラムで指定している行列は、 (300000.500.500100001)\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次元ベクトルを掛けるという方法で行われます。例えば、この例では最初の頂点が (0.50.50)\left( \begin{array}{ccc} -0.5 \\-0.5 \\0 \end{array} \right) なので、まずこれに4番目の要素として1を加え、 (0.50.501)\left( \begin{array}{cccc} -0.5 \\-0.5 \\0\\1 \end{array} \right) とします。これと先ほどの行列の積をとり、 (300000.500.500100001)×(0.50.501)=(1.50.2501)\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) を実際に使う頂点にします。他の頂点も同様に、 (300000.500.500100001)×(0.50.501)=(1.50.2501)\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)   (300000.500.500100001)×(00.501)=(00.7501)\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) となるので、結局 (1.50.2501),(1.50.2501),(00.7501)\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();
}

キャプチャ

ちょっとやることが増えましたが、1つずつ見ていきましょう。まず行列を3つ定義します。行列は、 (0.2000010000100001),(1001010000100001),(3000010000100001),\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つ目は (0.2000010000100001)\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つ目は (0.2000010000100001)×(1001010000100001)=(0.2000.2010000100001)\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つ目は、 (0.2000.2010000100001)×(3000010000100001)=(0.6000.2010000100001)\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();
}
この記事を書いた人
sobaya007

東工大工学部の異端児

この記事をシェア

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

関連する記事

2015年12月2日
D言語でOpenGL†入門†
sobaya007
2015年12月2日
VisualDでOpenGL
sobaya007
2017年11月17日
そばやのワク☆ワク流体シミュレーション~MPS編~
sobaya007

活動の紹介

カテゴリ

タグ