注意 如果无法理解向量,矩阵以及变换的概念,请先了解下简单的线性代数,对其有所了解即可
OpenGL没有自带任何的矩阵和向量知识,但是我们可以使用已经做好了的数学库,专门为OpenGL量身定做的数学库,那就是GLM。
GLM是OpenGL Mathematics的缩写,它是一个只有头文件的库,只需包含对应的头文件就行了,不用链接和编译。GLM可以在它们的网站上下载。把头文件的根目录复制到你的includes文件夹,然后你就可以使用这个库了。
下载链接:https://github.com/g-truc/glm/releases/tag/0.9.8.5
测试GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
// 译注:下面就是矩阵初始化的一个例子,如果使用的是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;
std::cout << vec.x << vec.y << vec.z << std::endl;
大多数需要的GLM的功能都可以从上面这3个头文件中找到
这里先用GLM内建的向量类定义一个叫做vec
的向量作为将要被位移的向量,接下来定义一个mat4
类型的trans
,默认是一个4×4单位矩阵。
通过glm::translate
可以创建转化矩阵,第一个参数传入单位矩阵,第二个参数传入位移的向量。这里,传入了一个(1.0,1.0,0.0)的位移。
最后把向量乘以位移矩阵并且输出最后的结果。
自己口算可以知道,结果应该是(2,1,0)。
对图形施加变化
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));
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
首先,把图形在每个轴都缩放到0.5倍,然后沿z轴旋转90度。GLM希望它的角度是弧度制的(Radian),所以要使用glm::radians
将角度转化为弧度。
注意有纹理的那面矩形是在XY平面上的,所以需要把它绕着z轴旋转,即第三个参数。
因为我们把这个矩阵传递给了GLM的每个函数,GLM会自动将矩阵相乘,返回的结果是一个包括了多个变换的变换矩阵。
接着需要在主程序中添加一个uniform。首先查询uniform变量的地址,然后用有Matrix4fv后缀的glUniform函数把矩阵数据发送给着色器。
- 第一个参数是uniform的位置值。
- 第二个参数告诉OpenGL将要发送多少个矩阵,这里是1。
- 第三个参数询问我们是否希望对传入矩阵进行转置,也就是说交换矩阵的行和列。OpenGL开发者通常使用一种内部矩阵布局,叫做列主序布局。GLM的默认布局就是列主序,所以并不需要转置矩阵,填GL_FALSE。
- 最后一个参数是真正的矩阵数据,但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此要先用GLM的自带的函数value_ptr来变换这些数据。
#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);
}
对应的,在shader代码中进行修改,引入转化矩阵,并将其左乘到坐标上。这样就可以完成一个缩放加旋转的变换了。
进一步,可以在循环之中创建随时间变换的变换矩阵,从而使得图形可以旋转起来。
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));
最终应该出现如下情况:
参考资料
[1] https://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/03%20Hello%20Window/
[2] http://bit.ly/2lt7ccM