OpenGL学习笔记6-Transformations

Transformations(转换)

我们现在知道如何创建对象,颜色和/或使用纹理给它们一个详细的外观,但它们仍然没有那么有趣,因为它们都是静态对象。我们可以尝试通过改变它们的顶点和重新配置它们的缓冲来移动它们,但是这样做很麻烦并且消耗了相当多的处理能力。转换对象有更好的方法,那就是使用(多个)矩阵对象。这并不意味着我们要谈论功夫和一个庞大的数字人工世界。

矩阵是非常强大的数学结构,乍一看似乎有些可怕,但一旦你习惯了它们,就会证明它们非常有用。当讨论矩阵时,我们将不得不稍微深入一些数学知识,对于更倾向于数学的读者,我将发布额外的资源以供进一步阅读。

然而,为了完全理解转换,在讨论矩阵之前,我们首先必须深入研究向量。本章的重点是给你一个基本的数学背景的主题,我们将在后面需要。如果主题是困难的,试着尽可能多地理解它们,然后在需要的时候回到本章回顾这些概念。

Vectors(向量)

在它最基本的定义中,向量是方向,仅此而已。矢量有方向和大小(也称为强度或长度)。你可以把矢量想象成藏宝图上的方向:“向左走10步,现在向北走3步,向右走5步”;左边是方向,10步是矢量的大小。因此,藏宝图的方向包含3个矢量。向量可以有任何维数,但我们通常处理的维数是2到4。如果一个向量有2维,它表示平面上的一个方向(考虑二维图形),如果它有3维,它可以表示三维世界中的任何方向。

下面您将看到3个向量,其中每个向量在2D图中用箭头(x,y)表示。因为以2D(而不是3D)显示向量更直观,所以可以把2D向量看作z坐标为0的3D向量。因为向量代表方向,所以向量的原点不会改变它的值。在下面的图中我们可以看到,尽管矢量v¯和w¯w¯的来源不同,但它们是相等的:

在描述向量时,数学家通常喜欢将向量描述为头顶上有一个小横杠的字符符号,比如v¯v¯。另外,在公式中显示向量时,通常用以下方式表示:

因为向量被指定为方向,所以有时候很难把它们想象成位置。如果我们想想象向量位置我们可以想象的起源方向向量为(0,0,0),然后指定点指向一个方向,使其位置向量(我们也可以指定一个不同的起源,然后说:“这个向量指向,从这个起源点在空间)。在原点为(0,0)的图上,位置向量(3,5)将指向(3,5)。利用向量,我们可以在二维和三维空间中描述方向和位置。

就像使用正规数一样,我们也可以定义对向量的几种操作(其中一些您已经看到了)。

Scalar vector operations(标量向量操作)

标量是一位数。当用标量对向量进行加减乘除时,我们只需对向量的每个元素进行加减乘除。为了加它看起来是这样的:

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

Where ++ can be ++ ,−− ,⋅⋅ or ÷÷ where ⋅⋅ is the multiplication operator.

Vector negation向量否定

使一个向量负向,就会使一个向量的方向相反。一个指向东北方向的矢量在否定后将指向西南方向。为了对一个向量求负数,我们在每个分量上加一个负号(你也可以用标量值为-1的标量向量乘法表示):

 

Addition and subtraction(加法和减法)

两个向量的加法定义为分量加法,即一个向量的每个分量加到另一个向量的相同分量上,这样:

视觉上,在向量v=(4,2)和k=(1,2)上是这样的,其中第二个向量加在第一个向量的端点上,找到得到的向量的端点(头尾法):

 

就像一般的加减法一样,向量的减法和第二个向量的加减是一样的:

两个向量相减得到的向量是两个向量所指向的位置的差值。在某些情况下,当我们需要检索两个点之间的差值时,这被证明是有用的。

Length(长度)

为了获取一个向量的长度/大小,我们使用你可能在数学课上记得的毕达哥拉斯定理。当你把一个向量的x和y分量看成三角形的两条边时,它就形成了一个三角形:

因为两边(x, y)的长度是已知的,我们想知道倾斜边的长度v¯v¯,我们可以使用毕达哥拉斯定理来计算:

 

其中,||v¯||||v¯||表示为向量v¯v¯的长度。通过在方程中加入z2z2,可以很容易地扩展到3D。此时,向量(4,2)的长度为:

