OpenGL编程/现代OpenGL教程 04
在本教程中,我们会涉足变换矩阵(transformation matrices)的世界,以便让我们可以平移、旋转以及缩放我们的三角形。
设置矩阵事宜
编辑书架 |
自然科学
- 数学
- 生物学
- 工程学
- 计算机科学
- 医学
- 体育 |
一些在使用矩阵时要在意的地方(tidbits):
- 变换是通过对4x4的矩阵进行逆序相乘达成的。
M = M_translation * M_rotation
意味着首先旋转,然后平移。 - 单位矩阵(identity matrix)是指那种什么都不做的矩阵——没有任何变换。
- 为了变换一个顶点(vertex),我们使用矩阵对它进行相乘:
v' = M * v
- 4x4矩阵仅能被用在4x1向量上——该向量是由我们将1增加在第4维上后所获得的:(x, y, z, 1)。
为了完成这些乘法运算,我们需要一个数学函数库。着色器带有对矩阵运算内置、简易的支持,但是通常情况下我们需要从C代码中操纵矩阵。这样做也更加高效,因为着色器是为每个顶点执行的,所以说预先计算矩阵是更佳的做法。
该教程将使用OpenGL Mathematics (GLM) 库,它由C++写成。GLM有意识地使用和GLSL一样的约定,于是会更容易起步。它的文档也描述了对已声明不赞成使用的OpenGL 1.x和GLU函数——例如glRotate
、glFrustum
或gluLookAt
——的替代选择,这将会对那些已经在使用它们的人有很大帮助。
替代项也存在,例如libSIMDx86(顺便一提,它也可以工作在非x86处理器上)。你也可以自己写矩阵代码,毕竟它也不是很长——可以参考Mesa 3D示例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()中对顶点数组的初始化:
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()
中增加变换矩阵,并且计算一个渐进的旋转及所搭的(combined)平移:
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);
[...]
glm4(1.0f)是单位矩阵,意味着我们在草稿状态已经开始进行变换。
传递变换矩阵
编辑就像我们在前一个教程所看到的那样,我们会增加一个新的律态——借由glUniformMatrix4fv
:
/* 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_fade " << 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的显示器上全屏观看电视节目一样)。 我们会在下个教程中使用Model-View-Projection矩阵解决它。
进行试验!
编辑还记得我们提到过要逆序运用矩阵么?在我们的例子中,首先是旋转,然后是平移。
可以试试用另一种方式去做:你会使该三角形在移动后再旋转,这意味着它会绕着原点旋转而不是绕着它自己的中心。