OpenGL編程/現代OpenGL教程 04

在本教程中,我們會涉足變換矩陣(transformation matrices)的世界,以便讓我們可以平移、旋轉以及縮放我們的三角形。

設置矩陣事宜

編輯
書架

自然科學 - 數學 - 生物學 - 工程學 - 計算機科學 - 醫學 - 體育
人文科學 - 哲學 - 藝術 - 文學 - 語言 - 歷史 - 社會科學 - 商業與經濟學 - 雜項

一些在使用矩陣時要在意的地方(tidbits):

  • 變換是通過對4x4的矩陣進行逆序相乘達成的。M = M_translation * M_rotation 意味着首先旋轉,然後平移。
  • 單位矩陣(identity matrix)是指那種什麼都不做的矩陣——沒有任何變換。
  • 爲了變換一個頂點(vertex),我們使用矩陣對它進行相乘:v' = M * v
  • 4x4矩陣僅能被用在4x1向量上——該向量是由我們將1增加在第4維上後所獲得的:(x, y, z, 1)。

爲了完成這些乘法運算,我們需要一個數學函數庫。着色器帶有對矩陣運算內置、簡易的支持,但是通常情況下我們需要從C代碼中操縱矩陣。這樣做也更加高效,因爲着色器是爲每個頂點執行的,所以說預先計算矩陣是更佳的做法。

該教程將使用OpenGL Mathematics (GLM) 庫,它由C++寫成。GLM有意識地使用和GLSL一樣的約定,於是會更容易起步。它的文檔也描述了對已聲明不贊成使用的OpenGL 1.x和GLU函數——例如glRotateglFrustumgluLookAt——的替代選擇,這將會對那些已經在使用它們的人有很大幫助。

替代項也存在,例如libSIMDx86(順便一提,它也可以工作在非x86處理器上)。你也可以自己寫矩陣代碼,畢竟它也不是很長——可以參考Mesa 3D示例mesa-demos-8.0.1/src/egl/opengles2/tri.c中的代碼。

GLM是一個純頭文件的庫,所以你不需要修改Makefile,而只需要確保頭文件被安裝在一個標準路徑中。 來安裝GLM:

apt-get install libglm-dev  # Debian, Ubuntu
dnf install glm-devel  # Fedora

我們現在可以增加GLM頭文件了:

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

使用3D的點

編輯

我們的變換矩陣是爲3D頂點而設計的。即使我們現在處於2D,也可以利用Z=0來將三角形描述成3D點集。總之,我們會在下個教程中轉移到3D物體上去 :)

在triangle.cpp中給OpenGL定義它(每個頂點3個元素):

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

然後,在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) {
    cerr << "Could not bind attribute " << attribute_name << endl;
    return false;
  }

修改render()中對頂點數組的初始化:

  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帶有內置的函數以計算旋轉、平移和縮放矩陣。 一起在logic()中增加變換矩陣,並且計算一個漸進的旋轉及所搭的(combined)平移:

void logic() {
	float move = sinf(SDL_GetTicks() / 1000.0 * (2*3.14) / 5); // -1<->+1 every 5 seconds
	float angle = SDL_GetTicks() / 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), glm::radians(angle), axis_z);
  [...]

glm4(1.0f)是單位矩陣,意味着我們在草稿狀態已經開始進行變換。

傳遞變換矩陣

編輯

就像我們在前一個教程所看到的那樣,我們會增加一個新的律態——藉由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) {
    cerr << "Could not bind uniform_fade " << uniform_name << endl;
    return false;
  }
  /* logic() */
  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的顯示器上全屏觀看電視節目一樣)。 我們會在下個教程中使用Model-View-Projection矩陣解決它。

進行試驗!

編輯

還記得我們提到過要逆序運用矩陣麼?在我們的例子中,首先是旋轉,然後是平移。

可以試試用另一種方式去做:你會使該三角形在移動後再旋轉,這意味着它會繞着原點旋轉而不是繞着它自己的中心。