OpenGL started 6: Matrix and transformation

This article is a personal record of learning, learning is recommended to see the tutorial https://learnopengl-cn.github.io/
very grateful to the original author JoeyDeVries and quality tutorials, mostly Chinese translation provided by

Recent

Feel sad

Foreword

Before reading this blog, you must have a basic understanding of vectors and matrices, and can skillfully perform arithmetic vectors and matrices

We already know how to create an object, coloring, adding texture, but they are still static objects
we can try to change the object's frame at each vertex buffers and reconfiguration so that they move
but such an operation is too complex, and consumption performance is also great

We now have a better solution, using multiple matrix (Matrix) Object Transform (Transform) of an object

If you have a mathematical basis vectors and matrices I said, the next operation is very simple

Use matrix transformation vector

Zoom (Scale)

Of the length of the vector is scaled, its direction is kept unchanged

Since we are 2 or 3-dimensional operation, we can define a respective two or three scaling variable vector, each variable scale one axis (x, y, or z)

Remember, the OpenGL usually operating in 3D space , for the 2D case we can scale 1 times the z-axis, only the operation of x and y axes, so that the value of the z-axis to change, each axis scaling factor ( scaling Factor) are not the same, is uneven (Non-uniform) scaling , are the same so called uniform scaling (uniform scale)

We will construct the following transformation matrix to provide a zoom feature for us:

Each diagonal elements will respectively corresponding elements of the vector from the matrix by multiplying learned that if we put 1 into a 3 will happen? This way, then, we put each element of the vector is multiplied by 3, and which in fact put vector zoom 3 times

If we expressed as the scaling variable (S1, S2, S3) we can define an arbitrary vector (x, y, z) a scaling matrix:

Fourth still scaling vector w 1, w component because scale in the 3D space is meaningless (w component and another for other purposes)

Displacement (Translation)

On the basis of the original plus another vector to obtain a vector on, so in the course of the new vector displacement vectors in different locations based on the movement of the original vector, you certainly learned a vector addition, it should not be too familiar

And scaling matrix, as in the 4 × 4 matrix are several special locations to perform a specific operation, the displacement is the fourth column are the top three values. If we take the displacement vector is represented as (Tx, Ty, Tz), we will be able to shift matrix is ​​defined as:

1564305462487

With displacement matrix we can move in three directions on the object (x, y, z), which is our conversion kit useful in a transformation matrix

Rotation (Rotate)

First, let's define what a vector rotation in the end yes. 2D or 3D space using the rotation angle (Angle) is represented, the angle may be an angle in radians or made of round angle 360 ​​is the angle or 2pi radians

Most functions require a rotation angle in radians, but fortunately the angle of the corner may be made readily converted to radians of:

  • Radian angle:角度 = 弧度 * (180.0f / PI)
  • Degrees to radians:弧度 = 角度 * (PI / 180.0f)

PIApproximately equal to 3.14159265359

Half turn rotates 360/2 = 180 degrees, rotated to the right rotates clockwise circle represents 1/5 360/5 = 72 degrees. The figure shows a 2D vector is rotated to the right 72 degrees is obtained:

Rotation in 3D space need to define a corner and a rotation shaft (Rotation Axis)

Using trigonometry, a given angle, a vector can be transformed into a new vector after rotation, which is usually used a series of sine and cosine functions (sin and generally referred COS) obtained by combining various ingenious

3D space rotation matrix in each unit has a different definition of the axis, the rotation angle is represented by θ:

Along the x-axis:

Along the y axis:

Along the z-axis:

利用旋转矩阵我们可以把任意位置向量沿一个单位旋转轴进行旋转,也可以将多个矩阵复合,比如先沿着x轴旋转再沿着y轴旋转。但是这会很快导致一个问题——万向节死锁(Gimbal Lock)

