Unity3D - 詳解Quaternion類(二)


OK,不做引子了,接上篇Unity3D - 詳解Quaternion類(一)走起!

四、Quaternion類靜態方法

Quaternion中的靜態方法有9個即:Angle方法、Dot方法、Euler方法、FromToRotation方法、Inverse方法、Lerp方法、LookRotation方法、RotateToWards方法和Slerp方法。關於靜態的方法的使用就是直接用類名調用其靜態方法,例如Quaternion.Angle(q1,q2);下面對這些靜態方法做下分析。

1、Angle方法

1.1 函數原型

public static float Angle(Quaternion a,Quaternion b);

該方法可以計算兩個旋轉狀態a達到b時需要旋轉的最小夾角。

1.2 實例演示

using UnityEngine;
using System.Collections;

public class Angle_ts : MonoBehaviour {
   

    // Use this for initialization
    void Start () {

        Quaternion q1 = Quaternion.identity;
        Quaternion q2 = Quaternion.identity;
        q1.eulerAngles = new Vector3(30.0f, 40.0f, 50.0f);
        float a1 = Quaternion.Angle(q1, q2);
        float a2 = 0.0f;
        Vector3 v = Vector3.zero;
        q1.ToAngleAxis(out a2,out v);

        Debug.Log("a1: " + a1);
        Debug.Log("a2: " + a2);
        Debug.Log("q1的歐拉角: " + q1.eulerAngles + " q1的rotation: " + q1);
        Debug.Log("q2的歐拉角: " + q2.eulerAngles + " q2的rotation: " + q2);
    }
    
    // Update is called once per frame
    void Update () {
    
    }
}

運行結果

從輸出結果可以看出a1和a2的值相等,即Angle的返回值是兩個Quaternion實例轉換的最小夾角。

2、Dot方法-點乘

2.1 函數原型

public static float Dot(Quaternion a,Quaternion b);

該方法可以根據點乘的結果,判斷a和b對應歐拉角的關系。

例如有兩個Quaternion實例q1(x1,y1,z1,w1)和q2(x2,y2,z2,w2),則float f = Quaternion.Dot(q1,q2);即f = x1*x2+y1*y2+z1*z2+w1*w2,結果值f的范圍為[-1,1]。

當f=+(-)1時,q1和q2對應的歐拉角是相等的,即旋轉狀態是一致的。特別地,當f = -1時,說明其中一個rotation比另外一個rotation多旋轉了360°。

2.2 實例演示

using UnityEngine;
using System.Collections;

public class Dot_ts : MonoBehaviour {
   
    public Transform A, B;
    Quaternion q1 = Quaternion.identity;
    Quaternion q2 = Quaternion.identity;
    // Use this for initialization
    void Start () {

        A.eulerAngles = new Vector3(0.0f, 40.0f, 0.0f);
        B.eulerAngles = new Vector3(0.0f, 360.0f + 40.0f, 0.0f);
        q1 = A.rotation;
        q2 = B.rotation;
        float f = Quaternion.Dot(q1, q2);
        
        Debug.Log("q1的歐拉角: " + q1.eulerAngles + " q1的rotation: " + q1);
        Debug.Log("q2的歐拉角: " + q2.eulerAngles + " q2的rotation: " + q2);
        Debug.Log("Dot(q1,q2): " + f);
    }
    
    // Update is called once per frame
    void Update () {
    
    }
}

運行輸出

從輸出結果可以證明q1和q2的歐拉角相等,但是rotation值卻是相反的,也說明當Dot的返回值為-1時,兩個參數的角度相差360°。

3、Euler方法

3.1 函數原型

public static Quaternion Euler(Vector3 euler);
public static Quaternion Euler(float x,float y,float z);

該方法用於返回歐拉角Vector3(ex,ey,ez)對應的四元數Quaternion q(qx,qy,qz,qw)。其對應關系如下:

已知PIover180 = 3.141592/180 = 0.0174532925f是計算機圖形學中的一個常亮,其變換過程如下:

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);

3.2 驗證變換過程

using UnityEngine;
using System.Collections;

public class Euler_ts : MonoBehaviour {

    public float ex, ey, ez;
    float qx, qy, qz,qw;
    float PIover180 = 0.0174532925f;
    Quaternion q = Quaternion.identity;
    Vector3 euler;
  
