Unity 不同空间坐标转换中的矩阵应用

本文深入探讨Unity中如何利用矩阵进行空间坐标转换,包括从世界坐标到局部坐标的矩阵计算过程,详细解析了齐次坐标、TRS矩阵及其转换。通过实例分析矩阵乘法和矩阵变换的顺序,帮助理解矩阵在3D空间变换中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本文章主要介绍通过矩阵的方式实现对象的空间变换,主要涉及的变换形式为世界空间转相对空间中的矩阵实现过程

实际开发中,Unity已经为开发者封装好一些坐标转换的方法,使用起来是比较方便的,但是对于理解而言就比较复杂了,所以下面的介绍会基于模拟的方式来了解Unity的矩阵坐标变换过程

一、通过向量进行空间变换

在开始前先做一点准备工作,创建两个空物体分别命名为centerPostargetPos

接下来所执行的操作就可以理解为求targetPos相对于centerPos的本地坐标系下的坐标,即此时centerPos为坐标原点,来求出targetPos在该坐标系下的坐标位置

为了便于观察,通过Gizmos绘制出centerPos的本地坐标系,同时标识出两者的位置关系,如图所示:
在这里插入图片描述

为了便于后续矩阵的理解,先通过常规的方式来计算得到targetPos相对于centerPos的本地坐标位置,实现过程很简单,分为两步来描述:

  • 得到从centerPos指向targetPos的向量
  • 计算该向量在centerPos坐标系下各个轴的投影

得到一个向量可以通过终止坐标点减去起始坐标点获取,而投影就更简单了,只需要与centerPos本地坐标系的各个轴的向量做一个点积即可,经过计算就可以得到转换坐标系后的坐标位置,代码为:

    public Vector3 GetTargetRelatPos(Transform targetPos , Transform centerPos)
    {
   
        Vector3 result=new Vector3(0,0,0);
        Vector3 v3 = targetPos.position - centerPos.position;
        result.x = Vector3.Dot(v3 , centerPos.right);
        result.y = Vector3.Dot(v3 , centerPos.up);
        result.z = Vector3.Dot(v3 , centerPos.forward);
        return result;
    }

直接通过代码解释比较抽象,而这部分恰巧又与后面坐标转换矩阵的内容相关,所以这里来将其可视化一下:
在这里插入图片描述
图中各个对象的信息为:

  • 绿色小球:中心点centerPos与目标点targetPos
  • 红、绿、蓝三条辅助线:centerPos的本地坐标系的三个轴
  • 黄色辅助线:从centerPos指向targetPos的向量
  • 白色辅助线:代表向量投影坐标轴的垂线
  • 白色小球:代表向量在各个轴上的投影点

绘制的代码为,可以在项目中实际操作一下,转换角度来理解:

	public void OnDrawGizmos()
    {
   
        //绿色小球绘制
        Gizmos.color = Color.green;
        Gizmos.DrawSphere(centerPos.position, 0.1f);
        Gizmos.DrawSphere(targetPos.position, 0.1f);
        //坐标辅助线绘制
        Gizmos.color=Color.red;
        Gizmos.DrawLine(centerPos.position, centerPos.position+centerPos.right*10);  
        Gizmos.color=Color.green;
        Gizmos.DrawLine(centerPos.position, centerPos.position+centerPos.up*10);  
        Gizmos.color=Color.blue;
        Gizmos.DrawLine(centerPos.position, centerPos.position+centerPos.forward*10);
        //向量绘制
        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(centerPos.position, targetPos.position);

        Vector3 v3 = GetTargetRelatPos(targetPos,centerPos);
        //垂线绘制
        Gizmos.color = Color.white;
        Gizmos.DrawLine(targetPos.position, centerPos.position+ v3.x * centerPos.right); 
        Gizmos.DrawLine(targetPos.position, centerPos.position+ v3.y * centerPos.up);
        Gizmos.DrawLine(targetPos.position, centerPos.position+ v3.z * centerPos.forward);
        //小球绘制
        Gizmos.DrawSphere(centerPos.position + v3.x * centerPos.right, 0.1f);
        Gizmos.DrawSphere(centerPos.position + v3.y * centerPos.up, 0.1f);
        Gizmos.DrawSphere(centerPos.position + v3.z * centerPos.forward, 0.1f);
    }

通过对上面的代码的解析,可以看出影响物体坐标转换的关键因素在于参考点centerPos的坐标位置与其旋转关系,这里通过一个动图来简单的解释一下:

在这里插入图片描述
通过图中演示坐标系初始位置、旋转、缩放三个维度变化对于最终结果的影响可以看出,坐标系初始位置的变化会引起向量的模的变化,而旋转则会改变向量在各个坐标轴的分量,即局部空间下的向量方向的改变,那么我们很容易就总结出:

  • 影响物体局部坐标的关键因素在于初始点的位置与其方向,这里的方向可以拆分为三个坐标轴rightupforward,而缩放则不会引起坐标转换过程中位置的变化

二、使用空间变换矩阵