这是4.47。

还有一种特殊类型的向量,我们称之为单位向量。单位向量有一个额外的性质那就是它的长度正好是1。我们可以用任何向量的每个分量除以它的长度来计算一个单位向量n^n^:

我们称它为向量的标准化。单位向量在其上方有一个小屋顶,通常更容易处理,特别是当我们只关心它们的方向时(如果我们改变向量的长度,方向不会改变)。

Vector-vector multiplication(向量乘法)

两个向量相乘是一种奇怪的情况。正常增殖并不是定义在向量因为它没有视觉意义,但我们有两个具体案例,相乘时,我们可以选择:一个是点积表示为v¯⋅k¯v¯⋅k¯十字架,另一个是产品表示v¯×k¯v¯×k¯。

Dot product(点积)

两个向量的点积等于它们长度的标量积乘以它们夹角的余弦值。如果这听起来让人困惑,看看它的公式:

其中,它们之间的夹角表示为(θθ).这为什么有趣?假设v¯v¯ and k¯k¯ 是单位向量那么它们的长度就等于1。这将有效地将公式简化为:

现在点积只定义了两个向量的夹角。你们可能还记得cos函数在角为90度时为0当角为0时为1。这使得我们可以很容易地通过点积来检验这两个向量是正交的还是平行的(正交意味着这两个向量是直角的)。如果你想知道更多关于sin或cos函数的知识我建议你看一下可汗学院Khan Academy videos 关于基本三角函数的视频。

你也可以计算出两个非单位向量之间的夹角,但是你必须将两个向量的长度与结果相除得到cosθcosθ. 。

那么如何计算点积呢?点积是一种组件式的乘法我们把结果加在一起。它是这样的,有两个单位向量(你可以验证它们的长度都是1):

为了计算这两个单位向量之间的度数,我们使用余弦函数的倒数cos - 1cos - 1,结果是143.1度。我们现在有效地计算了这两个向量的夹角。点积被证明是非常有用的,当做照明计算以后。

Cross product(交叉乘积,向量积)

叉乘只定义在三维空间中,它取两个非平行的向量作为输入,产生第三个向量,这个向量与两个输入向量正交。如果两个输入向量也是正交的,那么叉乘会得到3个正交的向量;这将在接下来的章节中证明是有用的。下面的图片展示了它在3D空间中的样子:

不像其他运算,不深入线性代数,叉乘并不是很直观,所以最好只是记住公式,你就会没事(或者不,你可能也会没事)。下面是两个正交向量A和B的外积:

正如你所看到的,这似乎没有什么意义。但是,如果你遵循这些步骤你会得到另一个向量它正交于你的输入向量。

Matrices(矩阵)

现在我们已经讨论了几乎所有的向量,是时候进入矩阵了!矩阵是由数字、符号和(或)数学表达式组成的矩形数组。矩阵中的每一项称为矩阵的一个元素。以2x3矩阵为例:

矩阵的下标为(i,j),其中i为行,j为列,因此上述矩阵称为2x3矩阵(3列2行,也称为矩阵的维数)。这与您在为2D图形(x,y)建立索引时所使用的方法相反。为了检索值4,我们将其索引为(2,1)(第二行,第一列)。

矩阵基本上就是这样,只是数学表达式的矩形数组。它们确实有一组很好的数学性质,就像向量一样,我们可以在矩阵上定义一些运算,即:加,减,乘。

Addition and subtraction(加法和减法)

两个矩阵之间的加法和减法是在每个元素的基础上完成的。所以同样的一般规则适用于我们对正规数熟悉的规则,但是对两个矩阵中具有相同索引的元素做了处理。这意味着加法和减法只适用于维数相同的矩阵。一个3x2矩阵和一个2x3矩阵(或者一个3x3矩阵和一个4x4矩阵)不能相加或相减。让我们看看矩阵加法是如何作用于两个2x2矩阵的:

同样的规则也适用于矩阵减法:

 

Matrix-scalar products(矩阵标量产品)

矩阵-标量积乘以矩阵的每个元素乘以一个标量。下面的例子演示了乘法运算:

这也解释了为什么这些单个数字被称为标量。标量基本上是根据它的值缩放矩阵中的所有元素。在前面的例子中,所有的元素都被缩放了2。

