萬向節死鎖產生的原因
阿新 • • 發佈:2018-11-02
萬向節死鎖產生的根本原因是,旋轉矩陣是依次進行的,假設先圍繞x軸旋轉,再圍繞y軸旋轉,最後圍繞z軸旋轉,這就導致物體其實是圍繞自己的X軸旋轉,而不是世界座標的X軸旋轉。
然後,進行一次旋轉時,物體是先圍繞x軸進行旋轉,這時候物體的區域性座標系和世界座標系是重合的,雖然是圍繞世界座標的x軸旋轉,但也是圍繞自己的x軸進行旋轉。最後得到的旋轉結果,也變成了物體是繞自己的x軸轉的結果了。
表現就是,在一個尤拉角(x,y,z)下,改變x的值,物體會圍繞物體自己的x軸進行旋轉,而不是世界座標系的x軸進行旋轉。
下圖所示,改變x值,都是在繞自己的紅色的軸(x軸)轉。RGB三個顏色分別對應XYZ正半軸:
首先,旋轉3D模型時,實際是對原始模型的頂點進行旋轉。比如旋轉(x1,y1,z1),就是把原始模型旋轉到這個角度,然後渲染顯示。當旋轉變成(x2,y2,z2),會把原始模型旋轉到這個角度,然後渲染顯示。
然後,進行一次旋轉時,物體是先圍繞x軸進行旋轉,這時候物體的區域性座標系和世界座標系是重合的,雖然是圍繞世界座標的x軸旋轉,但也是圍繞自己的x軸進行旋轉。最後得到的旋轉結果,也變成了物體是繞自己的x軸轉的結果了。
最後,當把物體的x軸旋轉到與世界的z軸重合時,歐垃角的x和z旋轉結果就都一樣了,也就丟失了一個維度。另一方面,比如在(30, 30, 30)的歐垃角下,把y從30調到60會發現並不是繞自己的y軸在轉,也不是繞世界座標的y旋轉。
可以自己通過尤拉角計算出頂點旋轉後的座標來驗證這個。
參考資料:
附上自己的Unity測試程式碼:
using UnityEngine; [ExecuteInEditMode] public class TestEuler : MonoBehaviour { public Vector3 Euler; public Vector3 OrgPosOfA; public Vector3 OrgPosOfB; public Vector3 OrgPosOfC; public Transform TransA; public Transform TransB; public Transform TransC; // Use this for initialization void Start() { } // Update is called once per frame void Update () { TransA.position = Rotate(OrgPosOfA, Euler) + transform.position; TransB.position = Rotate(OrgPosOfB, Euler) + transform.position; TransC.position = Rotate(OrgPosOfC, Euler) + transform.position; } Vector3 Rotate(Vector3 pos, Vector3 euler) { pos = RotateX(pos, euler.x * Mathf.Deg2Rad); pos = RotateY(pos, euler.y * Mathf.Deg2Rad); pos = RotateZ(pos, euler.z * Mathf.Deg2Rad); return pos; } Vector3 RotateX(Vector3 pos, float r) { Vector3 newPos = pos; float cos = Mathf.Cos(r); float sin = Mathf.Sin(r); newPos.y = pos.y * cos + pos.z * sin; newPos.z = -pos.y * sin + pos.z * cos; return newPos; } Vector3 RotateY(Vector3 pos, float r) { Vector3 newPos = pos; float cos = Mathf.Cos(r); float sin = Mathf.Sin(r); newPos.x = pos.x * cos - pos.z * sin; newPos.z = pos.x * sin + pos.z * cos; return newPos; } Vector3 RotateZ(Vector3 pos, float r) { Vector3 newPos = pos; float cos = Mathf.Cos(r); float sin = Mathf.Sin(r); newPos.x = pos.x * cos + pos.y * sin; newPos.y = -pos.x * sin + pos.y * cos; return newPos; } void OnDrawGizmos() { //座標軸 Gizmos.color = Color.red; Gizmos.DrawLine(transform.position, transform.position + Vector3.right); Gizmos.color = Color.green; Gizmos.DrawLine(transform.position, transform.position + Vector3.up); Gizmos.color = Color.blue; Gizmos.DrawLine(transform.position, transform.position + Vector3.forward); //旋轉後的點 Gizmos.color = Color.red; Gizmos.DrawLine(transform.position, TransA.position); Gizmos.color = Color.green; Gizmos.DrawLine(transform.position, TransB.position); Gizmos.color = Color.blue; Gizmos.DrawLine(transform.position, TransC.position); } }
下圖所示,改變x值,都是在繞紅色的軸(x軸)轉。RGB三個顏色分別對應XYZ正半軸: