万向节死锁产生的原因

万向节死锁产生的根本原因是,旋转矩阵是依次进行的,假设先围绕x轴旋转,再围绕y轴旋转,最后围绕z轴旋转,这就导致物体其实是围绕自己的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旋转。


可以自己通过欧拉角计算出顶点旋转后的坐标来验证这个。


参考资料:

万向节死锁(Gimbal Lock)

欧拉角与万向节死锁(图文版)

旋转矩阵公式推导


附上自己的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正半轴:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值