上一节我们学习了基本变换的原理与实现,本节我们学习如何把这些变换应用到坐标系变换中。
坐标系变换的分类
前面我们讲过,在 3D 编程中,可控制的坐标系变换分为3类,分别是:
- 模型变换
- 模型变换负责将模型坐标转换成世界坐标。
- 视图变换
- 视图变换负责将世界坐标转换成相机坐标。
- 投影变换
- 投影变换负责将相机坐标转换成裁剪坐标,也就是将 3D 坐标投影到 2D 平面上。
需要强调一点变换矩阵相乘的顺序,假设最终变换矩阵为 F,模型矩阵为 M, 视图矩阵为 V,投影矩阵为 P,那么有:
$ F = P \times V \times M $
这个顺序不能有错,否则效果与预想的会不一致。
接下来,我们从第一个变换模型变换
讲起。
坐标系变换的起点
编程之初,我们首先能拿到的是模型数据,模型中各个点的位置是相对于模型的某个位置确定的,一般这个位置是模型的中心点,下面这个坐标系就是模型坐标系,立方体的所有顶点坐标都是相对于中心位置。
默认情况下,模型坐标系和世界坐标系重合,所有创建好的模型首先会放置在世界坐标系中心位置,我们创建一个立方体和一个球体:
可以看到,我们创建的模型叠加在了一起。
那么,我们需要将我们的模型摆放在 3D 世界中的特定位置,3D 世界所遵循的参照就是世界坐标系。
这就涉及到模型变换了,模型变换的作用是将模型顶点从模型坐标系
转换到世界坐标系
。
模型变换
模型变换是由多个基本变换组合而成,那么对矩阵而言,就是由多个基本变换矩阵相乘而得到,既然提到相乘,那么相乘的顺序
就至关重要,因为矩阵不满足交换律,两个矩阵相乘顺序不同,结果也不同。体现到坐标系变换上,就会发现模型经过变换后的坐标也不同,这一点很重要,请大家一定要牢记。
变换顺序
接下来我们演示下,对一个立方体进行平移旋转变换。
平移与旋转
我们先将立方体平移5 个单位,然后逆时针旋转 45 度,看一下立方体是什么状态。
平移矩阵:
var translateMatrix = matrix.translate(5, 0, 0);
旋转矩阵:
var rotateMatrix = matrix.rotateX(Math.PI / 180 * 45);
创建两个立方体,红色立方体先旋转再平移,黄色立方体先平移再旋转。
先旋转再平移矩阵:
var redMatrix = matrix.multiply(translationMatrix, rotateMatrix);
先平移再旋转矩阵:
var yellowMatrix = matrix.multiply(rotateMatrix, translationMatrix);
将两个矩阵应用到立方体上,我们可以看到下面的效果:
很容易发现,当我们需要模型自身绕着模型中心旋转时,要先执行旋转再平移,党需要模型绕世界中心旋转时,需要先平移再旋转。但通常情况,我们都是先执行旋转再执行平移,也就是红色立方体的旋转效果。
考虑缩放
当然,除了平移与旋转,有时也会涉及到模型缩放,很显然,对模型进行缩放也是要放在平移之前,但是缩放和旋转哪个在先,哪个在后呢?
先缩放再旋转,还是先旋转再缩放?
依然通过两个立方体来比较,红色立方体先执行缩放,再进行旋转,黄色立方体则先旋转再缩放,在这里缩放比例采用 X 轴放大 2 倍, Y 轴放大 1.5 倍来处理。
可以看到,先旋转再缩放的黄色立方体不是我们所期望的结果,它会改变模型的形状。而先缩放再旋转
的红色立方体则是我们所期望的。
上例我们采用的是不一样的缩放比例,大家能很容易地看出差别,这次我们采用一样的缩放比例,看下效果:
可以看到,当缩放矩阵的缩放比例一致时,旋转与缩放的顺序就不那么重要了,表现都是一样的。
模型变换公式
假设模型变换矩阵为 M,其中缩放矩阵为 S,旋转矩阵为 R,平移矩阵为 T,考虑到我们是列主序,所以有如下公式:
$M = T(平移矩阵) \times R(旋转矩阵) \times S(缩放矩阵) $
模型变换的意义
前面我们讲了,3D 世界中会有很多模型,每个模型所处的位置和朝向都不一样,这就需要我们对它们进行安放,模型变换就是我们安放模型的手段。
回顾
本节主要讲解坐标系变换的分类,以及模型变换
的作用以及使用时需要注意的地方,主要是矩阵相乘顺序。
下一节,我们讲解视图变换的推导与应用。