同样对于上面的一个案例,通过矩阵的方式来计算得到targetPos相对于centerPos坐标系的局部坐标,为了便于理解,我们先排除缩放的影响,默认centerPos的缩放为(1,1,1),则可以通过下面几种方式完成计算:

	//第一种:
	public Vector3 GetTargetRelatPosFirst(Transform targetPos, Transform centerPos)
    {
         
        return centerPos.InverseTransformPoint(targetPos.position);
    }
 	//第二种:
    public Vector3 GetTargetRelatPosSencond(Transform targetPos, Transform centerPos)
    {
          
        Matrix4x4 m4 = centerPos.worldToLocalMatrix;
        return m4.MultiplyPoint3x4(targetPos.position);
    }
    //第三种:
    public Vector3 GetTargetRelatPosThird(Transform targetPos, Transform centerPos)
    {
   
        Vector4 v4 = new Vector4(targetPos.position.x, targetPos.position.y, targetPos.position.z, 1);
        return centerPos.worldToLocalMatrix * v4;
    }
	

几种方式的实现过程大致相同,都是基于矩阵乘法的坐标转换,不过第一种与第二种是Unity官方对矩阵与三维向量乘法的一个进一步封装,而第三种的实现过程相对完整,这里就根据第二种的计算方法来理解,大概可以分为下面几个环节:

1、齐次坐标转换:

通过第三种解决方案的代码可以看到,会将targetPosPositionVector3变换为Vector4,并对最后的一维补充为1。通过高中数学知识可以了解到,向量可以转换成为矩阵,而对于矩阵的乘法而言,第一个矩阵的列数必须与第二个矩阵的行数相同,所以这里为了可以进行计算,需要将三维向量转换为四维

但是最后一个1并不是单纯的补位,在矩阵的乘法中也有着重要的计算意义,比如说,如果我们不手动将Vector3变换为Vector4Unity也会默认帮我们处理变换,但是最后补充的就是数字0,然后你就会惊奇的发现计算的结果与实际结果有一定的偏差

这里是齐次坐标转换规则的原因,一般来说,在坐标转换矩阵中,会将对象的坐标运算从笛卡尔坐标空间转换到齐次坐标空间内。而在齐次坐标空间内0代表向量,而1代表的是坐标点,而由于我们 通过转换的矩阵是直接通过目标点targetPos的世界坐标来计算的,而不像我们初始实现的那样,通过centerPostargetPos的向量来执行计算,所以这里要补充的是1,代表该vector3表示的是targetPos的世界坐标

由于该位是1,就会在后续的矩阵乘法计算中加上偏移量,也就是centerPos的世界坐标点,代表的其相对于世界坐标(0,0,0)的偏移量,有点难以理解,但是到后面的矩阵计算时就会一目了然了

2、理解TRS矩阵

TRS矩阵,也就是由TranslateRotateScale组合而成的矩阵,并且矩阵的变换顺序为先Scale,然后Rotate,最后再进行Translate的操作,这样的执行顺序是为了避免由于位移与旋转的影响而产生缩放的形变问题,所以先执行缩放的操作,而TRS的执行顺序可以理解为:

  • T(R(Sp)))

要完成TRS矩阵的生成转发,首先要理解TRS矩阵所构成的位置、旋转、缩放分别对应的基本矩阵,所以这里首先拆解一下各个矩阵:

Translate:

基于单位矩阵进行变化,位置信息会被记录在4x4矩阵的m01m02m03位置,在Unity中可以通过Matrix4x4.Translate()打印出位置矩阵。这里通过设置物体坐标位置(1,2,3)并通过Debug.Log(Matrix4x4.Translate(centerPos.position));打印出centerPos对应的位置矩阵,结果如图所示:

在这里插入图片描述
Translate的矩阵表示为:
{ 1 0 0 T x 0 1 0 T y 0 0 1 T z 0 0 0 1 } \left\{ \begin{matrix} 1 & 0 & 0 & T_x\\ 0 & 1 & 0 & T_y\\ 0 & 0 & 1 & T_z \\ 0 &0 &0 &1 \end{matrix} \right\} 100001000010TxTyTz1

Rotate:

旋转矩阵本身需要3x3矩阵,该矩阵记录了对象本地坐标的三个坐标轴的单位向量,即centerPos.RightcenterPos.Up以及centerPos.Forward三个坐标轴的向量,为centerPos设置一定的旋转量,并打印出结果,实施打印的代码为:

        Debug.Log(Matrix4x4.Rotate(centerPos.rotation));
        Debug.Log(centerPos.right);
        Debug.Log(centerPos.up);
        Debug.Log(centerPos.forward);

打印结果,如图:
在这里插入图片描述
可以看出矩阵数值与对象的局部坐标轴的单位向量相互对应,即对象的旋转矩阵表示为:

{ R x U x F x 0 R y U y F y 0 R z U z F z 0 0 0 0 1 } \left\{ \begin{matrix} R_ x & U_x & F_x & 0\\ R_y & U_y & F_y & 0\\ R_z & U_z & F_z & 0 \\ 0 &0 &0 &1 \end{matrix} \right\} RxRyRz0UxUyUz0

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之凌儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值