跳转到内容

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 编程

浏览和下载 完整代码
华夏公益教科书