编程

基于Babylon的三维模型旋转指南

chenmo · 1月12日 · 2022年 · · · 112次已读
Babylon_Rotate_Mesh

基于Babylon的三维模型旋转指南

前言

在做基于Babylon的三维橱柜项目时,需要用到旋转的功能,探究了一下,发现三维旋转有着多种方法,且多种方法有着自己的特性,因此在此记录一下。

 

正文

旋转理论

三维旋转常见的有三种表示方式:矩阵旋转欧拉旋转四元数

  • 矩阵旋转使用了一个4*4大小的矩阵来表示绕任意轴旋转的变换矩阵;
  • 欧拉旋转则是按照一定的坐标轴顺序(例如先x、再y、最后z)、每个轴旋转一定角度来变换坐标或向量,它实际上是一系列坐标轴旋转的组合;
  • 四元数则是使用四维的四元数就可以表示任意方向.

1.矩阵旋转

简单来说,一个点在二维或三维里旋转一定角度后新的坐标,是可以通过一个矩阵计算求解的,具体的证明过程可参见:旋转矩阵(Rotation Matrix)的推导及其应用

优缺点
  • 优点:

    • 旋转轴可以是任意向量.
  • 缺点:

    • 旋转其实只需要知道一个向量 + 一个角度,一共4个值的信息,但矩阵法却使用了16个元素;
    • 而且在做乘法操作时也会增加计算量,造成了空间和时间上的一些浪费.

2.欧拉旋转

欧拉角是用来确定定点转动刚体位置的3个一组独立角参量,由章动角θ(or β ) 、旋进角(即进动角)ψ(or γ )和自转角φ(or α )组成。任何一个旋转可以表示为依次绕着三个旋转轴旋三个角度的组合。这三个角度称为欧拉角 三个轴可以指固定的世界坐标系轴,也可以指被旋转的物体坐标系的轴。三个旋转轴次序不同,会导致结果不同。

用一句话说,欧拉角就是物体绕坐标系三个坐标轴(x,y,z轴)的旋转角度。

img

由于欧拉角并没有一个统一的标准,这样就会导致不同人描述欧拉角的旋转轴旋转的顺序可能是不同的,因此使用其他人的欧拉角时,应当搞清楚对方的约定。具体的欧拉角还挺复杂的,更多详细内容可参见:欧拉角

由于Babylon最常用的方法(Rotation)是基于欧拉角来决定模型的旋转的,因此简单介绍一下欧拉角

在Babylon中,需要记住:

  • 旋转角度是左手系;
  • 旋转的中心默认是模型的本地中心点;
  • 参照系可以是本地坐标系,也可以是世界坐标系,也可以是指定的旋转轴;
  • Babylon的旋转的顺序是YXZ.
优缺点
  • 优点:

    • 很容易理解,形象直观;

    • 表示更方便,只需要3个值(分别对应x、y、z轴的旋转角度).

      但按我的理解,它还是转换到了3个3*3的矩阵做变换,效率不如四元数

  • 缺点:

    • 之前提到过这种方法是要按照一个固定的坐标轴的顺序旋转的,因此不同的顺序会造成不同的结果;

    • 会造成万向节锁(Gimbal Lock)的现象;

      这种现象的发生就是由于上述固定坐标轴旋转顺序造成的。理论上,欧拉旋转可以靠这种顺序让一个物体指到任何一个想要的方向,但如果在旋转中不幸让某些坐标轴重合了就会发生万向节锁,这时就会丢失一个方向上的旋转能力,也就是说在这种状态下我们无论怎么旋转(当然还是要原先的顺序)都不可能得到某些想要的旋转效果,除非我们打破原先的旋转顺序或者同时旋转3个坐标轴。

      这里有个视频可以直观的理解下:欧拉旋转

    • 由于万向节锁的存在,欧拉旋转无法实现球面平滑插值.

3.四元数旋转

四元数是一个四维向量(x,y,z,w),要成为一个旋转四元数,它必须是一个单位向量,即x²+y²+z²+w²=1。举个例子,如果使用一个四元数q=((x,y,z)sinθ/2, cosθ/2) 来执行一个旋转,即把空间的一个点P绕着单位向量轴u = (x, y, z)表示的旋转轴旋转θ角度。具体的证明方式可参见:四元数