到目前为止还好,我们所有的情况都不是很复杂。直到我们开始讲矩阵-矩阵乘法。

Matrix-matrix multiplication(矩阵与矩阵的乘法)

矩阵乘法并不一定复杂,但是很难熟练掌握。矩阵乘法基本上意味着在乘法时遵循一组预定义的规则。不过也有一些限制:

  1. 只有两个矩阵相乘左边矩阵的列数等于右边矩阵的行数。
  2. 矩阵乘法是不交换⋅

让我们从两个2x2矩阵相乘的例子开始:

现在你可能在想刚才到底发生了什么?矩阵乘法是使用左矩阵的行和右矩阵的列的普通乘法和加法的组合。让我们试着用下图来讨论这个问题:

首先取左矩阵的上一行然后取右矩阵的一列。我们选择的行和列决定了我们将要计算的结果2x2矩阵的哪个输出值。如果我们取左矩阵的第一行结果值将会在结果矩阵的第一行结束,然后我们选择一列,如果它是第一列结果值将会在结果矩阵的第一列结束。这就是红色通道的情况。为了计算右下的结果,我们取第一个矩阵的下一行和第th的最右列

为了计算结果值,我们使用普通乘法将行和列的第一个元素相乘,对第二个、第三个、第四个等元素也做同样的操作。然后对单个乘法的结果求和,得到我们的结果。现在,有一个要求是合理的,即左矩阵的列和右矩阵的行大小相等,否则我们无法完成操作!

结果是一个维数为(n,m)的矩阵其中n等于左边矩阵的行数而m等于右边矩阵的列数。

如果你在脑子里想象乘法有困难,也不用担心。只要继续尝试手工计算,当你有困难时回到这一页。随着时间的推移,矩阵乘法成为你的第二天性。

让我们用一个更大的例子来结束对矩阵-矩阵乘法的讨论。试着用颜色来想象图案。作为一个有用的练习,看看你是否能得出自己的乘法答案,然后将它们与结果矩阵进行比较(一旦你尝试手工做一个矩阵乘法,你会很快掌握它们)。

正如你所看到的,矩阵-矩阵乘法是一个相当麻烦的过程,而且很容易出错(这就是为什么我们通常让计算机来做这个),当矩阵变大时,这个问题很快就会出现。如果你还想知道更多你对矩阵的数学性质很好奇我强烈建议你看一下可汗学院关于矩阵的视频Khan Academy videos

不管怎样,既然我们知道了如何把矩阵相乘,我们就可以开始做一些有用的事情了。

Matrix-Vector multiplication(矩阵向量乘法)

到目前为止,我们已经有了足够多的向量。我们用它们来表示位置、颜色甚至纹理坐标。让我们继续往下看,告诉你一个向量基本上是一个Nx1矩阵,其中N是向量的分量数(也被称为N维向量)。如果你仔细想想,就会发现这很有道理。向量就像矩阵一样,是一组数字,但只有一列。那么,这个新的信息是如何帮助我们的呢?如果我们有一个MxN矩阵我们可以将这个矩阵与Nx1向量相乘,因为这个矩阵的列数等于这个向量的行数,因此矩阵乘法就被定义了。

但是为什么我们要关心矩阵和向量的乘积呢?恰好有很多有趣的2D/3D变换我们可以放在一个矩阵中,然后用这个矩阵乘以一个向量然后变换那个向量。如果您仍然有点困惑,让我们以一些例子开始,您很快就会明白我们的意思。

Identity matrix (单位矩阵)

在OpenGL中,我们通常使用4x4变换矩阵,有几个原因,其中一个是,大多数向量的大小是4。我们能想到的最简单的变换矩阵是单位矩阵。单位矩阵是一个NxN矩阵,除了对角线上只有0。正如你将看到的,这个变换矩阵使一个向量完全无损:

向量是完全不动的。这从乘法规则中变得很明显:第一个结果元素是矩阵第一行的每个单独元素与向量的每个元素相乘。由于行中除第一个元素外的每个元素都是0,我们得到:,同样的情况也适用于向量的其他3个元素。

你可能想知道一个不变换的变换矩阵有什么用?单位矩阵通常是生成其他变换矩阵的起点,如果我们进一步深入线性代数,它是证明定理和求解线性方程的非常有用的矩阵。

 

Scaling(缩放比例)

