前置知识,能理解欧拉角的旋转过程和它的万向节死锁。
一、四元数的基本介绍
1.四元数的特点与缺点
1)优点:
可以避免万向节锁现象;
只需要一个4维的四元数就可以执行绕任意过原点的向量的旋转,方便快捷,在某些实现下比旋转矩阵效率更高;
可以提供平滑插值;
为什么可以平滑插值可参考:如何通俗地解释欧拉角?之后为何要引入四元数? - 知乎
补充:大概原因就是因为其最后的角度可以表示成一个欧拉公式形式,在均匀插值的时候只要不断乘以对应的四元数的指数形式即可(指数相乘即指数相加,四元数不断叠乘即旋转角度叠加)。而欧拉角只能在x、y、z各自的方向上进行均匀插值,无法做到任意角均匀插值。
2)缺点:
比欧拉旋转稍微复杂了一点点,因为多了一个维度;
理解更困难,不直观;
接下来带着四元数的优点和缺点去理解四元数会更加有的放矢。
2.四元数的基本运算规律
1)这里面东西太多了,贴几个比较好的帖子大家去看看。
https://krasjet.github.io/quaternion/quaternion.pdf
不过最推荐的还是这个,看着会比较舒服:图形学笔记 - 知乎
2)大致总结一下比较重要的概念:
四元数的虚部与实部、单位四元数、纯四元数、四元数的点积、乘积、叉积、四元数的共轭与逆、四元数表示旋转、四元数的插值。感觉这些概念看懂应该就算入门四元数了吧。
3.能大致理解四元数运算的几何含义
可以参考一下这个:https://jingyan.baidu.com/article/4ae03de3dbbac83eff9e6b00.html
里面提出的四元数可以表示状态和动作的这种说法感觉很赞,对理解记忆四元数有帮助。
二、四元数与欧拉角的相互转化
这可以当成四元数的一个理论理解的练习,推导过于麻烦了,就直接贴出参考链接,这四个链接给出了不错的推导过程,需要注意的就是在unity中欧拉角的旋转顺序。
三维旋转:欧拉角、四元数、旋转矩阵、轴角之间的转换 - 知乎
四元数与欧拉角(RPY角)的相互转换 - XXX已失联 - 博客园
四元数与欧拉角(Yaw、Pitch、Roll)的转换_xiaoma_bk的博客-CSDN博客_四元数与欧拉角
Unity 四元数与欧拉角的相互转换及推导_晒网君的博客-CSDN博客_unity 欧拉角转四元数
接下来我就直接贴出相互转化的测试代码了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class QuaternionRotate : MonoBehaviour
{
void Start()
{
Quaternion q=Quaternion.Euler(60,60,60);
Debug.Log("原始欧拉角 " + new Vector3(60, 60, 60));
Debug.Log("原始四元数 "+q);
Debug.Log("转换的欧拉角 " + QuaternionToEuler(q.x, q.y, q.z, q.w));
Debug.Log("转换的四元数 " + EulerToQuaternion(60,60,60));
}
public Quaternion EulerToQuaternion(float xx, float yy, float zz)
{
float X = xx / 180 * Mathf.PI;
float Y = yy / 180 * Mathf.PI;
float Z = zz / 180 * Mathf.PI;
float x = Mathf.Cos(Y / 2) * Mathf.Sin(X / 2) * Mathf.Cos(Z / 2) + Mathf.Sin(Y / 2) * Mathf.Cos(X / 2) * Mathf.Sin(Z / 2);
float y = Mathf.Sin(Y / 2) * Mathf.Cos(X / 2) * Mathf.Cos(Z / 2) - Mathf.Cos(Y / 2) * Mathf.Sin(X / 2) * Mathf.Sin(Z / 2);
float z = Mathf.Cos(Y / 2) * Mathf.Cos(X / 2) * Mathf.Sin(Z / 2) - Mathf.Sin(Y / 2) * Mathf.Sin(X / 2) * Mathf.Cos(Z / 2);
float w = Mathf.Cos(Y / 2) * Mathf.Cos(X / 2) * Mathf.Cos(Z / 2) + Mathf.Sin(Y / 2) * Mathf.Sin(X / 2) * Mathf.Sin(Z / 2);
Quaternion quataion = new Quaternion(x, y, z, w);
return quataion;
}
public Vector3 QuaternionToEuler(float xx, float yy, float zz, float ww)
{
float X = Mathf.Asin(2 * (ww * xx - yy * zz));
float Y = Mathf.Atan2(2 * (ww * yy + xx * zz), 1 - 2 * (xx * xx + yy * yy));
float Z = Mathf.Atan2(2 * (ww * zz + yy * xx), 1 - 2 * (xx * xx + zz * zz));
Vector3 euler = new Vector3(X * 180f / Mathf.PI, Y * 180f / Mathf.PI, Z * 180f / Mathf.PI);
return euler;
}
}
上面的EulerToQuaternion函数对应unity中的Quaternion.Euler函数,QuaternionToEuler函数对应unity中的q.eulerAngles函数。
三、以Unity举例四元数的使用
该部分主要参考自:
【100个 Unity实用技能】| 游戏中使技能或装备跟随角色环绕,持续旋转_呆呆敲代码的小Y的博客-CSDN博客_unity 技能
四元数主要就是旋转和旋转插值用,用下面的代码可以做出一个某个物体一直跟随另一个物体旋转(跟随旋转)的效果。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Saber : MonoBehaviour {
public Transform targetPos;//旋转中心对象
public float speed = 200f;//旋转速度
public float distance;//旋转半径
Vector3 dir;
void Start()
{
dir = transform.position - targetPos.position;
}
void Update() {
//更新跟随物体的位置
transform.position = targetPos.position + dir.normalized * distance;
//围绕角色旋转
transform.RotateAround(targetPos.position, Vector3.up, speed * Time.deltaTime);
//更新方向向量
dir = transform.position - targetPos.position;
}
}
参考RotateAround函数的源码,可以看出里面对四元数的使用。
public void RotateAround(Vector3 point, Vector3 axis, float angle)
{
Vector3 vector = position;
Quaternion quaternion = Quaternion.AngleAxis(angle, axis);
Vector3 vector2 = vector - point;
vector2 = quaternion * vector2;
vector = (position = point + vector2);
RotateAroundInternal(axis, angle * ((float)Math.PI / 180f));
}