OpenGLプログラミング/モダンOpenGL チュートリアル 04

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

このチュートリアルでは、変換行列の世界に飛び込んでいき、それで三角形を移動したり、拡大縮小したり、回転させることができるようになります。

行列のセットアップ[編集]

テンプレート:Wikibooks ここにあるのが行列を扱うときに覚えておくべきひとかじりです:

  • 変換は逆の順序で4x4の行列を乗算することにより適用されます。 M = M_translation * M_rotation なら、回転が最初でその後平行移動を意味します。
  • 単位行列は、何もしない行列です - 何も変換しません。
  • 頂点を変換するには、行列によって乗算します: v' = M * v
  • 4x4行列は4x1ベクトルにだけ適用することができ、頂点の第4次元の1を用いて取得します: (x, y, z, 1)。

これらの乗算を行うためには、数学ライブラリが必要になります。 シェーダには内蔵の簡単な行列演算用のサポートが付属しますが、通常はCコードから行列を操作する必要があります シェーダが各頂点ごとに実行されることから、それはまた、より効率的でもあるので、事前に行列を計算する方が良いでしょう。

このチュートリアルでは、 OpenGL Mathematics (GLM) ライブラリを使用し、これはC++で書かれています。 GLMはGLSLと同じ規則を使用する傾向があり、そのためスタートしやすくなっています。 その資料では、非推奨のOpenGL 1.xやGLU関数を置き換える、 OpenGL 1.x and GLU functions, such as glRotate, glFrustum , gluLookAt, なども説明していて、既にそれらを使用しているのであれば役に立つはずです。

代替案も存在し、 libSIMDx86などがあります (ちなみに、x86以外のプロセッサ上でも動作します )。 あなた自身の行列コードを書くこともでき、それほど長くはないので、 mesa-demos-8.0.1/src/egl/opengles2/tri.cという例をMesa3D demosで見てみましょう。

注:私たちはC++を使用しているため、私たちのプログラムも同様にC + +切り替える必要あります - 申し訳ありませんが、最初からそれを行うためには最初のチュートリアルを編集する必要があります。 実際には、 triangle.ctriangle.cppへ名前を変更する必要があるだけです。

GLMは、ヘッダのみのライブラリなので、Makefileを変更する必要はなく、ヘッダが標準パスにインストールされてさえいればかまいません。 DebianやUbuntuのもとでGLMをインストールするには:

apt-get install libglm-dev

これでもうGLMのヘッダを追加することができます:

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

3Dポイントの使用[編集]

私たちの変換行列は、3Dの頂点のためのものです。 たとえ現時点で2Dでも、Z = 0の3Dポイントとして三角形を描いていきます。 いずれにしても、次のチュートリアルでは3Dオブジェクトに移っていきます :)

triangle.cppのOpenGLに、これ(頂点ごとに3つの要素)を定義してみましょう:

struct attributes {
  GLfloat coord3d[3];
  GLfloat v_color[3];
};

そのあと, in init_resources()内で

  struct attributes triangle_attributes[] = {
    {{ 0.0,  0.8, 0.0}, {1.0, 1.0, 0.0}},
    {{-0.8, -0.8, 0.0}, {0.0, 0.0, 1.0}},
    {{ 0.8, -0.8, 0.0}, {1.0, 0.0, 0.0}}
  };

...

  attribute_name = "coord3d";
  attribute_coord3d = glGetAttribLocation(program, attribute_name);
  if (attribute_coord3d == -1) {
    fprintf(stderr, "Could not bind attribute %s\n", attribute_name);
    return 0;
  }

onDisplay()内で頂点配列の設定を変更します

  glVertexAttribPointer(
    attribute_coord3d,   // attribute
    3,                   // number of elements per vertex, here (x,y,z)
    GL_FLOAT,            // the type of each element
    GL_FALSE,            // take our values as-is
    sizeof(struct attributes),  // next coord3d appears every 6 floats
    0                    // offset of first element
  );

'attribute_coord2d'のもうひとつの出現箇所もそれに応じて置き換えて、新しい座標を使用するようシェーダーに伝えます:

attribute vec3 coord3d;
[...]
void main(void) {
  gl_Position = vec4(coord3d, 1.0);

変換行列を作成する[編集]

GLMには、回転、平行移動、拡大縮小の行列を計算する組み込み関数が付属しています。 変換行列を onIdle()に追加して、変換と組み合わせて順回転を計算させてみましょう:

void onIdle() {
  float move = sinf(glutGet(GLUT_ELAPSED_TIME) / 1000.0 * (2*3.14) / 5); // -1<->+1 every 5 seconds
  float angle = glutGet(GLUT_ELAPSED_TIME) / 1000.0 * 45;  // 45° per second
  glm::vec3 axis_z(0, 0, 1);
  glm::mat4 m_transform = glm::translate(glm::mat4(1.0f), glm::vec3(move, 0.0, 0.0))
    * glm::rotate(glm::mat4(1.0f), angle, axis_z);
  [...]

変換行列を渡す[編集]

前のチュートリアルで見たように、新しいuniformを、 glUniformMatrix4fvで追加します :

  /* Global */
  #include <glm/gtc/type_ptr.hpp>
  GLint uniform_m_transform;
  /* init_resources() */
  uniform_name = "m_transform";
  uniform_m_transform = glGetUniformLocation(program, uniform_name);
  if (uniform_m_transform == -1) {
    fprintf(stderr, "Could not bind uniform %s\n", uniform_name);
    return 0;
  }
  /* onIdle() */
  glUniformMatrix4fv(uniform_m_transform, 1, GL_FALSE, glm::value_ptr(m_transform));

GLMを使用しない場合、次のように、GLfloat[16]配列へのポインタを渡すだけで十分です:

  GLfloat matrix[16] = {...};
  glUniformMatrix4fv(uniform_m_transform, 1, GL_FALSE, matrix);

頂点シェーダはちょうど上で見たように行列を頂点に掛けなければなりません:

uniform mat4 m_transform;
void main(void) {
  gl_Position = m_transform * vec4(coord3d, 1.0);
  [...]
Our triangle, transformed

まだアスペクト比の問題があることに注意しましょう(16:9モニタのフルスクリーンでテレビ番組を見ているような)。 次のチュートリアルでは、モデル- ビュー-投影行列を用いて、これを修正していきます。

実験 ![編集]

行列を逆の順序で適用することについて述べたことを覚えているでしょうか? ? この例では、まず回転させ、つぎに平行移動させています。

逆回りの方法でそれをやってみましょう:三角形を移動させた後に回転させることになり、原点を中心にではなく自身の中心の周りに回転させることを意味します。