Unity中表示旋轉有三種方式:四元數,歐拉角,矩陣
一、四元數
1、概念
Quaternion中存放了x,y,z,w四個數據成員,可以用下標來進行訪問,對應的下標分別是0,1,2,3
其實最簡單來說:四元數就是表示一個3D物體的旋轉,它是一種全新數學數字,甚至不是復數。
四元數其實就是表示旋轉。
1、四元數的書寫格式
q = w v 這里w是標量,v是向量
另一種表示格式
q = w(x,y,z) 看到這里我們就熟悉了。
一般我們在Unity中表示旋轉的方式:
c.Rotate (new Vector3(10,0,0));
c.Rotate (Quaternion.identity * Vector3.left);
2、Unity中四元數API
- 1.來把角度轉化為相應四元素
Quaternion rot=Quaternion.Euler(30,60,90);
旋轉順序是,先繞Z軸轉動90度,再繞X軸轉動30度,最后繞Y軸轉動60度
- 2.繞指定軸旋轉指定角度相應的四元素
Quaternion rot= Quaternion.AngleAxis(60, transform.forward);
意思為繞自身正方向轉60度其相應的四元素,當然了,第一個參數可以是其他方向變量
- 3.兩個四元素相乘,代表按順序進行兩次旋轉
Quaternion rot1=Quaternion.Euler(0,30,0);
Quaternion rot2=Quaternion.Euler(0,0,45);
Quaternion rot=rot2*rot1;
意思是先繞y旋轉30度再繞z旋轉45度,所以,你要先繞哪個四元素先旋轉就放最右邊
- 4.四元素與向量相乘,得到的是將原向量旋轉后的新向量
Vector3 to = Quaternion.AngleAxis(45, Vector3.up)* Vector3.forward
to是將Vector3.forward繞Vector3.up旋轉45度后得到的新向量
- 5.已知兩個向量,求從一個向量轉到另一個向量的四元數:
Quaternion rot=Quaternion.FromToRotation(Vector3.up,Vector3.forward);
這的意思是創建一個從y軸正方向到z軸正方向的旋轉角度所對應的四元數
例子:
Vector3 aimDirection=(targetTrans.position – transform.position).normalized;
Quaternion rot = Quaternion.FromToRotation(transform.up,aimDirection);
transform.rotation = rot * transform.rotation;
此游戲對象自身轉向這個計算出來的偏轉角所對應的四元數,如果將此至于Update中就可以做到不斷對準目標
- 6.創建一個讓Z軸正方向和Y軸正方向指向規定方向的旋轉
Quaternion rot = Quaternion.LookRotation(Vector3.right, Vector3.down);
這的意思是創建一個讓Z軸正方向指向世界坐標x軸正向,y軸正方向豎直向下的旋轉角度所對應的四元數
Vector3 vr = new Vector3(1,0,0);
Quaternion q = Quaternion.LookRotation(vr);
void SetLookRotation(Vector3 view);
void SetLookRotation(Vector3 view,Vector3 up);
也是根據指定的向前和向上向量創建四元數,本質計算過程和LookRotation一樣,
只不過LookRotation是Quaternion上的靜態函數,而SetLookRotation則是Quaternion的成員函數。
- 7.已知兩個由四元數代表的旋轉角度,求出從一個角度漸變到另一個角度的增量
Quaternion rot=Quaternion.Euler(0,30,0)
Quaternion targetRot= rot*transform.rotation;
transform.rotation= Quaternion.RotateTowards(transform.rotation,targetRot,90*Time.deltaTime);
意思為游戲對象的角度會逐漸向targetRot以90*Time.deltaTime的角速度(90度每秒)逐漸逼近,因為這代碼并不能夠在一幀中完成所以要放于update中
- 8.兩個四元數之間的線性插值lerp
transform.rotation=Quaternion.Lerp(transform.rotation,Vector3.up,0.5f);
第一個參數是起始的角度所對應的四元數
第一個參數是終點的角度所對應的四元數
第三個參數是這個過程需要多少秒
- 9.兩個四元數之間的球形插值Slerp
transform.rotation=Quaternion.Slerp(transform.rotation,Vector3.up,0.5f);
第一個參數是起始的角度所對應的四元數
第一個參數是終點的角度所對應的四元數
第三個參數是這個過程需要多少秒
- 10.旋轉軸和旋轉角度算出四元數
Quaternion RotateTowards(Quaternion from,Quaternion to, float maxDegreesDelta);
以maxDegreesDelta作為角度步長計算從from到[to]
根據旋轉軸和旋轉角度算出四元數
- 11.四元數對應的三個軸向的歐拉角
Quaternion.eulerAngles
存放四元數對應的三個軸向的歐拉角,分別是繞x軸、y軸、z軸旋轉的角度
Quaternion q3 = new Quaternion();
q3.eulerAngles = new Vector3(10, 30, 20);
Quaternion qx3 = Quaternion.AngleAxis(10,Vector3.right);
Quaternion qy3 = Quaternion.AngleAxis(30,Vector3.up);
Quaternion qz3 = Quaternion.AngleAxis(20,Vector3.forward);
Quaternion qxyz3 = qz3*qy3*qx3;
從這里可以看出unity中旋轉順序也是按先繞x軸旋轉,然后y,最后z。unity中對向量應用旋轉量使用的是向量右乘,即如下:
Vector3 newV = qxyz3*v=qz3*qy3*qx3*v;
四元數實例
- eulerAngles
public class EulerAngle: MonoBehaviour
{
// 兩個物體A,B
public Transform A, B;
// 默認角度
Quaternion rotations=Quaternion.identity;
Vector3 eulerAngle = Vector3.zero;
float speed = 10.0f;
void Update()
{
//第一種方式:將Quaternion賦值給transform的rotation
rotations.eulerAngles = new Vector3(0.0f, speed * Time.time, 0.0f);
A.rotation = rotations;
//第二種方式:將三維向量代表的歐拉角直接賦值給transform的eulerAngles
eulerAngle = new Vector3(0.0f, speed * Time.time, 0.0f);
B.eulerAngles = eulerAngle;
}
}
- SetFormToRotation
注意點:不能直接obj.rotation.SetFromToRotation(A.position,B.position);因為需要實例化才能賦值給transform.rotation.
public class SetFromToRotation : MonoBehaviour {
public Transform A, B, C;
Quaternion q1 = Quaternion.identity;
void Update () {
//不可直接使用C.rotation.SetFromToRotation(A.position,B.position);
q1.SetFromToRotation(A.position,B.position);
C.rotation = q1;
Debug.DrawLine(Vector3.zero,A.position,Color.red);
Debug.DrawLine(Vector3.zero, B.position, Color.green);
Debug.DrawLine(C.position, C.position+new Vector3(0.0f,1.0f,0.0f), Color.black);
Debug.DrawLine(C.position, C.TransformPoint(Vector3.up*1.5f), Color.yellow);
}
}
- SetLookRotation : 設置Quaternion實例的朝向。此方法的功能是對一個Quaternion的實例的朝向進行設置。
public class LookRotation_ts : MonoBehaviour
{
public Transform A, B, C, D;
Quaternion q1 = Quaternion.identity;
void Update()
{
//使用實例方法
//不可直接使用C.rotation.SetLookRotation(A.position,B.position);
q1.SetLookRotation(A.position, B.position);
C.rotation = q1;
//使用類方法
D.rotation = Quaternion.LookRotation(A.position, B.position);
//繪制直線,請在Scene視圖中查看
Debug.DrawLine(Vector3.zero, A.position, Color.white);
Debug.DrawLine(Vector3.zero, B.position, Color.white);
Debug.DrawLine(C.position, C.TransformPoint(Vector3.up * 2.5f), Color.white);
Debug.DrawLine(C.position, C.TransformPoint(Vector3.forward * 2.5f), Color.white);
Debug.DrawLine(D.position, D.TransformPoint(Vector3.up * 2.5f), Color.white);
Debug.DrawLine(D.position, D.TransformPoint(Vector3.forward * 2.5f), Color.white);
}
}
- ToAngleAxis :Quaternion實例的角軸表示。其中angle為旋轉角,axis為軸向量。
public class ToAngleAxis : MonoBehaviour
{
public Transform A, B;
float angle;
Vector3 axis = Vector3.zero;
void Update()
{
//使用ToAngleAxis獲取A的Rotation的旋轉軸和角度
A.rotation.ToAngleAxis(out angle, out axis);
//使用AngleAxis設置B的rotation,使得B的rotation狀態的和A相同
//可以在程序運行時修改A的rotation查看B的狀態
B.rotation = Quaternion.AngleAxis(angle, axis);
}
}
- Angle方法: Quaternion實例間夾角
注意:返回的是GameObject對象從后面狀態(a)轉換到(b)時需要旋轉的最小夾角。
public class Angle : MonoBehaviour {
void Start()
{
Quaternion q1 = Quaternion.identity;
Quaternion q2 = Quaternion.identity;
q1.eulerAngles = new Vector3(10.0f, 20.0f, 30.0f);
float f1 = Quaternion.Angle(q1,q2);
float f2 = 0.0f;
Vector3 v1 = Vector3.zero;
q1.ToAngleAxis(out f2, out v1);
Debug.Log("f1:" + f1);
Debug.Log("f2:" + f2);
Debug.Log("q1的歐拉角:" + q1.eulerAngles + " q1的rotation:" + q1);
Debug.Log("q2的歐拉角:" + q2.eulerAngles + " q2的rotation:" + q2);
}
}
- Dot方法:點乘
用來求參數a和b的點乘。q1(x1,y1,z1,w1),q2(x2,y2,z2,w2)為兩個實例則float f = Quaternion.Dot(q1,q2); 等價于f = x1x2 + y1y2 + z1z2 + w1w2.
當f = ±1 時,q1和q2對應的歐拉角是相等的,在Game視圖中看他們旋轉狀態是一樣的。
當f=1時,它們的rotation相等;
當f=-1時,說明其中一個rotation比另一個多旋轉了360°
public class Dot : MonoBehaviour {
public Transform A, B;
Quaternion q1=Quaternion.identity;
Quaternion q2=Quaternion.identity;
float f;
void Start () {
A.eulerAngles = new Vector3(0.0f,40.0f,0.0f);
//B比A繞Y軸多轉360度
B.eulerAngles = new Vector3(0.0f, 360.0f+40.0f, 0.0f);
q1 = A.rotation;
q2 = B.rotation;
f = Quaternion.Dot(q1,q2);
Debug.Log("q1的rotation:"+q1);
Debug.Log("q2的rotation:" + q2);
Debug.Log("q1的歐拉角:" + q1.eulerAngles);
Debug.Log("q2的歐拉角:" + q2.eulerAngles);
Debug.Log("Dot(q1,q2):"+f);
}
}
-
Euler方法:歐拉角對應的四元數
Euler
public class Euler : MonoBehaviour
{
//記錄歐拉角,單位為角度,可以在Inspector面板中設置
public float ex, ey, ez;
//用于記錄計算結果
float qx, qy, qz, qw;
float PIover180 = 0.0174532925f;//常量
Quaternion Q = Quaternion.identity;
void OnGUI()
{
if (GUI.Button(new Rect(10.0f, 10.0f, 100.0f, 45.0f), "計算"))
{
Debug.Log("歐拉角:" + " ex:" + ex + " ey:" + ey + " ez:" + ez);
//調用方法計算
Q = Quaternion.Euler(ex, ey, ez);
Debug.Log("Q.x:" + Q.x + " Q.y:" + Q.y + " Q.z:" + Q.z + " Q.w:" + Q.w);
//測試算法
ex = ex * PIover180 / 2.0f;
ey = ey * PIover180 / 2.0f;
ez = ez * PIover180 / 2.0f;
qx = Mathf.Sin(ex) * Mathf.Cos(ey) * Mathf.Cos(ez) + Mathf.Cos(ex) * Mathf.Sin(ey) * Mathf.Sin(ez);
qy = Mathf.Cos(ex) * Mathf.Sin(ey) * Mathf.Cos(ez) - Mathf.Sin(ex) * Mathf.Cos(ey) * Mathf.Sin(ez);
qz = Mathf.Cos(ex) * Mathf.Cos(ey) * Mathf.Sin(ez) - Mathf.Sin(ex) * Mathf.Sin(ey) * Mathf.Cos(ez);
qw = Mathf.Cos(ex) * Mathf.Cos(ey) * Mathf.Cos(ez) + Mathf.Sin(ex) * Mathf.Sin(ey) * Mathf.Sin(ez);
Debug.Log(" qx:" + qx + " qy:" + qy + " qz:" + qz + " qw:" + qw);
}
}
}
-FromToRotation方法:Quaternion變換
public class FromToRotation : MonoBehaviour
{
public Transform A, B, C, D;
Quaternion q1 = Quaternion.identity;
void Update()
{
//使用實例方法
//不可直接使用C.rotation.SetFromToRotation(A.position,B.position);
q1.SetFromToRotation(A.position, B.position);
C.rotation = q1;
//使用類方法
D.rotation = Quaternion.FromToRotation(A.position, B.position);
//在Scene視圖中繪制直線
Debug.DrawLine(Vector3.zero, A.position, Color.white);
Debug.DrawLine(Vector3.zero, B.position, Color.white);
Debug.DrawLine(C.position, C.position + new Vector3(0.0f, 1.0f, 0.0f), Color.white);
Debug.DrawLine(C.position, C.TransformPoint(Vector3.up * 1.5f), Color.white);
Debug.DrawLine(D.position, D.position + new Vector3(0.0f, 1.0f, 0.0f), Color.white);
Debug.DrawLine(D.position, D.TransformPoint(Vector3.up * 1.5f), Color.white);
}
}
- Lerp方法:線性插值
- Slerp方法:球面插值
當參數t≤0時返回值為from,當參數t≥1時返回值為to。一般情況下兩種方法使用lerp代替
public class Slerp : MonoBehaviour
{
public Transform A, B, C, D;
float speed = 0.2f;
//分別演示方法Slerp和Lerp的使用
void Update()
{
C.rotation = Quaternion.Slerp(A.rotation, B.rotation, Time.time * speed);
D.rotation = Quaternion.Lerp(A.rotation, B.rotation, Time.time * speed);
}
}
- RotateTowards 方法 : Quaternion插值
from為起始Quaternion,參數to為結束Quaternion,參數maxDegreesDelta為每幀最大角度值。方法用于返回從參數from到to的插值,且返回值的最大角度不超過maxDegreesDelta.當maxDegreesDelta < 0 ,將沿著to到from的方向插值計算。
public class RotateTowards : MonoBehaviour {
public Transform A, B, C;
float speed = 10.0f;
void Update()
{
C.rotation = Quaternion.RotateTowards(A.rotation, B.rotation, Time.time * speed-40.0f);
Debug.Log("C與A的歐拉角的差值:" + (C.eulerAngles-A.eulerAngles) + " maxDegreesDelta:" + (Time.time * speed - 40.0f));
}
}
二、歐拉角
1、概念
1-描述定點轉動剛體的位形需要三個獨立坐標變量,即x,y,z
2-描述定軸轉動的剛體的位形只需要一個獨立坐標變量即轉角。
3-將定點轉動的過程分解為三個相互獨立的定軸轉動,相應的二三個相互獨立的轉角,即歐拉角。
2、什么是歐拉角?
Roll 滾動角 Z軸不動
Pitch 俯仰角 X軸不動
Heading(Yaw) 偏航角 : 就是Y軸不動
那么使用歐拉角表示旋轉矩陣
繞Z軸旋轉
繞Y軸旋轉
繞X軸旋轉
所以說:
3、局限性
歐拉角會產生萬向鎖bug
一旦選擇pitch角為±90°,就被限制在只能繞垂直軸旋轉
三、矩陣
1、概念
矩陣通常用在一些特殊的地方,例如攝像機的非標準投影。unity中僅僅提供了一個4×4的矩陣類Matrix4x4,它可以包含位移T、旋轉R和伸縮信息。矩陣中的元素都對應一個mxy的公有成員變量,因而要訪問單個元素的話可以直接訪問其成員。同時也提供了下標訪問,如下
public float this [int row, int column]
{
get
{
return this [row + column * 4];
}
set
{
this [row + column * 4] = value;
}
}
public float this [int index]
-
MultiplyPoint方法:投影矩陣變換
MultiplyPoint
-
MultiplyVector方法:矩陣變換
矩陣變換
- SetTRS方法:重設Matrix4x4變換矩陣
使用Matrix4x4的成員函數SetTRS
void SetTRS(Vector3 pos,Quaternion q,Vector3 s);
Quaternion q = Quaternion.LookRotation(new Vector3(0,0.5,1));
Matrix4x4 rot = new Matrix4x4();
rot.SetTRS(new Vector3(0,0,0),q,new Vector3(1,1,1));
-
Ortho方法:創建正交投影矩陣
Ortho方法
-
perspective方法:創建投影矩陣
image.png -
TRS方法:返回Matrix4x4實例
旋轉矩陣到四元數
使用Quaternion類的LookRotation函數
static Quaternion LookRotation(Vector3 forward,Vector3 upwards);
Matrix4x4 rot = new Matrix4x4();
rot.SetTRS(new Vector3(0,0,0),q,new Vector3(1,1,1));
Vector4 vy = rot.GetColumn(1);
Vector4 vz = rot.GetColumn(2);
Quaternion q = Quaternion.LookRotation(new Vector3(vz.x,vz.y,vz.z),new Vector3(vy.x,vy.y,vy.z));