【转载】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);
    }
}
相关文章
相关标签/搜索