节点变换矩阵
我们应该已经知道了如何创建一个几何,并给它定义材质,使它看起来更加立体和生动,但它仍然是静态物体。如果我们想渲染出一个带有动画的场景,应该怎么做呢?需要用到渲染节点node的transform属性,这个属性用于变换渲染节点在3D空间中的方位,同时也可以对节点内的渲染几何进行拉伸。
transform属性的数据类型是Matrix4,对一个向量进行任意缩放、平移和旋转变换,都可以看作是给向量左乘一个矩阵,如果存在多次变换,那就相当于连续多次左乘矩阵。那么问题来了,矩阵是如何与旋转、缩放和平移一一对应的呢?
想要表达3D空间中的一个旋转,需要定义一个角度θ和一个旋转轴u=(x,y,z),这个旋转轴是任意方向的单位向量。我们可以用一个四元数q来描述对向量ν0进行旋转,形成一个新向量ν1:
q=[cos(21θ),sin(21θ)u]
ν1=qν0q−1
四元数与一个旋转矩阵对应,令a=cos(21θ),b=sin(21θ)ux,c=sin(21θ)uy,d=sin(21θ)uz,这个矩阵如下:
Mrotation=1−2c2−2d22bc+2ad2bd−2ac02bc−2ad1−2b2−2d22ab+2cd02ac+2bd2cd−2ab1−2b2−2c200001
将矩阵作用到向量ν0上,可以得到同样的结果:
ν1=Mrotationν0
对空间中的一个向量进行缩放,由于向量的三个方向上的缩放系数可能不尽相同,所以可以用一个缩放向量来描述:(S1S2S3 ),这个向量与一个缩放矩阵对应:
Mscaling=S10000S20000S300001
我们将该矩阵与一个任意向量(xyz )左乘,就会得到一个被缩放的新向量
S10000S20000S300001xyz1=x×S1y×S2z×S31
平移是在原始向量的基础上,加上一个平移向量从而获得一个不同位置的新向量,如果我们把平移向量表示为:(T1T2T3 ),我们就可以把平移矩阵定义为:
Mtranslation=100001000010T1T2T31
我们将该矩阵与一个任意向量(xyz )左乘,就会得到一个被平移的新向量
100001000010T1T2T31xyz1=100001000010x+T1y+T2z+T31
首先我们定义一个transform矩阵:
auto transform = _mat4();
Matrix4类具备以下操作:
-
Matrix4::toRotation(), 获取当前矩阵的旋转四元数
-
Matrix4::toScaling(), 获取当前矩阵的缩放向量
-
Matrix4::toTranslation(), 获取当前矩阵的平移向量
-
Matrix4::fromRotation(r), 将旋转四元数r转换成矩阵
-
Matrix4::fromScaling(s), 将缩放向量s转换成矩阵
-
Matrix4::fromTranslation(t), 将平移向量t转换成矩阵
-
Matrix4::rotate(r), 将当前矩阵按照该旋转四元数进行旋转
-
Matrix4::scale(s), 将当前矩阵按照该缩放向量进行缩放
-
Matrix4::translate(t), 将当前矩阵按照该平移向量进行平移
在这个例子中,我们将上面定义的transform矩阵按照向量(2, 0, 0)平移,然后我们将该transform矩阵传递给一个渲染节点node,那么在渲染时,该节点中的渲染对象primitive在3D空间中的位置相较原来的位置平移了(2, 0, 0)。
transform->translate(vec3(2, 0, 0));
auto node = _node({ primitive }, transform);
案例二:定位器操纵几何
在下面的这个例子中,我们将定位器渲染节点dragger的transform变化,同步给一个cube几何所在的渲染节点,从而实现通过定位器来操纵cube几何。
dragger->bind("begun", [&]()->void {
t1 = dragger->getTransForm().toTranslation();
q1 = dragger->getTransForm().toRotation();
cubeInit = cubeNode->transform ? *cubeNode->transform : mat4();
});
dragger->bind("updated", [&]()->void {
auto t2 = dragger->getTransForm().toTranslation();
auto q2 = dragger->getTransForm().toRotation();
cubeNode->transform = std::make_shared<Matrix4f>(Matrix4f::fromRotation(q2) * Matrix4f::fromRotation(-q1) * Matrix4f::fromTranslation(t2 - t1) * cubeInit);
render();
});