在这里我们不会讨论它的细节,但是对于3D空间中的旋转,一个更好的模型是沿着任意的一个轴,比如单位向量\((0.662, 0.2, 0.7222)\)旋转,而不是对一系列旋转矩阵进行复合。这样的一个(超级麻烦的)矩阵是存在的,见下面这个公式,其中(Rx,Ry,Rz)代表任意旋转轴:

但是,即使这样一个矩阵也不能完全解决万向节死锁问题(虽然能尽量避免),避免万向节死锁的终极解决方案是使用四元数(Quaternion),它不仅更安全,而且计算会更有效率,这里暂不介绍四元数

矩阵的组合

使用矩阵进行变换的真正力量在于,根据矩阵之间的乘法,我们可以把多个变换组合到一个矩阵中

让我们看看我们是否能生成一个变换矩阵,让它组合多个变换:假设我们有一个顶点(x, y, z),我们希望将其缩放2倍,然后位移(1, 2, 3)个单位,我们需要一个位移和缩放矩阵来完成这些变换:

注意,当矩阵相乘时我们先写位移再写缩放变换的,矩阵乘法不遵守交换律,这意味着它们的顺序很重要。当矩阵相乘时,在最右边的矩阵是第一个与向量相乘的,所以你应该从右向左读这个乘法,建议在组合矩阵时,先进行缩放操作,然后是旋转,最后才是位移,否则会互相影响

用最终的变换矩阵左乘我们的向量会得到以下结果:

向量先缩放2倍,然后位移了(1, 2, 3)个单位

实践

OpenGL没有自带任何的矩阵和向量的东西,所以我们必须定义自己的数学类和函数,在教程中我们更希望抽象所有的数学细节,使用已经做好了的数学库

幸运的是,有个易于使用,专门为OpenGL量身定做的数学库,那就是GLM

GLM是 OpenGL Mathematics 的缩写,它是一个只有头文件的库,也就是说我们只需包含对应的头文件就行了,不用链接和编译。GLM可以在它们的网站上下载,把头文件的根目录复制到你的includes文件夹,然后你就可以使用这个库了

GLM库从0.9.9版本起,默认会将矩阵类型初始化为一个零矩阵(所有元素均为0),而不是单位矩阵(对角元素为1,其它元素为0)。如果你使用的是0.9.9或0.9.9以上的版本,你需要将所有的矩阵初始化改为 glm::mat4 mat = glm::mat4(1.0f)。如果你想与本教程的代码保持一致,请使用低于0.9.9版本的GLM,或者改用上述代码初始化所有的矩阵。

我们需要的GLM的大多数功能都可以从下面这3个头文件中找到:

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

我们来看看是否可以利用我们刚学的变换知识把一个向量(1, 0, 0)位移(1, 1, 0)个单位(注意,我们把它定义为一个glm::vec4类型的值,齐次坐标设定为1.0):

glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f); // 向量(1, 0, 0)
// 如果使用的是0.9.9及以上版本,下面这行代码就需要改为:
// glm::mat4 trans = glm::mat4(1.0f)
glm::mat4 trans; // 单位矩阵
// 传递单位矩阵和一个位移向量
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f)); 
vec = trans * vec; // trans为位移矩阵
std::cout << vec.x << vec.y << vec.z << std::endl; // 210

我们先用GLM内建的向量类定义一个叫做vec的向量,接下来定义一个mat4类型的trans(4×4单位矩阵),下一步是创建一个变换矩阵,我们是把单位矩阵和一个位移向量传递给glm::translate函数来完成这个工作的(然后用给定的矩阵乘以位移矩阵就能获得最后需要的矩阵)
之后我们把向量乘以位移矩阵并且输出最后的结果,得到的向量应该是(1 + 1, 0 + 1, 0 + 0),也就是(2, 1, 0)。这个代码片段将会输出210,所以这个位移矩阵是正确的。

我们来做些更有意思的事情,让我们来旋转和缩放之前教程中的那个箱子。首先我们把箱子逆时针旋转90度。然后缩放0.5倍,使它变成原来的一半大。我们先来创建变换矩阵:

