MVP矩阵—图形的空间变换

0)前言

这一块的内容需要提前充分理解空间与坐标系的关系,以及矩阵的变换,否则是无法理解的。可以看下面的

1)坐标空间

在计算机图形学中,当我们拥有了模型数据后,我们要将其显示到屏幕上,一般我们要通过5个空间变换,这样才能将不同的物体显示在同一张图上,这5个空间如下:

  • 局部空间(Local Space,或者称为物体空间(Object Space))
  • 世界空间(World Space)
  • 观察空间(View Space,或者称为视觉空间(Eye Space))
  • 裁剪空间(Clip Space)
  • 屏幕空间(Screen Space)
    spaces
    局部空间:就是物体自己坐在的坐标系,实际上每个物体都是在自己的空间内定义的,那么如何将多个物体合并到一起显示呢?如果都按照它们的局部空间的坐标显示,那么所有的物体很大的可能它们都会聚到同一个点上。这样显然是不合适的,因此我们通过一个Model矩阵,将每个物体都变换到同一个空间下的不同位置上。这个空间就是世界空间,这个Model矩阵就是M矩阵(不同物体的M矩阵肯定是不同的)。

世界空间:通过M矩阵将所有的物体都变换到的共同的空间。

观察空间:变换到了同一个世界空间后,还有一个问题,就是空间是立体的,而我们的眼睛(摄像机)是无法在同一个时间点看完整个空间的,因此观察空间就是摄像机能看到的空间。这里是通过View 矩阵来实现的空间变换,V矩阵是跟摄像机紧密相关的。

裁剪空间:将物体变换到观察空间后,实际上还是有一个投影的操作的。因为世界是3维的,但是图像却是2维的。因此需要一个投影的矩阵将3维物体投影到2维空间上,这个就是Projection矩阵。P矩阵跟投影的算法有关。一般由两种,一种是正交投影,一种是透射投影。
正射投影:
mvp1
透视投影:
mvp2
在现实世界中,透视投影才是更加贴近实际效果的投影。

屏幕空间:最后我们将裁剪空间做一下平移就得到立刻最终的屏幕空间。

因此我们可以看到上面的操作,主要内容是通过MVP矩阵,我们就将物体显示在了屏幕空间中。下面我们来挨个分析这三个矩阵。

2)Model矩阵

上面说了Model矩阵是将物体从局部空间变换到了世界空间,而这个变换操作一般就只有3种操作,平移,旋转和缩放。利用glm的库函数,我们可以如下生成矩阵:

1
2
3
4
glm::mat4 trans;
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.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));

上面表示构造一个位移的矩阵,将物体的位置移动(1.0,1.0,0.0)个单位,将物体逆时针旋转90度,然后缩放为原来的0.5倍

3)View矩阵

View矩阵跟camera的参数强相关的。当我们根据camera信息定义了右向量,上向量,以及方向向量后,就可以得到View 矩阵了。
具体的这些向量是怎么来的,在camera的章节中有讲述。

1
2
3
4
glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));

4)Projection矩阵

对于正交投影,我们可以使用下面的函数生成projection矩阵

1
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

前两个参数指定了平截头体的左右坐标,第三和第四参数指定了平截头体的底部和顶部。通过这四个参数我们定义了近平面和远平面的大小,然后第五和第六个参数则定义了近平面和远平面的距离。这个投影矩阵会将处于这些x,y,z值范围内的坐标变换为标准化设备坐标。

对于透视投影:

1
glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);

第一个参数是FOV,也就是视野。它定义了观察空间的大小。第二个参数是宽高比,第三个参数数近平面,第四个参数是远平面。
mvp3

参考资料:
https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/#_5