优缺点
  • 优点:

    • 可以避免万向节锁现象;
    • 只需要一个4维的四元数就可以执行绕任意过原点的向量的旋转,方便快捷,在某些实现下比旋转矩阵效率更高;
    • 可以提供平滑插值.
  • 缺点:

    • 比欧拉旋转稍微复杂了一点点,因为多了一个维度;
    • 理解更困难,不直观.

实例

模型本地旋转

1.Rotation

更改网格方向的最直接方法是“旋转(rotation)”特性。

Demo: Rotation

 

2.AddRotation

若想实现首先围绕y轴,然后围绕x轴,最后围绕z轴,在模型本地坐标系中应用旋转,然后围绕自定义轴序列旋转。使用上面的rotation属性显然有些余力不足。这里其实涉及隐式或显式旋转四元数,但是可以使用addRotation方法——使用其中两个参数为0的addRotation函数。

上述代码是将模型先按照x轴旋转 π/2, 然后按照 z轴旋转π/3,

该方法的本质是将欧拉角转换成了四元数,然后再转回来。

Demo: AddRotation

 

3.Rotate

想象一个圆盘的中心有一个轴,圆盘能够绕轴旋转。下图显示了光盘围绕轴的几个不同旋转点。

disc rotate

当轴也可以旋转时:

disc rotate and axle tilt

可以发现,当为轴指定旋转方向,和为圆盘指定旋转方向,是两种围绕不同坐标系的旋转。圆盘是本地坐标系,轴是世界坐标系。

rotate方法也是可以累加的,如下:

将从模型的当前方向开始,令其按照给定本地坐标轴(2, -3, 7)旋转π/3,然后关于世界y轴旋转 -π/2,关于给定世界坐标轴(5.6, 7.8, – 3.4)旋转1.5π,最后关于局部z轴旋转-π。

Demo: Rotate

 

4. Rotation Quaternions (旋转四元数)

我们已经使用了rotate来设置网格的旋转四元数。

rotation属性相同,“旋转四元数”属性设置以本地坐标系原点为旋转中心的模型方向。

除了旋转,还可以使用直接获取旋转四元数,通过rotationQuaternion属性设置模型的旋转,例如:

旋转四元数的参数为:轴方向,角度。轴方向是基于世界坐标系的。

旋转四元数可以转换为欧拉角:

注意:不能在同一模型上使用旋转四元数后跟旋转。应用旋转四元数后,任何后续使用旋转都将产生错误的方向,除非首先将旋转四元数设置为null。请注意,这在导入模型时通常适用,因为这些模型中有许多已经具有旋转四元数集。

 

模型外部旋转

模型的旋转通常由两部分构成:旋转中心,且穿过旋转中心三维方向向量定义,旋转中心位置向量定义。在Babylon.js创建模型时,旋转中心默认为模型的本地坐标系的中心原点,即模型的位置。使用旋转通过Euler角度alpha、beta、gamma指定轴,使用rotationQuaternionrotate 明确指定轴(方向与角度)。

但除此之外,也可以更改旋转中心,主要也三种方法:

1. TransformNode

TransformNode是未渲染的对象,但可以用作旋转中心(实际上是任何变换的中心)。这可以减少内存使用并提高渲染速度。TransformNode本质是通过将其作为pilot的子对象并旋转来用作轴心点。

模型会根据旋转中心的位置和旋转轴来旋转,且当旋转中心和旋转轴变化的时候,模型也会跟着变化:

Demo:

 

2. Parent

若模型存在父级,当父级旋转时,模型会随父级旋转而旋转:

Demo: Parent

 

3. Pivot

有时希望考虑通过更改父级位置而不是模型位置来实现父级的定位。此时可以通过矩阵设置相对于父级的模型位置、旋转。

Demo: Pivot

 

参考文献

 

 

 

 

 

0 条回应

🎉 总访问量:13161 今日访问量:323 您是今天第:323 个访问者🎉