glm::mat4 trans;
trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5)); 

首先,我们把箱子在每个轴都缩放到0.5倍,然后沿z轴旋转90度,GLM希望它的角度是弧度制的(Radian),所以我们使用glm::radians将角度转化为弧度,注意有纹理的那面矩形是在XY平面上的,所以我们需要把它绕着z轴旋转,因为我们把这个矩阵传递给了GLM的每个函数,GLM会自动将矩阵相乘,返回的结果是一个包括了多个变换的变换矩阵

下一个大问题是:如何把矩阵传递给着色器?我们在前面简单提到过GLSL里也有一个mat4类型,所以我们将修改顶点着色器让其接收一个mat4的uniform变量,然后再用矩阵uniform乘以位置向量:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

uniform mat4 transform;

void main()
{
    gl_Position = transform * vec4(aPos, 1.0f);
    TexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y);
}

GLSL也有mat2mat3类型从而允许了像向量一样的混合运算。前面提到的所有数学运算(像是标量-矩阵相乘,矩阵-向量相乘和矩阵-矩阵相乘)在矩阵类型里都可以使用。当出现特殊的矩阵运算的时候我们会特别说明

在把位置向量传给gl_Position之前,我们先添加一个uniform,并且将其与变换矩阵相乘。我们的箱子现在应该是原来的二分之一大小并(向左)旋转了90度。当然,我们仍需要把变换矩阵传递给着色器:

unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

我们首先查询uniform变量的地址,然后用有Matrix4fv后缀的glUniform函数把矩阵数据发送给着色器

  1. 第一个参数你现在应该很熟悉了,它是uniform的位置值
  2. 第二个参数告诉OpenGL我们将要发送多少个矩阵,这里是1
  3. 第三个参数询问我们我们是否希望对我们的矩阵进行置换(Transpose),也就是说交换我们矩阵的行和列,OpenGL开发者通常使用一种内部矩阵布局,叫做列主序(Column-major Ordering)布局,GLM的默认布局就是列主序,所以并不需要置换矩阵,我们填GL_FALSE
  4. 最后一个参数是真正的矩阵数据,但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此我们要先用GLM的自带的函数value_ptr来变换这些数据。

我们创建了一个变换矩阵,在顶点着色器中声明了一个uniform,并把矩阵发送给了着色器,着色器会变换我们的顶点坐标。最后的结果应该看起来像这样:

我们的箱子向左侧旋转,并是原来的一半大小,所以变换成功了

我们现在想让箱子随着时间旋转,并且把箱子放在窗口的右下角

我们必须在游戏循环中更新变换矩阵,因为它在每一次渲染迭代中都要更新,我们使用GLFW的时间函数来获取不同时间的角度:

glm::mat4 trans;
trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));
trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));

要记住的是前面的例子中我们可以在任何地方声明变换矩阵,但是现在我们必须在每一次迭代中创建它,从而保证我们能够不断更新旋转角度,这意味着我们不得不在每次游戏循环的迭代中重新创建变换矩阵,通常在渲染场景的时候,我们也会有多个需要在每次渲染迭代中都用新值重新创建的变换矩阵

Here we put a box around the origin (0, 0, 0) rotation, and then we put the box after a rotational displacement of the lower right corner of the screen, keep in mind that the actual transformation should reverse the order and reading order: in the code we first then rotate displacement , the actual transformation is applied first and then a rotation displacement

If you do right, you will see the following results:

vs7blfFLVk

awesome

Now you can understand why the matrix in the graphics area is such an important tool, we can define an unlimited number of transformation, but combining them is merely a matrix , if you wish we can reuse it

Use redefined skill matrix may be omitted in the vertex shader data, it is possible to save processing time, because we do not have to re-send our data (this is a very slow process)

Guess you like

Origin www.cnblogs.com/zhxmdefj/p/11272841.html