当我们缩放一个矢量时我们增加箭头的长度,以我们想要缩放的量为单位,保持它的方向不变。因为我们在2维或3维中工作,我们可以定义缩放由2或3个缩放变量组成的向量,每个变量缩放一个轴(x, y或z)。

让我们试着缩放向量)我们将沿着x轴将矢量缩放0.5,从而使其窄到原来的两倍;我们将这个向量沿着y轴缩放2,使它的高度是原来的两倍。让我们看看如果我们将向量缩放(0.5,2)等于s¯s¯:

请记住,OpenGL通常在3D空间中运行,所以在这个2D情况下,我们可以将z轴比例设置为1,而不伤害它。我们刚刚执行的缩放操作是非均匀缩放的,因为每个轴的缩放因子不相同。如果标量在所有坐标轴上都相等,那它就叫做均匀标度。

让我们开始构建一个变换矩阵来进行缩放。从单位矩阵中可以看出,每个对角元素都与其对应的向量元素相乘。如果我们把单位矩阵中的1变成3呢?在这种情况下,我们将把每个向量元素乘以一个值3,从而有效地将向量缩放为3。如果我们将缩放变量表示为,我们可以定义任意向量(x,y,z)(x,y,z)上的缩放矩阵为:

 

注意,我们保持第4个缩放值1。我们稍后将看到w组件用于其他目的。

 

Translation(平移)

平移是在原向量上添加另一个向量,以返回位置不同的新向量,从而基于平移向量移动该向量的过程。我们已经讨论过向量的加法,所以这应该不是很新鲜。

就像比例矩阵一样在一个4乘4的矩阵中有几个位置我们可以用来执行特定的操作和转换它们是第4列的前3个值。将平移向量表示为,可以定义平移矩阵:

这是可行的,因为所有的平移值都乘以向量的w列,并添加到向量的原始值(记住矩阵乘法规则)。这对于3×3矩阵是不可能的。

Homogeneous coordinates(齐次笛卡儿坐标)

向量的w分量也称为齐次坐标。为了从齐次向量得到三维向量我们用w坐标除以x y z坐标。我们通常不会注意到这一点,因为w组件大多数时候是1.0。使用齐次坐标有几个优点:它允许我们在三维向量上做矩阵平移(没有w分量我们不能平移向量),在下一章我们将使用w值来创建三维透视图。
同样,当齐次坐标等于0时,这个向量被称为方向向量,因为w坐标为0的向量不能被平移。

通过平移矩阵,我们可以在3个轴方向(x, y, z)的任意方向移动对象,这使得它成为我们的转换工具箱中非常有用的转换矩阵。

 

Rotation(旋转)

最后几个转换在2D或3D空间中相对容易理解和可视化,但是旋转就有点复杂了。如果你想知道这些矩阵是如何构造的,我建议你去看可汗学院线性代数视频linear algebra 中的旋转项。

首先我们来定义向量的旋转是什么。2D或3D中的旋转用角度表示。角可以用角度或弧度表示整个圆是360度或2弧度。我更喜欢用角度来解释旋转因为我们通常更习惯用角度来解释旋转。

大多数旋转函数都需要弧度角度,但幸运的是,角度很容易转换成弧度:

角度表示的角=弧度表示的角* (180 / PI)

弧度表示的角=角度表示的角* (PI / 180)

其中PI等于(圆角)3.14159265359。

 

旋转半圈,360/2 = 180度,向右旋转1/5意味着向右旋转360/5 = 72度。对于一个基本的2D向量,可以证明v¯v¯从k¯k¯向右旋转72度(或顺时针旋转):

在3D中旋转指定了角度和旋转轴。指定的角度将使对象沿着给定的旋转轴旋转。试着通过旋转你的头到一定程度,同时继续向下看一个旋转轴来想象这一点。例如,当在3D世界中旋转2D向量时,我们将旋转轴设置为z轴(尝试将其可视化)。

利用三角函数可以将向量转换为给定角度的新旋转的向量。这通常是通过正弦和余弦函数(通常缩写为sin和cos)的巧妙组合来实现的。关于旋转矩阵是如何生成的讨论超出了本章的范围。

在三维空间中为每个单位轴定义一个旋转矩阵,其中角度表示为符号symbol θ:

Rotation around the X-axis:绕x轴旋转:

 