    void OnGUI()
    {
        if(GUI.Button(new Rect(10.0f,10.0f,100.0f,45.0f),"計算"))
        {
            euler = new Vector3(ex,ey,ez);
            Debug.Log("歐拉角Euler(ex,ey,ez): " + euler);
            q = Quaternion.Euler(ex, ey, ez);
            Debug.Log("對應的四元數為: " + q);
            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);
        }
    }
}

運行結果

從輸出結果可以證明該公式是正確,另外轉換后的四元數直接輸出的話,如下:

 q = Quaternion.Euler(ex, ey, ez);
 Debug.Log("對應的四元數為: " + q);

輸出值是做了四舍五入處理的。

4、FromToRotation方法

函數原型

public static Quaternion FromToRotation(Vector3 fromDirection,Vector3 ToDirection);

在前面介紹了SetFromToRotation實例方法,它們的功能都是一樣的只不過用法稍有不同。使用FromToRotation類靜態方法,需要直接使用類名進行調用,如Quaternion.FromToRotation(v1,v2);

在此就不做演示了!

5、Inverse方法

5.1 函數原型

public static Quaternion Inverse(Quaternion rotation);

該方法可以返回參數rotation的逆向Quaternion值。

例如rotation(x,y,z,w),那么Quaternion.Inverse(rotation) = (-x,-y,-z,w)。假設rotation的歐拉角為(a,b,c),則transform.rotation = Quaternion.Inverse(rotation)相當於transform依次繞自身坐標系的z軸、x軸和y軸分別旋轉-c°、-a°和-z°。由於是在局部坐標系內的變換,最后transform的歐拉角的各個分量值並不一定等於-a、-b或-c。

5.2 實例演示

using UnityEngine;
using System.Collections;

public class Invers_ts : MonoBehaviour {
    public Transform A, B;

    // Use this for initialization
    void Start () {

        Quaternion q1 = Quaternion.identity;
        Quaternion q2 = Quaternion.identity;
        q1.eulerAngles = new Vector3(30.0f,40.0f,50.0f);
        q2 = Quaternion.Inverse(q1);
        A.rotation = q1;
        B.rotation = q2;

        Debug.Log("q1的歐拉角: " + q1.eulerAngles + "q1的rotation: " + q1);
        Debug.Log("q2的歐拉角: " + q2.eulerAngles + "q2的rotation: " + q2);
    }
    
    // Update is called once per frame
    void Update () {
    
    }
}

運行截圖

6、Lerp和Slerp方法

6.1 函數原型

public static Quaternion Lerp(Quaternion form, Quaternion to,float t);
public static Quaternion Slerp(Quaternion form, Quaternion to,float t);

兩種方法的作用都是返回從form到to的插值。當參數t<=0時返回值為from,當參數t>=1時返回值為to。其中Lerp是線性差值,而Slerp是球面插值。

6.2 實例演示

using UnityEngine;
using System.Collections;

public class LerpAndSlerp_ts : MonoBehaviour
{
    public Transform A, B, C,D;
    float speed = 0.2f;
    float total = 0.0f;
    // Use this for initialization
    void Start () {
    
    }
    
    // Update is called once per frame
    void Update () {
        total += Time.deltaTime * speed;
        if(total >= 1.0f)
            total = 1.0f;
        C.rotation = Quaternion.Lerp(A.rotation, B.rotation, total);
        D.rotation = Quaternion.Lerp(A.rotation, B.rotation, total);
        //C.rotation = Quaternion.Lerp(A.rotation, B.rotation, Time.deltaTime * speed);
        //D.rotation = Quaternion.Lerp(A.rotation, B.rotation, Time.deltaTime * speed);
    }
}

7、RotateTowards方法

7.1 函數原型

public static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta);

該方法也是一個插值方法,即從返回參數from到to的插值,且返回值的最大角度不超過maxDegreesDelta。maxDegreesDelta是角度值,不是插值系數,當maxDegreesDelta < 0時,將進行逆向插值即從to到from的方向進行插值計算。

7.2 實例演示

using UnityEngine;
using System.Collections;

public class RotateToWards_ts : MonoBehaviour {

