Unity变换矩阵之如何构建变换矩阵

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_33953662/article/details/78851930

变换矩阵包括:平移、旋转、缩放。

平移矩阵:


旋转矩阵:

X轴旋转矩阵:



Y轴旋转矩阵:



Z轴旋转矩阵:


缩放矩阵:


变换矩阵,就是将一个向量依次进行 平移、旋转和缩放变换,

设平移矩阵为MatMove,旋转矩阵分别为 MatRotateX、MatRotateY、MatRotateZ,缩放矩阵为MatScale

在Unity中,处理旋转是按照 Z 、 X、Y轴顺序依次进行旋转,所以最终的旋转矩阵

 公式一:MatRotate =  MatRotateY x MatRotateX x MatRotateZ

矩阵相乘的顺序要从右往左读,关于旋转矩阵的相乘顺序,还有另一种

公式二:MatRotate =  MatRotateZ x MatRotateX x MatRotateY

两种顺序刚好相反,这是因为关于旋转有两种解读方案,

第一种是,世界坐标系下依次进行Z轴旋转、X轴、Y轴旋转。对应公式一。

第二种是,先在世界坐标系下进行Z轴旋转,同时将世界坐标系一起旋转,得到新的临时坐标系1,

接着在临时坐标系1下进行X轴旋转,同时将临时坐标系1一起旋转,得到新的临时坐标系2,

最后在在临时坐标系2下进行Y轴旋转,得到最后的旋转结果。对应公式二。

Unity中按照第一种顺序相乘。

最终的变换矩阵,就是:

公式三:MatTransfer = MatMove x MatRotate x MatScale

如何用两个已知的向量 反推 变换矩阵,并使用该变换矩阵,将模型所有的顶点变换掉呢?

下面举个例子:

 已知模型空间下的向量 A ,求其在世界空间下的向量 A‘

可得:

A' = 变换矩阵 * A

但是如何获得这个 变换矩阵呢?






如上图,我们想把Start胶囊体最终变换到End胶囊体,在两个胶囊体的Transform组件上获得构建变换矩阵的所有参数。

其中需要两个位置,用来构建平移矩阵。两个向量,用来构建旋转矩阵,分别选为两个胶囊体模型空间下的y轴方向。

还有一个向量,来记录模型沿X、Y、Z方向的缩放。

我们可以用Global和Local切换按钮看到模型空间下的坐标系


模型空间下的坐标系:


我们可以通过访问 transform.up属性直接获得该模型的y轴向量

将下列脚本挂在模型上,右键点击该脚本,会打印出所在模型点的模型空间y轴向量。

(该向量是归一化向量)

using UnityEngine;
using System.Collections;
using UnityEditor;

public class GetVector : MonoBehaviour {

    [ContextMenu("GetLocalUp")]
    public void GetLocalUp()
    {
         Debug.Log(name + "的模型空间y轴向量为: " + transform.up.ToString("0.00"));
    }
}



接下来开始构建各个变换矩阵

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

public class TransferMatrix : MonoBehaviour {

