跳轉到內容

OpenGL 程式設計/現代 OpenGL 教程 04

來自華夏公益教科書,自由的教科書

在本教程中,我們將深入探討變換矩陣的世界,以便我們可以平移、旋轉和縮放三角形。

矩陣設定

[編輯 | 編輯原始碼]

以下是使用矩陣時需要記住的一些要點

  • 變換是透過以相反的順序乘以 4x4 矩陣來應用的。M = M_translation * M_rotation 表示先旋轉,然後平移。
  • 單位矩陣是不執行任何操作的矩陣 - 根本沒有變換。
  • 要變換頂點,我們將它乘以矩陣:v' = M * v
  • 4x4 矩陣只能應用於 4x1 向量,我們透過在頂點的第 4 維使用 1 來獲得:(x,y,z,1)。

為了進行這些乘法,我們需要一個數學庫。著色器自帶對矩陣運算的內建簡單支援,但通常我們需要從 C 程式碼中操作矩陣。它也更有效,因為著色器對每個頂點執行一次,因此最好提前計算矩陣。

本教程將使用 OpenGL 數學 (GLM) 庫,該庫是用 C++ 編寫的。GLM 傾向於使用與 GLSL 相同的約定,因此更容易上手。它的文件還描述了對已棄用的 OpenGL 1.x 和 GLU 函式的替換,例如 glRotateglFrustumgluLookAt,如果您已經使用過它們,這將非常方便。

還存在其他選擇,例如 libSIMDx86(順便說一下,它也適用於非 x86 處理器)。您也可以編寫自己的矩陣程式碼,因為程式碼並不長,例如在 Mesa3D 演示中檢視 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() 中的 vertices 陣列設定

  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() 中新增我們的變換矩陣,並計算一個與平移相結合的漸進旋轉

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);
  [...]

mat4(1.0f)是單位矩陣,這意味著我們從頭開始進行變換。

傳遞變換矩陣

[編輯 | 編輯原始碼]

正如我們在上一教程中看到的,我們將使用 glUniformMatrix4fv 新增一個新的 uniform。

  /* 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 " << 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);
  [...]
我們的三角形,已變換

我們注意到我們仍然有縱橫比問題(就像在 16:9 顯示器上全屏觀看電視節目一樣)。我們將在下一個教程中使用模型-檢視-投影矩陣來解決此問題。

實驗!

[編輯 | 編輯原始碼]

還記得我們提到的以相反的順序應用矩陣嗎?在我們的示例中,我們先旋轉,然後平移。

嘗試以相反的方式進行:您將使三角形在移動後旋轉,這意味著它將圍繞原點旋轉而不是圍繞其自身中心旋轉。

< OpenGL 程式設計

瀏覽和下載 完整程式碼
華夏公益教科書