【前言】
欧拉角对人来说是十分直观的,很适合人机交互中,但不适用于插值和迭代。
在说到欧拉角时有两点非常重要:旋转方式和旋转顺序
【旋转方式】
首先要区分每次旋转是绕固定轴旋转的,还是绕旋转之后的轴旋转的。
绕固定轴旋转就是旋转过程中XYZ轴不变,绕旋转之后的轴旋转表示每次旋转时XYZ改变了。
在Unity中绕固定轴旋转可以看做是绕世界坐标系旋转。看下图中的鲨鱼的Transform中,面板上显示的Rotation为(0,0,0)。在场景中由于我们选择的是Global,相当于绕固定轴旋转。(这是鲨鱼的初始图)
现在我们把鲨鱼的Rotation改为(0,90,0),显然我们的意思是绕Y轴正方向逆时针旋转了90°。(这里要注意旋转正负角度时是如何旋转的,一般来说逆时针旋转为正值,顺时针旋转为负值。)
注意看鲨鱼身上的坐标轴没有变。
现在把Global改为Local,再看鲨鱼身上的坐标轴,蓝色的Z轴和红色的X轴变了。这就是绕旋转之后的坐标轴旋转,下次再旋转时以这个新的坐标轴为准。
【旋转顺序】
绕固定轴旋转时,分别绕X、Y、Z旋转90°(左图)和分别绕Z、Y、X旋转90°(右图)所得结果不同。(鲨鱼的初始图看上面)
绕旋转之后的轴旋转时,同样地,分别绕X、Y、Z轴旋转90°(左图)和分别绕Z、Y、X旋转90°(右图)所得结果仍然不同。
因而,在用欧拉角表示旋转时,需要先声明旋转顺序,否则,按照不同的旋转顺序得到的结果不同。
【Unity中的旋转方式和旋转顺序】
transform.Rotate (Vector3 eulers, Space relativeTo= Space.Self);
在Unity中旋转物体时,我们通常会用到Rotate,官网文档说的是绕固定轴按照ZXY顺序旋转。固定轴有两个选项,世界空间的轴向,模型自身的轴向。这意味着:
代码A transform.Rotate(new Vector3(60, 45, 90),Space.Self);
代码B transform.Rotate(new Vector3(0, 0, 90), Space.Self);
transform.Rotate(new Vector3(45, 0, 0), Space.Self);
transform.Rotate(new Vector3(0, 60, 0), Space.Self);
这两者所得结果不同,在A中是按照当前模型的轴向先绕Z轴旋转90°,再绕X轴旋转45°,再绕Y轴旋转60°。在B中,先按照当前模型的轴向旋转90°,此时模型自身的轴向改变了;之后以改变了的轴向再旋转,绕X轴旋转45°,旋转后,模型自身的轴向又改变了;最后,绕又改变的轴向绕Y轴旋转60°。
代码C: transform.Rotate(new Vector3(60, 45, 90),Space.World);
代码D: transform.Rotate(new Vector3(0, 0, 90), Space.World);
transform.Rotate(new Vector3(45, 0, 0), Space.World);
transform.Rotate(new Vector3(0, 60, 0), Space.World);
这两种所得结果相同,因为每次都是按照世界空间的轴向来旋转。(注:D中的顺序不能改变,否则得到的结果也不一样,因为顺序改变了相当于你改变了旋转顺序,虽然Unity在执行每个Rotate时都按照ZXY的顺序来执行的。)
【面板中的Rotation】
我们都知道面板这里的Rotaton表示欧拉角,X的值表示绕X旋转多少度,Y的值表示绕Y旋转多少度,Z的值表示绕Z旋转多少度。在这个面板中,对于(60,45,90)这个欧拉角来说,无论你按照XYZ、ZXY、ZYX等任何顺序输入,你会看到模型的结果都是一样的。为什么会这样?上文不是说不同的旋转顺序会导致不同的结果吗?
这是因为Unity内部,对于当前面板上的欧拉角,其都是先从(0,0,0)开始按照ZXY的顺序旋转(计算)得到的。也就相当于模型初始的欧拉角的(0,0,0),然后执行了 transform.Rotate(new Vector3(60, 45, 90),Space.Self)。
(如果模型在(0,0,0)时的坐标系与世界坐标系相同,那么按照Space.Self和Space.World旋转的结果是一样的。否则会出现差别,所以在做模型时要求模型坐标系与Unity世界坐标系相同。)
当你按照XYZ顺序输入的时候,你只能一个个地输入。先输入X,此时面板上为(60,0,0),Unity按照ZXY的顺序从(0,0,0)开始计算得到一个结果。然后输入Y,此时面板上为(60,45,0),Unity按照ZXY的顺序从(0,0,0)开始计算得到一个结果,而不是从(60,0,0)开始计算。然后输入Z····
所以,到这里你就可以理解为什么输入顺序不同最后结果都一样了吧。
这是你输入的情况,当你左右滑动连续改变X、Y、Z的值的时候,实际上也都是从(0,0,0)开始计算得到的结果的。因为算得足够快,所以你在场景中看到模型也是连续旋转的。
也许多数时候,你滑动X或Y或Z的值,模型确实是在自身坐标系下绕X(或Y或Z)的情况来旋转的,但有时候就不是这样。
例如,先设置Local,确保模型的Rotation在(0,0,0)时模型坐标系与世界坐标系一致。之后将Rotation设置为(0,0,90)。此时,你再滑动X,你会看到模型不是绕X轴旋转的;你滑动Y,你会看到模型不是绕Y轴旋转的。
这是为什么呢?为什么模型没有按照你想象的方式旋转呢?
因为模型的Rotation是从(0,0,0)开始计算的,因为算得足够快,所以在人眼看来模型就是跟着你滑动的X或Y或Z连续变化的。
这会导致万向锁的问题。
【欧拉角的万向锁】
实际上无论是绕固定轴还是绕旋转之后的轴旋转,无论你怎么旋转,都不会导致万向锁的问题。所以,你不用奇怪,你感觉无论怎么旋转都不会导致万向锁是正确的。
你之所以会观察到万向锁的现象是由于Unity计算欧拉角和人习惯计算欧拉角的方式不同所导致的。模型当前的欧拉角为(60,45,0),要变为(60,45,90)时。Unity会从(0,0,0)变到(60,45,90);而人从(60,45,0)变到(60,45,90)。两者的计算方式不同,而你在Unity中看见的结果是按照Unity自身的方式计算得到的,不是按照你想向的方式计算得到的,所以你会看见让你难以理解的万向锁的问题。
下图展示了万向锁的问题,你会发现无论怎样滑动Y、Z,鲨鱼始终是在世界坐标系的XZ平面。按照人的计算方式,滑动Y时鲨鱼的尾巴应该会朝上的,但没有。
由于Unity是按照固定轴按照ZXY的顺序来旋转的,所以按照Unity的计算方式,总是最后再绕Y轴旋转。所以,当你滑动Y,连续改变欧拉角时,看来像是再绕着世界坐标系的Y轴旋转,而不是绕模型的Y轴来旋转。所以你在Unity中,无论XZ的值时怎样的,只要你滑动Y,Unity计算欧拉角的方式给你呈现的视觉效果就是像围绕世界坐标系的Y轴来旋转一样。
而当你滑动Z时,由于Unity总是先旋转绕Z轴旋转的,所以给你呈现的视觉效果是滑动Z时在场景中看到模型真的在绕模型的Z轴旋转。而有时你滑动X呢,你会发现模型既不绕世界坐标系的X轴旋转,也不绕模型坐标系的X轴旋转。
因而,当模型的X为90时,使得模型坐标系的Z轴和世界坐标系的Y轴在同一条线上,所以你无论怎样滑动Y都看不到鲨鱼尾巴立起来的视觉效果。
那么为什么计算欧拉角时都要从(0,0,0)开始算呢?这个问题还没有搞明白,以后在补充。
【参考】
https://blog.csdn.net/fengya1/article/details/50721768