    [Header("起始位置")]
    public Vector3 StartPos;
    [Header("结束位置")]
    public Vector3 EndPos;
    [Header("起始方向")]
    public Vector3 StartVec;
    [Header("结束方向")]
    public Vector3 EndVec;
    [Header("缩放比例")]
    public Vector3 Scale;
    // Use this for initialization
    void Start() {

        //平移矩阵
        Vector4[] moveTransfer = {
            new Vector4(1,0,0,EndPos.x - StartPos.x),
            new Vector4(0,1,0,EndPos.y - StartPos.y),
            new Vector4(0,0,1,EndPos.z - StartPos.z),
            new Vector4(0,0,0,1)
        };
        Matrix4x4 moveTransferMatrix = new Matrix4x4();
        for (int i = 0; i < moveTransfer.Length; i++)
        {
            moveTransferMatrix.SetRow(i,moveTransfer[i]);
        }


        //计算两个向量间的欧拉角
        Vector3 eular = Quaternion.FromToRotation(StartVec,EndVec ).eulerAngles;
        eular.x = Vector3.Angle(Vector3.ProjectOnPlane(StartVec,Vector3.right), Vector3.ProjectOnPlane(EndVec, Vector3.right));
        eular.y = Vector3.Angle(Vector3.ProjectOnPlane(StartVec, Vector3.up), Vector3.ProjectOnPlane(EndVec, Vector3.up));
        eular.z = Vector3.Angle(Vector3.ProjectOnPlane(StartVec, Vector3.forward), Vector3.ProjectOnPlane(EndVec, Vector3.forward));
        //x轴旋转矩阵
        float angleX = eular.x == 90f ? 0 : eular.x; 
        float cosX = (float)Math.Cos((float)Math.PI / 180 * angleX);
        float sinX = (float)Math.Sin((float)Math.PI / 180 * angleX);
        Vector4[] rotateTransferX = {
            new Vector4(1,  0,      0,      0),
            new Vector4(0,  cosX,   -sinX,   0),
            new Vector4(0,  sinX,   cosX,  0),
            new Vector4(0,  0,      0,      1)
        };
        Matrix4x4 rotateTransferXMatrix = new Matrix4x4();
        for (int i = 0; i < rotateTransferX.Length; i++)
        {
            rotateTransferXMatrix.SetRow(i, rotateTransferX[i]);
        }


        //y轴旋转矩阵
        float angleY = eular.y == 90f ? 0 : eular.y;

        float cosY = (float)Math.Cos((float)Math.PI / 180 * angleY);
        float sinY = (float)Math.Sin((float)Math.PI / 180 * angleY);
        Vector4[] rotateTransferY = {
            new Vector4(cosY,  0,   sinY,   0),
            new Vector4(0,     1,   0,      0),
            new Vector4(-sinY, 0,   cosY,   0),
            new Vector4(0,     0,   0,      1)
        };
        Matrix4x4 rotateTransferYMatrix = new Matrix4x4();
        for (int i = 0; i < rotateTransferY.Length; i++)
        {
            rotateTransferYMatrix.SetRow(i, rotateTransferY[i]);
        }

        //Z轴旋转矩阵
        float angleZ = eular.z == 90f ? 0 : eular.z;

        float cosZ = (float)Math.Cos((float)Math.PI / 180 * angleZ);
        float sinZ = (float)Math.Sin((float)Math.PI / 180 * angleZ);
        Vector4[] rotateTransferZ = {
            new Vector4(cosZ,   -sinZ,  0,      0),
            new Vector4(sinZ,   cosZ,   0,      0),
            new Vector4(0,      0,      1,      0),
            new Vector4(0,      0,      0,      1)
        };
        Matrix4x4 rotateTransferZMatrix = new Matrix4x4();
        for (int i = 0; i < rotateTransferZ.Length; i++)
        {
            rotateTransferZMatrix.SetRow(i, rotateTransferZ[i]);
        }

        //缩放矩阵
        Vector4[] scaleransfer = {
            new Vector4(Scale.x,      0,          0,          0),
            new Vector4(0,            Scale.y,    0,          0),
            new Vector4(0,            0,          Scale.z,    0),
            new Vector4(0,            0,          0,          1)
        };
        Matrix4x4 scaleTransferMatrix = new Matrix4x4();
        for (int i = 0; i < scaleransfer.Length; i++)
        {
            scaleTransferMatrix.SetRow(i, scaleransfer[i]);
        }


        //Unity中的旋转矩阵:
        //Unity中按照  Z、X、Y轴的顺序进行旋转,下式乘法顺序不可变
        Matrix4x4 rotateMatrix = rotateTransferYMatrix  * rotateTransferXMatrix  * rotateTransferZMatrix;

        //最终变换矩阵
        Matrix4x4 transferMatrix = moveTransferMatrix * rotateMatrix *  scaleTransferMatrix;


        Matrix4x4 matScale = new Matrix4x4();

        matScale.SetTRS(Vector3.zero, Quaternion.Euler(Vector3.zero), new Vector3(Scale.x, Scale.y, Scale.z));
        Debug.Log("---------------------------\n");
        Debug.Log("测试缩放:\n" + scaleTransferMatrix.ToString("0.00"));
        Debug.Log("参照:\n" + matScale.ToString("0.00"));

        Matrix4x4 matMove = new Matrix4x4();
        matMove.SetTRS(EndPos - StartPos, Quaternion.Euler(Vector3.zero), Vector3.one);
        Debug.Log("---------------------------\n");
        Debug.Log("测试平移:\n" + moveTransferMatrix.ToString("0.00"));
        Debug.Log("参照:\n" + matMove.ToString("0.00"));

        Matrix4x4 matRoX = new Matrix4x4();
        matRoX.SetTRS(Vector3.zero, Quaternion.AngleAxis(angleX, Vector3.right), Vector3.one);
        Debug.Log("---------------------------\n");
        Debug.Log("测试旋转矩阵X:\n" + rotateTransferXMatrix.ToString("0.00"));
        Debug.Log("参照:\n" + matRoX.ToString("0.00"));

        Matrix4x4 matRoY = new Matrix4x4();
        matRoY.SetTRS(Vector3.zero, Quaternion.AngleAxis(angleY, Vector3.up), Vector3.one);
        Debug.Log("---------------------------\n");
        Debug.Log("测试旋转矩阵Y:\n" + rotateTransferYMatrix.ToString("0.00"));
        Debug.Log("参照:\n" + matRoY.ToString("0.00"));

        Matrix4x4 matRoZ = new Matrix4x4();
        matRoZ.SetTRS(Vector3.zero, Quaternion.AngleAxis(angleZ, Vector3.forward), Vector3.one);
        Debug.Log("---------------------------\n");
        Debug.Log("测试旋转矩阵Z:\n" + rotateTransferZMatrix.ToString("0.00"));
        Debug.Log("参照:\n" + matRoZ.ToString("0.00"));

        Debug.Log("---------------------------\n");

        //利用U3D内置方法算出由向量v1到向量v2的变换矩阵,用来参照
        Matrix4x4 referenceMat = new Matrix4x4();
        referenceMat.SetTRS(EndPos - StartPos, Quaternion.Euler(eular),new Vector3(Scale.x, Scale.y, Scale.z));

        Debug.Log("变换矩阵 : \n" + transferMatrix.ToString("0.00"));
        Debug.Log("参照矩阵 : \n" + referenceMat.ToString("0.00"));


        List<Vector3> meshList = new List<Vector3>();
        Mesh n_mesh = GetComponent<MeshFilter>().mesh;

        for (int i = 0; i < n_mesh.vertexCount; i++)
        {
            meshList.Add(transferMatrix.MultiplyPoint(n_mesh.vertices[i]));
        }
        n_mesh.SetVertices(meshList);
    }
}

将脚本挂在Start胶囊体上面,并填写参数


运行Unity,可以看到Start胶囊体成功和End胶囊体重合。


在Console中,可以看到各个矩阵的计算结果,作为参照,还使用Unity中自带的构建变换矩阵的方法计算相应的矩阵作为参照, 其中最终的计算矩阵为:



展开阅读全文

没有更多推荐了,返回首页