Unity矩阵matrix4x4的应用:世界空间与模型空间的坐标系转化

文章目录

  • 前言
  • 一、矩阵 Matrix4x4?
  • 二、应用过程
    • 1、案例分析
    • 2、函数介绍
      • Matrix4x4.TRS
      • Matrix4x4.MultiplyVector
      • Matrix4x4.MultiplyPoint
      • Matrix4X4.inverse
      • Matrix4X4.rotation
    • 3、实际代码
  • 总结


前言

近期由于项目需求,研究了一下通过矩阵来转换不同坐标系,接下来就简单记录一下。


一、矩阵 Matrix4x4?

UnityEngine.Matrix4x4 是 Unity 提供的方便矩阵计算而封装的一个结构体,提供了很多相对便于日常开发的函数。除了 官方API 之外,相信网上有很多关于这个矩阵的定义的解释了,这里就不介绍了,给个 友情链接 吧。

这里我们结合一个具体例子来看吧:我们想知道一只蚂蚁在一个很大的球上的可视范围(别和我纠结蚂蚁的视力问题啊!)。

二、应用过程

1、案例分析

       首先,我们能确定的是,蚂蚁在球面上是相对于球面有一个偏转的。所以,蚂蚁自身是有一个 “模型空间坐标系”的,同时,不可避免的,蚂蚁也处于另外的一个“Unity世界空间坐标系”中。在Unity中,蚂蚁的位置是相对世界空间坐标系来说的。
       我们想知道蚂蚁的可视范围,可以想到的方式有,以蚂蚁为圆心,向四周打一圈射线,判断碰撞,得出可视范围,可以此绘制出Mesh。

2、函数介绍

Matrix4x4.TRS

        该函数可用于构建旋转矩阵。三个参数分别为位置、旋转与缩放。上述案例中,世界空间坐标系为标准三维空间坐标系,而模型空间坐标系可以用蚂蚁的位置与旋转来表示。

Matrix4x4.MultiplyVector

        上述旋转矩阵中,该函数可用于转换世界空间坐标系中的向量至模型空间坐标系。

Matrix4x4.MultiplyPoint

        上述旋转矩阵中,该函数可用于转换世界空间坐标系中的坐标点至模型空间坐标系。

Matrix4X4.inverse

        矩阵的逆。可获取该矩阵的逆矩阵。结合上面两个函数,我们就可转换模型空间坐标系中的信息至世界空间。

Matrix4X4.rotation

        用于获取该旋转矩阵中的旋转四元数。可以乘以另一个四元数以获取它在另一个坐标系下的表示。


    public bool isPoint;
    public bool inverse;

    public Transform orgCoor;	// 原始坐标系
    public Transform newCoor;	// 新坐标系

    public Transform tranOrg;	// 原始位置
    public Transform tranNew;	// 转换后的位置

    /// 
    /// 转点
    /// 
    private void MultiplyPoint()
    {
     
        Matrix4x4 matrix4X4 = Matrix4x4.TRS(newCoor.position, newCoor.rotation, newCoor.localScale);

        if (!inverse)
            tranNew.position = matrix4X4.MultiplyPoint(tranOrg.position);
        else
            tranNew.position = matrix4X4.inverse.MultiplyPoint(tranOrg.position); // 这里的 position 依然是相对于世界空间坐标系(orgCoor)来说的

        //Vector4 a = matrix4X4.inverse * (tranOrg.position);
    }

    /// 
    /// 转向量
    /// 
    private void MultiplyVector()
    {
     
        Matrix4x4 matrix4X4 = Matrix4x4.TRS(newCoor.position, newCoor.rotation, newCoor.localScale);

        if (!inverse)
        {
     
            tranNew.position = matrix4X4.MultiplyVector(tranOrg.position - orgCoor.position) + newCoor.position;
            tranNew.rotation = matrix4X4.rotation * tranOrg.rotation;
        }
        else
        {
     
            tranNew.position = matrix4X4.inverse.MultiplyVector(tranOrg.position - newCoor.position) + orgCoor.position;
            tranNew.rotation = matrix4X4.inverse.rotation * tranOrg.rotation;   // 四元数相乘 改朝向
        }
    }

Unity矩阵matrix4x4的应用:世界空间与模型空间的坐标系转化_第1张图片

3、实际代码

       利用世界空间坐标系与Mesh的绘制坐标系相同,我们可以将理想的锥形在世界空间中表示出来,再转换到模型空间坐标系中(此时虽然是模型空间中,但还是用的世界空间坐标系中的坐标点表示位置)利用碰撞计算Mesh的顶点位置,最后再转换回世界空间坐标系中。此时,Mesh顶点的坐标表示是相对世界空间的,可以用于生成 Meshfilter.mesh 的赋值。

最后,附上相关代码


    /// 
    /// 球面上的地面侦察范围Mesh创建(带碰撞)
    /// 
    /// 球面上某点的Transform
    /// 侦察半径
    /// 层级
    /// 
    public static Mesh CreateAntDetectionRange(Transform trans, float radius, int layer, int upHeight = 20)
    {
     
        // 此处涉及2个空间坐标系,分别为 unity世界空间坐标系、物体的模型空间坐标系
        Matrix4x4 matrix4X4 = Matrix4x4.TRS(trans.position, trans.rotation, trans.localScale);  // 构造从 unity世界空间坐标系 到 物体的模型空间坐标系 矩阵
        Vector3 orgPoint = trans.position;
        Vector3 upPoint = matrix4X4.MultiplyPoint(Vector3.up * upHeight);   // 中间点的 unity世界坐标点

        //List vertices = new List { Vector3.up * upHeight };
        List<Vector3> vertices = new List<Vector3>();

        for (int i = 0; i <= 360; i += 3)
        {
     
            Vector3 endPosSelf = Quaternion.AngleAxis(i, Vector3.up) * Vector3.forward * radius;    // 模型空间中的位置信息
            Vector3 endPos = matrix4X4.MultiplyPoint(endPosSelf);   // 模型空间相对位置信息 转化为 世界空间位置信息
            if (Physics.Raycast(upPoint, (endPos - upPoint).normalized, out RaycastHit hit, radius, layer))
            {
     
                endPos = hit.point;
                Debug.DrawLine(upPoint, endPos, Color.yellow);
            }
            else
            {
     
                Debug.DrawLine(upPoint, endPos, Color.red);
            }

            // //Debug.DrawLine(Vector3.up * upHeight + orgPoint, Quaternion.AngleAxis(i, Vector3.up) * Vector3.forward * radius + orgPoint, Color.green);


            //Vector3 direction = matrix4X4.MultiplyVector(Quaternion.AngleAxis(i, Vector3.up) * Vector3.forward * radius - Vector3.up * upHeight);  // 自身射线方向 转到 世界空间射线方向
            //Vector3 endPos = upPoint + direction;
            //if (Physics.Raycast(upPoint, direction.normalized, out RaycastHit hit, radius, layer))
            //{
     
            //    endPos = hit.point;
            //    Debug.DrawLine(upPoint, endPos, Color.yellow);
            //}
            //else
            //{
     
            //    Debug.DrawLine(upPoint, endPos, Color.red);
            //}
            vertices.Add(matrix4X4.inverse.MultiplyPoint(endPos));
        }
        return CreatePolygonMesh(matrix4X4.inverse.MultiplyPoint(upPoint), vertices.ToArray());
    }
    /// 
    /// 简单多边形网格(单中心点连边)
    /// 
    /// 中点
    /// 
    /// 
    public static Mesh CreatePolygonMesh(Vector3 center, Vector3[] points, bool coverEnd = true)
    {
     
        List<Vector3> vertices = new List<Vector3>();
        vertices.Add(center);
        vertices.AddRange(points);
        List<int> triangles = new List<int>();
        for (int i = 1; i < vertices.Count; i++)
        {
     
            if (i == vertices.Count - 1)
            {
     
                if (coverEnd)
                {
     
                    triangles.Add(0);
                    triangles.Add(i);
                    triangles.Add(1);
                }
            }
            else
            {
     
                triangles.Add(0);
                triangles.Add(i);
                triangles.Add(i + 1);
            }
        }

        Mesh mesh = new Mesh()
        {
     
            vertices = vertices.ToArray(),
            triangles = triangles.ToArray()
        };
        //mesh.RecalculateNormals();

        return mesh;
    }

总结

坐标系之间的转化需要一定的空间想象能力,这方面不是很强的同学可以试试用三支笔模拟一个坐标系,方便理解。

你可能感兴趣的:(Unity,unity,旋转矩阵,坐标系转化)