Rotation around the Y-axis:绕y轴旋转:

 

Rotation around the Z-axis:绕z轴旋转:

利用旋转矩阵,我们可以围绕三个单位轴中的一个变换位置向量。为了绕任意的三维轴旋转,我们可以通过先绕x轴旋转,然后是Y轴,然后是Z轴旋转来组合它们。然而,这很快就引入了一个叫做框架锁的问题。我们不会讨论细节,但是一个更好的解决方案是围绕一个任意的单位轴旋转,例如(0.662,0.2,0.722)(注意这是一个单位向量),而不是组合旋转矩阵。存在(verbose)矩阵,以(Rx,Ry,Rz)(Rx,Ry,Rz)为任意旋转轴,如下所示:

生成这样一个矩阵的数学讨论超出了本章的范围。请记住,即使这个矩阵也不能完全防止万向锁(尽管它变得更加困难)。为了真正防止万向锁,我们必须使用四元数来表示旋转,这不仅更安全,而且在计算上更友好。然而,对四元数的讨论超出了本章的范围。

 

Combining matrices (结合矩阵)

The true power from using matrices for transformations is that we can combine multiple transformations in a single matrix thanks to matrix-matrix multiplication. Let's see if we can generate a transformation matrix that combines several transformations. Say we have a vector (x,y,z) and we want to scale it by 2 and then translate it by (1,2,3). We need a translation and a scaling matrix for our required steps. The resulting transformation matrix would then look like:

使用矩阵进行变换的真正威力在于,借助矩阵-矩阵乘法,我们可以在一个矩阵中组合多个变换。我们看看能否生成一个结合了几个变换的变换矩阵。假设我们有一个向量(x,y,z)我们想把它缩放2然后平移(1,2,3)我们需要一个转换矩阵和一个比例矩阵来完成所需的步骤。由此得到的变换矩阵将如下所示:

 

注意,当我们乘矩阵时,首先做平移,然后做缩放变换。矩阵乘法是不可交换的,这意味着它们的顺序是重要的。当矩阵相乘时,最右边的矩阵首先与向量相乘,所以你应该从右到左阅读乘法。建议在组合矩阵时首先进行缩放操作,然后进行旋转,最后进行平移,否则它们可能会相互影响。例如,如果你首先做一个平移,然后缩放,平移向量也会缩放!

对我们的向量运行最终的变换矩阵,得到以下向量:

太棒了!向量首先被缩放到2,然后平移到(1,2,3)。

In practice(实践)

既然我们已经解释了转换背后的所有理论,现在是时候看看我们如何将这些知识应用到我们的优势上了。OpenGL没有内置任何形式的矩阵或向量知识,所以我们必须定义自己的数学类和函数。在这本书中,我们宁愿从所有微小的数学细节中抽象出来,简单地使用预先制作的数学库。幸运的是,有一个易于使用且为opengl量身定制的数学库,名为GLM。

GLM

GLM代表OpenGL数学是一个只包含头文件的库,这意味着我们只需要包含适当的头文件就行了;不需要链接和编译。GLM可从其网站website. 下载。将头文件的根目录复制到include文件夹中,让我们开始吧。

我们需要的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);
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
vec = trans * vec;
std::cout << vec.x << vec.y << vec.z << std::endl;

我们首先使用GLM的内置向量类定义一个名为vec的向量。接下来,我们定义一个mat4,并通过将矩阵的对角线初始化为1.0来显式地将它初始化为单位矩阵;如果我们不将其初始化为单位矩阵,则该矩阵将是一个空矩阵(所有元素均为0),随后的所有矩阵操作也将以一个空矩阵告终。

下一步是通过将单位矩阵传递给glm::translate函数和一个平移向量(给定的矩阵与一个平移矩阵相乘,返回得到的矩阵)来创建一个变换矩阵。然后我们用变换矩阵乘以我们的向量并输出结果。如果我们还记得矩阵平移是如何工作的,那么结果向量应该是(1+1,0+1,0+0)也就是(2,1,0)这段代码输出210,因此转换矩阵完成了它的工作。

让我们做一些更有趣的事情,缩放和旋转上一章中的容器对象:


