欧拉旋转、四元数旋转和矩阵旋转
把Euler和Quaternion放在一起是因为他们都是跟旋转相关的类(虽然Matrix也可以做旋转,不过这里我们还是把欧拉和四元数单独拿出来说)
首先来回顾一下欧拉角和四元数的几个区别,这里不做详细展开,有兴趣的同学可以自己查查额外的资料:
欧拉旋转定义了沿XYZ轴的三个旋转角度(逆时针方向),用到了3个值,且必须用一个额外的值规定旋转顺序。
四元数旋转由一个旋转轴(一个向量)和一个旋转角度(逆时针方向)构成,用到了4个值。
欧拉旋转在特定情况下可能产生万向锁,四元数没有这个问题。
也是因为万向锁的问题,欧拉角不能做slerp,也就是球面插值,但是四元数可以。
欧拉旋转通俗易懂,初中生都能明白!
四元数好特么难懂,毕竟我等蝼蚁是三维碳基猴子变的。
这里再多提一句矩阵旋转:
三维矩阵旋转需要用到16个值(其实就是一个4x4变换矩阵),效率和空间上会比四元数和欧拉差一些。
矩阵旋转不能做插值,lerp或slerp都不行。
但是某些情况下矩阵旋转非常好用,比如做二维的UV旋转时。
上面写了那么多,看起来我好像很偏袒四元数似的。确实,四元数在某些情况下非常好用,比如一个物体A绕着物体B的正方向轴做公转的问题,只需一个正方向向量就可以轻松构建出四元数并解决这个问题。但在一些常见的简单业务场景,比如物体绕着自身坐标系中某个轴自转,完全可以用更直观更简单的欧拉旋转来编写逻辑。
不过说一千道一万,threejs中的物体旋转底层逻辑是用四元数解决的,矩阵和欧拉旋转都只是一个表象。等我们分析到Object3D时会看到。
Euler
先来看看比较简单的欧拉旋转类。
通过构造函数我们可以知道,threejs中的欧拉角是默认按照XYZ轴的顺序进行旋转计算的,比较符合大多数人的习惯。除了构造函数外,欧拉对象还有其他的赋值方式:
但无论是哪种方式,我们可以看到顺序都是必须要确定的。这里order是个可选参数,默认还是使用XYZ这个顺序。
setFromVector3比较好理解,使用了三维向量的XYZ的值直接定义了XYZ轴上的旋转角度:
setFromQuaternion和setFromRotationMatrix其实是一回事,最后都会调用setFromRotationMatrix。
在四元数赋值时,先把四元数转换成了一个旋转矩阵:
compose的方法中的具体逻辑就不赘述了,总之我们只要记住:
用位置+四元数+缩放可以构成一个4x4变换矩阵。
在使用欧拉角构建变换矩阵时,位置是(0,0,0),缩放是(1,1,1)
在setFromRotationMatrix中,我们可以看到threejs是如何通过旋转矩阵来计算欧拉旋转的。因为欧拉旋转本身是跟顺序相关的,所以内部有大量的cases判断
最后我们发现会有一个参数是否控制回调函数_onChangeCallback(),不过这个参数一直是undefined,也就是说这个回调函数会被调用。
查找发现这个回调函数默认情况下啥都没有,但是可以通过_onChange方法赋值。也许后面会用到,我们暂时放在一旁不管。
总结
欧拉旋转默认代表沿XYZ轴分别旋转3个角度
欧拉旋转可以用四元数、向量或矩阵赋值,用四元数会先转换成位置000缩放111的4x4变换矩阵,再用矩阵赋值。