变换
1.向量
向量有一个方向(Direction)和大小(Magnitude,也叫做强度或长度)。
数学家喜欢在字母上面加一横表示向量,比如说v¯v¯。当用在公式中时它们通常是这样的:
1.1.向量相乘
1.1.1.点乘
我们该如何计算点乘呢?点乘是通过将对应分量逐个相乘,然后再把所得积相加来计算的。
1.1.2.叉乘
叉乘只在3D空间中有定义,它需要两个不平行向量作为输入,生成一个正交于两个输入向量的第三个向量。如果输入的两个向量也是正交的,那么叉乘之后将会产生3个互相正交的向量。
下面你会看到两个正交向量A和B叉积:
2.矩阵
2.1.矩阵相乘
相乘还有一些限制:
- 只有当左侧矩阵的列数与右侧矩阵的行数相等,两个矩阵才能相乘。
- 矩阵相乘不遵守交换律(Commutative),也就是说A⋅B≠B⋅AA⋅B≠B⋅A。
相乘规则实例:
2.2.矩阵与向量相乘
如果我们有一个M×N矩阵,我们可以用这个矩阵乘以我们的N×1向量,因为这个矩阵的列数等于向量的行数,所以它们就能相乘。用这个矩阵乘以我们的向量将变换(Transform)这个向量。
2.3.单位矩阵
在OpenGL中,由于某些原因我们通常使用4×4的变换矩阵,而其中最重要的原因就是大部分的向量都是4分量的。我们能想到的最简单的变换矩阵就是单位矩阵(Identity Matrix)。单位矩阵是一个除了对角线以外都是0的N×N矩阵。
3.缩放
如果我们把缩放变量表示为(S1,S2,S3)我们可以为任意向量(x,y,z)定义一个缩放矩阵:
4.位移
在4×4矩阵上有几个特别的位置用来执行特定的操作,对于位移来说它们是第四列最上面的3个值。如果我们把位移向量表示为(Tx,Ty,Tz),我们就能把位移矩阵定义为:
向量的w分量也叫齐次坐标。想要从齐次向量得到3D向量,我们可以把x、y和z坐标分别除以w坐标。我们通常不会注意这个问题,因为w分量通常是1.0。
5.旋转
沿x轴旋转:
沿y轴旋转:
沿z轴旋转:
但是对于3D空间中的旋转,一个更好的模型是沿着任意的一个轴,比如单位向量 ( 0.662 , 0.2 , 0.7222 ) (0.662, 0.2, 0.7222) (0.662,0.2,0.7222)旋转,而不是对一系列旋转矩阵进行复合。这样的一个(超级麻烦的)矩阵是存在的,见下面这个公式,其中(Rx,Ry,Rz)代表任意旋转轴:
扩展部分:
上述绕轴旋转,和绕任意轴旋转存在万向节死锁问题(绕任意轴旋转会极大地避免)。避免万向节死锁的解决方案是使用四元数(Quaternion),它不仅更安全,而且计算会更有效率。万向锁,四元数不在此展开,需要时,单独了解。
6.矩阵的组合
使用矩阵进行变换的真正力量在于,根据矩阵之间的乘法,我们可以把多个变换组合到一个矩阵中。
实例:
用整合后的单个矩阵乘以向量,实现向量先缩放2倍,然后位移了(1, 2, 3)个单位。
7.GLM&OpenGL实例
// 4*4单位矩阵
glm::mat4 transform = glm::mat4(1.0f);
// transform=transform*glm::vec3(0.5f, -0.5f, 0.0f)对应的平移矩阵
transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f));
// transform=transform*[glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)]所确定的旋转矩阵
transform = glm::rotate(transform, glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));ourShader.use();
// 获得着色器中命名uniform变量位置
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
// 用于设置着色器中类型mat4的变量
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
对glUniformMatrix4fv:
第二个参数告诉OpenGL我们将要发送多少个矩阵,这里是1。第三个参数询问我们是否希望对我们的矩阵进行转置(Transpose),也就是说交换我们矩阵的行和列。OpenGL开发者通常使用一种内部矩阵布局,叫做列主序(Column-major Ordering)布局。GLM的默认布局就是列主序,所以并不需要转置矩阵,我们填GL_FALSE
。最后一个参数是真正的矩阵数据,但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此我们要先用GLM的自带的函数value_ptr来变换这些数据。