glm::mat4 trans = glm::mat4(1.0f);
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期望它的角度以弧度表示,所以我们使用GLM::弧度将角度转换为弧度。注意,纹理矩形在XY平面上,所以我们想绕z轴旋转。记住我们旋转的轴应该是一个单位向量,所以如果你不是绕X, Y, Z轴旋转的话,一定要首先对向量进行标准化。因为我们将矩阵传递给每个GLM的函数,所以GLM自动将这些矩阵相乘,得到一个变换矩阵

下一个大问题是:我们如何得到变换矩阵到着色器?我们在前面提到过GLSL也有mat4类型。因此,我们将调整顶点着色器以接受mat4统一变量,并将位置向量乘以矩阵统一:


#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, aTexCoord.y);
} 

GLSL还具有mat2和mat3类型,它们允许像向量一样的混合器操作。前面提到的所有数学运算(如标量-矩阵乘法、矩阵-向量乘法和矩阵-矩阵乘法)都允许用于矩阵类型。只要使用特殊的矩阵运算,我们一定会解释发生了什么。

在将其传递给gl_Position之前,我们添加了统一的位置向量,并将其与变换矩阵相乘。我们的容器现在应该是原来的两倍,并旋转90度(向左倾斜)。我们仍然需要传递变换矩阵到着色器:


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

我们首先查询uniform变量的位置,然后使用带有矩阵4fv后缀的glUniform将矩阵数据发送到着色器。第一个参数现在应该很熟悉了,那就是uniform的位置。第二个参数告诉OpenGL我们要发送多少个矩阵,即1。第三个参数问我们是否要转置矩阵,也就是交换列和行。OpenGL开发者经常使用一个内部矩阵布局称为列主排序,这是默认的矩阵布局在GLM,所以没有必要转置矩阵;我们可以用GL_FA

我们创建了一个变换矩阵,在顶点着色器中声明了一个uniform的矩阵,并将这个矩阵发送到我们变换顶点坐标的着色器中。结果应该是这样的:

完美!我们的容器确实向左倾斜,而且小了两倍,所以转换成功了。让我们做一些更有趣的事情,看看是否可以随着时间的推移旋转容器,为了好玩,我们还将容器重新定位在窗口的右下角。要随时间旋转容器,我们必须在渲染循环中更新转换矩阵,因为它需要更新每一帧。我们使用GLFW的时间函数来得到一个随时间变化的角度:


glm::mat4 trans = glm::mat4(1.0f);
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));

请记住,在前面的例子中,我们可以在任何地方声明变换矩阵,但是现在我们必须在每次迭代时创建它来不断地更新旋转。这意味着我们必须在渲染循环的每次迭代中重新创建转换矩阵。通常在渲染场景时,我们会在每一帧中使用新值重新创建几个变换矩阵。

在这里,我们首先围绕原点旋转容器(0,0,0),一旦它旋转,我们将其旋转后的版本平移到屏幕的右下角。记住,实际的转换顺序应该反向读取:尽管在代码中我们先转换然后再旋转,但实际的转换首先应用旋转然后再转换。理解所有这些转换组合以及它们如何应用于对象是很困难的。尝试和试验这样的转换,你会很快掌握它。

如果你做了正确的事情,你应该得到以下结果:

就是这样了。一个随时间旋转的已转换容器,全部由一个变换矩阵完成!现在您可以看到为什么矩阵在图形界是如此强大的结构。我们可以定义无限多的变换,并将它们全部合并到一个矩阵中,我们可以随时重复使用。在顶点着色器中使用这样的转换节省了我们重新定义顶点数据的努力,也节省了我们一些处理时间,因为我们不必一直重新发送我们的数据(这是相当慢的);我们需要做的就是更新变换uniform。

如果你没有得到正确的结果或你被困在其他地方,看看源代码source code和更新的着色器类shader

在下一章中,我们将讨论如何使用矩阵来定义顶点的不同坐标空间。这将是我们进入3D图形的第一步!

 

Further reading

  • Essence of Linear Algebra: great video tutorial series by Grant Sanderson about the underlying mathematics of transformations and linear algebra.

Exercises

  • Using the last transformation on the container, try switching the order around by first rotating and then translating. See what happens and try to reason why this happens: solution.
  • Try drawing a second container with another call to glDrawElements but place it at a different position using transformations only. Make sure this second container is placed at the top-left of the window and instead of rotating, scale it over time (using the sin function is useful here; note that using sin will cause the object to invert as soon as a negative scale is appli

     

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值