    public Transform A, B, C;
    float speed = 10.0f;
    float total = 0.0f;
    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        total += Time.deltaTime * speed;
        if (total >= 1.0f)
            total = 1.0f;
        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));

    }
}

運行截圖

8、LookRotation方法

函數原型

public static Quaternion LookRotation(Vector3 forward);
public static Quaternion LookRotation(Vector3 forward,Vector3 upwards);

參數forward為返回Quaternion實例的forward朝向。該方法和前面講到的SetLookRotation實例方法的功能是一樣的,故不多做闡述了。

五、Quaternion類運算符

Quaternion類涉及到兩個Quaternion對象相乘和Quaternion對象與Vector3對象相乘,那么就必須重載"*"運算符。

1、函數原型

public static Quaternion operator *(Quaternion lhs, Quaternion rhs);
public static Vector3 operator *(Quaternion rotation, Vector3 point);

2、兩個Quaternion對象相乘

對於兩個Quaternion對象相乘主要用於自身旋轉變換,例如:

B.rotation *= A.rotation;
  • B會繞着B的局部坐標系的z、x、y軸按照先繞z軸再繞x軸最后繞y軸的旋轉次序,分別旋轉A.eulerAngles.z度、A.eulerAngles.x度、和A.eulerAngles.y度。由於是繞着局部坐標系進行旋轉,所以當繞着其中一個軸進行旋轉時,可能會影響其余兩個坐標軸方向的歐拉角(除非其余兩軸的歐拉角都為0才不會受到影響)。
  • 假如A的歐拉角為aEuler(ax,ay,az),則沿着B的初始局部坐標系的aEuler方向向下看B在繞着aEuler順時針旋轉。B的旋轉狀況還受到其初始狀態的影響。

2.1 實例演示

using UnityEngine;
using System.Collections;

public class Multiply1_ts : MonoBehaviour {

    public Transform A, B;

    // Use this for initialization
    void Start () {

        A.eulerAngles = new Vector3(1.0f, 1.5f, 2.0f);
    }
    
    // Update is called once per frame
    void Update () {

        B.rotation *= A.rotation;
        Debug.Log(B.eulerAngles);
    }
}

運行截圖

2.2 分析

B繞着其自身坐標系的Vector3(1.0f,1.5f,2.0f)方向旋轉。雖然每次都繞着這個軸向旋轉的角度相同,但角度的旋轉在3個坐標軸的值都不為零,三個軸的旋轉會相互影響,所以B的歐拉角的各個分量的每次遞增是不固定的。

3、Quaternion對象與Vector3對象

對於Quaternion對象與Vector3對象相乘主要用於自身移動變換,例如

transform.position += tansform.rotation * A;

其中A為Vector3的對象。transform對應的對象會沿着自身坐標系中向量A的方向移動A的模長的距離。transform.rotation與A相乘可以確定移動的方向和距離。

3.1 實例演示

using UnityEngine;
using System.Collections;

public class Multiply2_ts : MonoBehaviour {
    public Transform A;
    float speed = 0.1f;

    // Use this for initialization
    void Start () {

        A.position = Vector3.zero;
        A.eulerAngles = new Vector3(0.0f, 45.0f, 0.0f);
    }
    
    // Update is called once per frame
    void Update () {

        A.position += A.rotation * (Vector3.forward * speed);
        Debug.Log(A.position);
    }
}

運行截圖

4、兩個Quaternion對象相乘與Quaternion對象與Vector3對象相乘的異同

  • 設A為兩個兩個Quaternion對象相乘的結果。B為Quaternion對象與Vector3對象相乘的結果。其中A為Quaternion類型,B為Vector3類型。
  • A與B的相同之處是它們都通過自身坐標系的“相乘”方式來實現在世界坐標系中的變換。
  • A主要用來實現transform繞自身坐標系的某個軸旋轉,B主要用來實現transform沿着自身坐標系的某個方向移動。
  • 必須遵守Quaternion對象*Vector3對象的形式,不可以顛倒順序。
  • 由於它們都是相對於自身坐標系進行的旋轉或移動,所以當自身坐標系的軸向和世界坐標系的軸向不一致時,它們繞着自身坐標系中的某個軸向的變動都會影響到物體在世界坐標系中各個坐標軸的變動。

--------------------------------------------------------------------------------------------------------至此完結!-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM