【轉載】Unity中矩陣的平移、旋轉、縮放

年代久遠,圖片連接失效了,這裏從新放一個騰訊學院的版本,裏面有圖片數組

http://gad.qq.com/article/detail/33543ide

 

By:克森函數

 

簡介

在這篇文章中,咱們將會學到幾個概念:平移矩陣、旋轉矩陣、縮放矩陣。在學這幾個基本概念的同時,咱們會用到 Mesh(網格)、數學運算、4x4矩陣的一些簡單的操做。但因爲克森也是新手,文章的嚴謹性可能不是很高,還請大神們多多指教。工具

 

建立項目

首先建立一個Unity工程,克森把他命名爲「Matrix of China」(中國的矩陣),基本配置以下圖所示:學習

 

 

爲了便於查找,讓咱們在 Assets 目錄下新建三個文件夾,分別命名爲「Scripts」、「Shader」、「Materials」,這個不用解釋,大夥們都看得懂吧。以下圖所示:測試

 

 

接下來再 Scripts 文件夾裏建立一個 C# 腳本,命名爲「Triangle」,該腳本用於建立一個簡單的三角形。而後在 Hierarchy 面板下建立一個空物體,命名爲「Triangle」。而後爲該物體添加以前建立的「Triangle」腳本,且爲該物體添加 Mesh 相關的兩個組件,以下圖所示:ui

 

 

好,接下來讓咱們開始碼了個碼。this

 

Triangle.cs spa

首先讓咱們看一看完整的代碼,而後再一步一步的分析:code

 

 

代碼解析

相信這段代碼對於大夥來講都不是很難吧。建立一個網格的步驟通常都是按這個順便來建立的:

    1.爲頂點數組賦值

    2.爲三角形數組賦值

在代碼中的「vertices」就是所謂的頂點數組、」triangles「就是所謂的三角形數組,它的做用其實就是對應頂點的索引。通常一個三角形是由三個頂點組成的。

 

好,讓咱們回到 Inspector 面板修改一下「Triangle」腳本的屬性,以下圖所示:

 

 

PS:三角形數組索引必須從0開始,按順序而後自增 1。且再次強調,三個頂點才能構成一個三角形。

 

好,如今讓咱們點擊 Play 進行測試一下:

 

 

Okey,如今咱們的三角形算是完成了,接下來開始玩弄它了。

 

腳本中的變換

首先建立一個 C# 腳本,命名爲「MyTransform」,併爲咱們的「Triangle」物體添加此腳本。代碼以下:

 

 

代碼解析

這段代碼很簡單,就是聲明一個 4x4 的矩陣,而後調用該 4x4 矩陣的 SetTRS 函數(T 表明 Translate(平移)、R 表明 Rotation(旋轉)、S 表明 Scale(縮放))。該函數用來設置一個平移、旋轉、縮放矩陣。在代碼中,咱們傳入的是該物體的 position、rotation、localScale,這樣作是爲了便於觀察相應的變換矩陣。

 

1平移矩陣

這個就是平移矩陣,其中 (Tx,Ty,Tz) 爲平移的方向向量,有些書上是把 Tx、Ty、Tz 放在第四行,當通過克森的測試,Unity的平移矩陣式這樣子的。下面咱們就作一個簡單的測試:

    1.修改「Triangle」的 Position 爲(1,2,3)

 

    2.點擊 Play 按鈕,觀察一下 matrix 屬性的變化:

 

 

    圖中畫紅線的「E03」、「E13」、「E23」正好就是對應上面平移矩陣圖中的「Tx」、「Ty」、「Tz」,這說明Unity使用的正是這種方式的平移矩陣。

 

接下來,咱們利用平移矩陣作個簡單的平移,讓咱們回到「MyTransform」腳本中添加一些代碼:

 

代碼解析

 以前在 Start 函數裏的代碼註釋掉,由於文章的後面還要用到。在代碼中,新添加了一個 Vector4 類型的變量,這是由於 4x4 的矩陣不能與三維向量相乘。

 

以後再 Start 函數中初始化了向量和矩陣,變量「v」存放的是當前物體的位置,而當前的矩陣爲單位矩陣(對於矩陣的乘法和單位矩陣我就不想講了,請大夥自行百度學習)。

 

而後找到矩陣中對應的平移的方向「Tx」、「Ty」、「Tz」,在代碼中,我讓物體向 X 軸方向平移3個單位、Y 軸方向平移4個單位、Z 軸方向平移5個單位。

 

 以後執行矩陣變換的操做,矩陣的操做以下圖所示:

最後將計算的結果傳給物體的 Position。而後在 Start 函數裏調用一下該函數:

 

PS:記得將「Triangle」物體的 Position 設置爲 (0,0,0)。如今讓咱們點擊 Play 查看一下結果對不對:

 

 

好了,值是正確的。你們也能夠修改一下參數玩玩。至此,平移矩陣大致講完了,仍是有點懵的夥計能夠給克森說說。

 

縮放矩陣

這個就是縮放矩陣,其中「Sx」、「Sy」、「Sz」就是各個軸上的縮放因子。縮放矩陣是矩陣表現物體大小變換的矩陣。若是縮放因子小於1,表現爲物體縮小;若是大於1,則表現爲物體擴大,若是等於1則不發生變化。

 

接下來咱們作個簡單的測試,把取消以前 Start 函數裏的代碼,而後修改「Triangle」物體的 Scale 屬性爲(1,2,3),點擊 Play 查看一下結果:

 

 

找到對應的各個軸的縮放因子,目前來講結果是正確的了。

 

接下來,咱們利用縮放矩陣作個簡單的操做,讓咱們回到「MyTransform」腳本中添加一些代碼:

 

 

代碼解析

這段代碼和以前平移的代碼差不到哪裏去。代碼裏也給了註釋。主要就是矩陣的縮放操做不同,下面弄張圖就搞定了,縮放操做相對來講仍是蠻簡單的。

所以就有:

最後修改一下 Start 函數的代碼便可:

如今點擊 Play 按鈕查看一下結果是否正確:

 

 

Okey,看來結果是正確的。

 

PS:當各個軸上的縮放因子相等時,即:Sx=Sy=Sz 時,則爲均勻縮放。

 

旋轉矩陣

上圖就是所謂的旋轉矩陣。在咱們的實踐中,我就使用沿 x- 軸進行旋轉作實踐便可。

 

首先仍是作個簡單的測試,讓咱們修改一下腳本,而後將物體的 Rotation參數設置爲 (45,0,0),最後點擊 Play 按鈕查看結果,以下圖所示:

 

在這裏我選擇了45°角,由於 sin45°和cos45° 的值是相等的(雖然在上圖中他們的值不相等,但其實是相等的,大夥們能夠用個 if 語句判斷一下),讓咱們看看結果對不對:

 

Okey,看來是正確的。

 

接下來,咱們利用旋轉矩陣作個簡單的操做,讓咱們回到「MyTransform」腳本中添加一些代碼:

 

因爲圖片是拼接的,全部有點彆扭

代碼解析

首先聲明瞭一個 float 類型的變量「angle」用於輸入須要旋轉的角度,以後又聲明瞭一個 emun(枚舉) 類型的變量「Axle」,用於選擇旋轉的方式。

 

接下在「MyRotation」函數中初始化了矩陣。接下來的判斷語句就是對應各個軸上的旋轉(能夠與上面那張旋轉矩陣進行對比,相信你們都能懂吧),在判斷語句中主要用到了三個函數:Mathf.Sin()、Mathf.Cos()、Mathf.Deg2Rad()。前面兩個函數你們都知道是什麼了吧,後面那個函數用於弧度轉角度,由於前面兩個函數接受的是一個弧度制的值。

接下來的代碼就是作矩陣轉爲四元數的操做,具體請看下圖中的公式:

 

 

最後將計算好的值傳給物體的 Rotation 便可

All right. 如今讓咱們回到物體的 Inspector 面板中修改「Angle」參數 和 Axle 選項,而後點擊 Play 按鈕進行測試便可(在這裏克森選擇的是,繞 X 軸旋轉45°):

 

 

Perfect. 和咱們預期的效果同樣,大夥們能夠自行修改參數進行測試一番。

 

最後給你們送上一個小工具,只需傳入一個變換矩陣便可幫你完成平移、旋轉、縮放的工做,而後把值傳給物體的 Position、Rotation、Scale:

 

 

接下來用一個例子來演示這個小工具怎麼使用。

 

回到咱們的場景中,找到「Triangle」物體,在 Inspector 面板中修改「MyTransform」腳本的「Matrix」屬性,以下圖所示:

 

紅色箭頭表明平移、綠色箭頭表明縮放、黃色箭頭表明旋轉,這個變換矩陣表示的是:物體在X軸上平移3個單位、Y軸上平移4個單位、Z軸上平移5個單位(position (3,4,5));物體繞着X軸旋轉90° (Rotation(90,0,0));物體沒有縮放 (1,1,1) 。

 

而後回到「MyTransform」腳本的Start函數中調用工具類裏的方法完成變換矩陣的各項操做:

 

最後點擊 Play 按鈕查看結果:

 

 

Perfect. 看來和咱們預期的效果如出一轍,大夥們能夠自行修改參數進行測試。

 

好了,這篇文章就到這裏了。這部份內容也是克森最近剛剛學的,也算是學習筆記吧,若是有什麼錯誤的地方還請大神們指教指教。

 

 

 

附上代碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Tringle : MonoBehaviour {

    private Mesh mesh;
    public Vector3[] vertices;
    public int[] triangle;

    // Use this for initialization
    void Start () {
        mesh = new Mesh ();
        mesh.vertices = vertices;
        mesh.triangles = triangle;

        var meshRender = GetComponent<MeshRenderer> ();
        if (meshRender == null)
            meshRender = this.gameObject.AddComponent<MeshRenderer> ();
        
        var meshFilter = GetComponent<MeshFilter> ();
        if (meshFilter == null)
            meshFilter = this.gameObject.AddComponent<MeshFilter> ();

        meshFilter.mesh = mesh;
    }
    
    // Update is called once per frame
    void Update () {
    
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum Axle
{
    X,
    Y,
    Z,
}

public class MyTransform : MonoBehaviour {

    public Axle axle;

    public Vector4 v4;

    public Matrix4x4 matrix;

    // Use this for initialization
    void Start () {
        //matrix.SetTRS (
        //    transform.position,
        //    transform.rotation,
        //    transform.localScale
        //);


    }
    
    void MyTranslate (float x, float y, float z) {
        v4 = new Vector4 (
            transform.position.x,
            transform.position.y,
            transform.position.z,
            1
        );

        /* identity
         * 1 0 0 0
         * 0 1 0 0
         * 0 0 1 0
         * 0 0 0 1
         */
        matrix = Matrix4x4.identity;

        // pos
        matrix.m03 = x;
        matrix.m13 = y;
        matrix.m23 = z;

        /* pos transform
         * 1 0 0 x * pos.x      1 * pos.x + 0 * pos.x + 0 * pos.x + x * pos.x          x * pos.x
         * 0 1 0 y * pos.y  ==  ...                                             ==     y * pos.y
         * 0 0 1 z * pos.z      ...                                                    z * pos.z
         * 0 0 0 1 * 1          ...                                                    1
         */
        v4 = matrix * v4;

        transform.position = new Vector3 (v4.x, v4.y, v4.z);
    }

    void MyScale(float x, float y, float z)
    {
        v4 = new Vector4 (
            transform.localScale.x,
            transform.localScale.y,
            transform.localScale.z,
            1
        );

        /* identity
         * 1 0 0 0
         * 0 1 0 0
         * 0 0 1 0
         * 0 0 0 1
         */
        matrix = Matrix4x4.identity;

        matrix.m00 = x;
        matrix.m11 = y;
        matrix.m22 = z;

        v4 = matrix * v4;

        transform.localScale = new Vector3 (v4.x, v4.y, v4.z);
    }

    void MyRotation(Axle axle, float angle) 
    {
        matrix = Matrix4x4.identity;

        // set matrix
        if (axle == Axle.X) {
            matrix.m11 = Mathf.Cos (angle * Mathf.Deg2Rad);
            matrix.m22 = -Mathf.Sin (angle * Mathf.Deg2Rad);
            matrix.m21 = Mathf.Sin (angle * Mathf.Deg2Rad);
            matrix.m22 = Mathf.Cos (angle * Mathf.Deg2Rad);
        } else if (axle == Axle.Y) {
            matrix.m00 = Mathf.Cos (angle * Mathf.Deg2Rad);
            matrix.m02 = Mathf.Sin (angle * Mathf.Deg2Rad);
            matrix.m20 = -Mathf.Sin (angle * Mathf.Deg2Rad);
            matrix.m22 = Mathf.Cos (angle * Mathf.Deg2Rad);
        } else if (axle == Axle.Z) {
            matrix.m00 = Mathf.Cos (angle * Mathf.Deg2Rad);
            matrix.m01 = -Mathf.Sin (angle * Mathf.Deg2Rad);
            matrix.m10 = Mathf.Sin (angle * Mathf.Deg2Rad);
            matrix.m11 = Mathf.Cos (angle * Mathf.Deg2Rad);
        }

        // to quaternion
        float qw = Mathf.Sqrt(1f + matrix.m00 + matrix.m11, matrix.m22) / 2;
        float w = 4 * qw;
        float qx = (matrix.m21 - matrix.m12) / w;
        float qy = (matrix.m02 - matrix.m20) / w;
        float qz = (matrix.m10 - matrix.m101) / w;

        transform.rotation = new Quaternion (qx, qy, qz, qw);
    }
}
相關文章
相關標籤/搜索