【前言】
歐拉角對人來說是十分直覺的,很适合人機互動中,但不适用于插值和疊代。
在說到歐拉角時有兩點非常重要:旋轉方式和旋轉順序
【旋轉方式】
首先要區分每次旋轉是繞固定軸旋轉的,還是繞旋轉之後的軸旋轉的。
繞固定軸旋轉就是旋轉過程中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