🧊

OpenGLで立方体を作る

2022/12/21に公開

OpenGLシリーズの第3弾、今回は立方体を描画する方法です。
よろしければ以前の記事も併せて読んでいただけると分かりやすいかと思います。

  1. 開発環境構築
  2. 三角形描画
  3. 立方体描画 ←ここ

前回と同様に、詳細なソースコードの説明はせず、立方体を描くことに注目して解説します。詳細な内容については、都度紹介しているリンク先を参照していただけると幸いです。

OpenGL Mathematic (GLM)

https://glm.g-truc.net/0.9.9/
今回は新たにglmというライブラリを追加します。
名前の通り、このライブラリは数学的な計算を行うための機能が詰まったライブラリです。
3D空間を2次元のディスプレイ上に描画するには、さまざまな行列計算が必要になります。煩わしい計算はこのライブラリに任せてしまいましょう。
また、このライブラリはヘッダーオンリーなので手軽に使えます。
↓こちらからglm-x.x.x.x.zipをダウンロード
https://github.com/g-truc/glm/releases
展開したフォルダの中にあるglmフォルダを、プロジェクトのincludeフォルダ内に突っ込むだけです。

今回使うのは次の3つのヘッダーです。
こちらをインクルードしてください。

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

頂点とインデックス

立方体なので頂点は8つになります。

GLfloat vertices[] =
{
   1.0f,  1.0f,  1.0f,
  -1.0f,  1.0f,  1.0f,
  -1.0f,  1.0f, -1.0f,
   1.0f,  1.0f, -1.0f,
   
   1.0f, -1.0f,  1.0f,
  -1.0f, -1.0f,  1.0f,
  -1.0f, -1.0f, -1.0f,
   1.0f, -1.0f, -1.0f,
};

頂点を適当な順番で並べて立方体を作ります。

GLuint indices[] =
{
  // Ceiling
  0, 1, 2,
  2, 3, 0,
  
  // Floor
  4, 5, 6,
  6, 7, 4,
  
  0, 1, 5,
  5, 4, 0,
  
  1, 2, 6,
  6, 5, 1,
  
  2, 3, 7,
  7, 6, 2,
  
  3, 0, 4,
  4, 7, 3
};

行列

3Dグラフィクスにおける行列に関する詳しい説明は、こちらのサイトが非常に分かりやすかったので是非お読みください。(私のプログラムはここを真似ています。)
http://www.opengl-tutorial.org/jp/beginners-tutorials/tutorial-3-matrices/
ここではコードに沿って簡単に説明します。

// ① モデル行列
glm::mat4 Model      = glm::mat4(1.0f);
// ② ビュー行列
glm::mat4 View       = glm::lookAt(
	glm::vec3(4,3,3), // カメラの位置
	glm::vec3(0,0,0), // カメラの視点
	glm::vec3(0,1,0)  // カメラの頭の方向
);
// ③ 射影行列
glm::mat4 Projection = glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, 100.0f);

// ④ 全ての行列の積
glm::mat4 MVP        = Projection * View * Model;

glm::mat4

これは、4x4の行列を表します。擬似的に書くと次のような値が入ります。

[ [1, 0, 0, 0],
  [0, 1, 0, 0],
  [0, 0, 1, 0],
  [0, 0, 0, 1] ]

この4x4行列で、位置・回転・大きさの全てを表すことが可能となり、3Dグラフィクスにおける計算は、この4x4の行列が全てです。
これを理解することによって、自由自在に立体を操作することができます。なぜ表現できるのかは私が解説するより(出来ない)、紹介したサイトの方が分かりやすいのでそちらをご覧ください。

モデル行列

glm::mat4(1.0f)

行列の対角成分を全て1に初期化し、それ以外をゼロとした単位行列を得る。
行列の4列目の上から3つの成分は、それぞれx,y,zを表すため、この行列はオブジェクトが中心に位置することを表します。
http://www.c-jump.com/bcc/common/Talk3/Math/GLM/GLM.html#W01_0020_vector_and_matrix_con

ビュー行列

これはカメラを表す行列です。

glm::lookAt()

第一引数:カメラの位置
第二引数:カメラの視点の位置((0,0,0)なら原点を見ている)
第三引数:カメラが頭の方向
さらに詳しい説明はこちら↓
https://learnopengl.com/Getting-started/Camera

射影行列

これが最も難しいですが、簡単に述べると遠くのものを小さくし、近くのものを大きく描画するための行列です。透視投影などと言ったりします。

glm::perspective

第一引数:視野角(見える範囲のこと、FOVなどと書かれていることが多い)
第二引数:アスペクト比(OpenGLでは、画面端の座標は1.0なので調整する必要がある)
第三引数:どれくらい近くの物まで見えるか(クリッピング平面)
第四引数:どれくらい遠くの物まで見えるか(クリッピング平面)

掛け合わせる

行列では掛け算の順序が大事です。
逆順にしてしまうと全く違う値になるので注意しなければなりません。

glm::mat4 MVP        = Projection * View * Model;

シェーダ

MVPができたら、この行列をシェーダに渡して全ての頂点に掛けてもらいます。
default.vert

#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 MVP;

void main()
{
   gl_Position = MVP*vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

main.cpp

GLuint MatrixID = glGetUniformLocation(shaderProgram.ID, "MVP");

シェーダのMVPの位置を表すIDを取得している。

仕上げ

最後に描画をしているループの中で次のように書きます。
glUniformMatrix4fvで、シェーダのMVPに対して値を渡し、glDrawElementsで三角形を描画しています。

glUniformMatrix4fv(MatrixID, 1, GL_FALSE, glm::value_ptr(MVP));
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0)


フルのコードはこちらです。
https://github.com/k41531/opengl-mac/blob/cube/main.cpp

Discussion