当前位置: 首页 > article >正文

Unity局部和世界坐标系相互转换的实现原理

注:本篇是基于唐老师的学习视频做的一些理论实践,需要提前知道一些线性代数的基础知识,原视频链接:

8.数学基础知识学习说明_哔哩哔哩_bilibili

前期准备:

知识点①:

        Unity中需要遵守的设定:

                1、我们约定变换顺序为:缩放->旋转->平移。

                2、我们约定旋转的顺序为:Z->X->Y。

知识点②:

        1、基础变换矩阵的构成规则:

        2、平移矩阵的定义:

                A=\begin{bmatrix} 1 & 0& 0 & tx \\ 0& 1& 0& ty\\ 0& 0& 1& tz\\ 0& 0&0 & 1 \end{bmatrix}       逆矩阵     A^{-1}=\begin{bmatrix} 1 & 0 & 0 & -tx \\ 0& 1 & 0& -ty\\ 0& 0& 1 & -tz\\ 0& 0& 0& 1 \end{bmatrix}

        3、旋转矩阵的定义:    

                       绕X轴旋转\beta度:                        绕Y轴旋转\beta度:                       绕Z轴旋转\beta度:

               \begin{bmatrix} 1 & 0 & 0 & 0\\ 0& cos\beta & -sin\beta &0 \\ 0& sin\beta & cos\beta &0 \\ 0& 0 & 0 & 1 \end{bmatrix}          \begin{bmatrix} 1 & 0 & 0 & 0\\ 0& cos\beta & -sin\beta &0 \\ 0& sin\beta & cos\beta &0 \\ 0& 0 & 0 & 1 \end{bmatrix}          \begin{bmatrix} 1 & 0 & 0 & 0\\ 0& cos\beta & -sin\beta &0 \\ 0& sin\beta & cos\beta &0 \\ 0& 0 & 0 & 1 \end{bmatrix}

                因为旋转矩阵是正交矩阵,所以它的逆矩阵就是它的转置矩阵。

                即:假设有旋转矩阵A,那么 A^{-1}=A^{T}

        4、缩放矩阵的定义:

                A=\begin{bmatrix} kx & 0 & 0 & 0\\ 0 & ky & 0 & 0\\ 0 & 0 & kz & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}    逆矩阵   A^{-1}=\begin{bmatrix} 1/kx & 0 & 0 & 0\\ 0 & 1/ky & 0 & 0\\ 0 & 0 & 1/kz & 0\\ 0 & 0 & 0 & 1 \end{bmatrix}

局部坐标转世界:

        我们需要明白一个概念,在3D空间中,假设有一个结点R存在一个子节点A,那么如果R就是坐标原点,A的局部坐标系就是世界坐标系。如果结点R存在旋转,平移等变换,那么A的局部坐标依旧不会变,R的变换会带动A的变换。那么最终的世界坐标满足关系式:

{A}'=M*A

M代表R的变换矩阵,A代表R在原点时的世界坐标(即局部坐标),A'代表最终的世界坐标。

再根据知识点1,得到矩阵M=平移矩阵A×旋转矩阵B×缩放矩阵C

便有如下代码:

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

public class Test : MonoBehaviour
{
    public Transform targetTrans;
    private void Start()
    {
        Vector4 startPos = new Vector4(targetTrans.localPosition.x, targetTrans.localPosition.y, targetTrans.localPosition.z, 1);
        Matrix4x4 scaleMatrix = ScaleMatrix(transform.localScale.x, transform.localScale.y, transform.localScale.z);
        Matrix4x4 rotateMatrix = RotateYMatrix(transform.eulerAngles.y)*RotateXMatrix(transform.eulerAngles.x)*RotateZMatrix(transform.eulerAngles.z);
        Matrix4x4 translateMatrix = TranslateMatrix(transform.position.x, transform.position.y, transform.position.z);
        //按照缩放->旋转(按照Z->X->Y顺序旋转)->平移的变换顺序
        Vector4 resPos = translateMatrix * rotateMatrix * scaleMatrix * startPos;
        Debug.Log(string.Format("局部坐标转世界坐标={0}",resPos));
        Debug.Log(string.Format("调用UnityAPI的结果={0}",transform.TransformPoint(startPos)));
    }

    //缩放矩阵
    private Matrix4x4 ScaleMatrix(float x,float y,float z)
    {
        Matrix4x4 targetMatrix = new Matrix4x4();
        targetMatrix.m00 = x;
        targetMatrix.m11 = y;
        targetMatrix.m22 = z;
        targetMatrix.m33 = 1;
        return targetMatrix;
    }

    //旋转矩阵(X轴)
    private Matrix4x4 RotateXMatrix(float angle)
    {
        Matrix4x4 targetMatrix = new Matrix4x4();
        targetMatrix.m00 = 1;
        targetMatrix.m11 = Mathf.Cos(angle * Mathf.Deg2Rad);
        targetMatrix.m12 = -Mathf.Sin(angle * Mathf.Deg2Rad);
        targetMatrix.m21 = Mathf.Sin(angle * Mathf.Deg2Rad);
        targetMatrix.m22 = Mathf.Cos(angle * Mathf.Deg2Rad);
        targetMatrix.m33 = 1;
        return targetMatrix;
    }
    
    //旋转矩阵(Y轴)
    private Matrix4x4 RotateYMatrix(float angle)
    {
        Matrix4x4 targetMatrix = new Matrix4x4();
        targetMatrix.m00 = Mathf.Cos(angle * Mathf.Deg2Rad);
        targetMatrix.m02 = Mathf.Sin(angle * Mathf.Deg2Rad);
        targetMatrix.m11 = 1;
        targetMatrix.m20 = -Mathf.Sin(angle * Mathf.Deg2Rad);
        targetMatrix.m22 = Mathf.Cos(angle * Mathf.Deg2Rad);
        targetMatrix.m33 = 1;
        return targetMatrix;
    }
    
    //旋转矩阵(Z轴)
    private Matrix4x4 RotateZMatrix(float angle)
    {
        Matrix4x4 targetMatrix = new Matrix4x4();
        targetMatrix.m00 = Mathf.Cos(angle * Mathf.Deg2Rad);
        targetMatrix.m01 = -Mathf.Sin(angle * Mathf.Deg2Rad);
        targetMatrix.m10 = Mathf.Sin(angle * Mathf.Deg2Rad);
        targetMatrix.m11 = Mathf.Cos(angle * Mathf.Deg2Rad);
        targetMatrix.m22 = 1;
        targetMatrix.m33 = 1;
        return targetMatrix;
    }
    
    //平移矩阵
    private Matrix4x4 TranslateMatrix(float x,float y,float z)
    {
        Matrix4x4 targetMatrix = new Matrix4x4();
        targetMatrix.m03 = x;
        targetMatrix.m13 = y;
        targetMatrix.m23 = z;
        targetMatrix.m00 = 1;
        targetMatrix.m11 = 1;
        targetMatrix.m22 = 1;
        targetMatrix.m33 = 1;
        return targetMatrix;
    }
}

挂载脚本:

我们用了Unity自带的局部转世界的APITransform.TransformPoint进行结果对比,发现最终的计算结果是一样的(忽略第四个参数1.0,代表的含义是点)。

世界坐标转局部:

        由刚刚的{A}'=M*A公式推导,其实可以得到:

                                        ​​​​​​​                M^{-1}*{A}'=A

        即局部坐标=逆变换*世界坐标

由上面的性质得到已知  矩阵M=平移矩阵A×旋转矩阵B×缩放矩阵C,那么矩阵M的逆矩阵

                                                         M^{-1}=C^{-1}*B^{-1}*A^{-1}

矩阵A,B,C的逆矩阵都可以根据知识点2得到结果,最终就可以根据世界坐标和逆变换反推导局部坐标。


http://www.kler.cn/a/446298.html

相关文章:

  • 29、基于springboot的网上购物商城系统研发
  • nginx自定义错误日志
  • MySQL复制问题和解决
  • 深度学习推理速度优化指南
  • OneCode:开启高效编程新时代——企业定制出码手册
  • Day45 动态规划part12
  • java——Synchronized与Lock
  • 2024年《网络安全事件应急指南》
  • oracle 设置归档日志存放路径
  • 网络安全 | 五大核心领域:防护、检测、响应、恢复与治理
  • 欢乐力扣1-10
  • filebeat7.0安装和基本使用
  • 【C++】智能指针详解
  • SpringBoot Redis list 消息队列
  • HTTP—03
  • Flutter组件————FloatingActionButton
  • 【优先算法】双指针 --(结合例题讲解解题思路)(C++)
  • 【java】全文索引,普通索引,以及ES搜索引擎组件的关系
  • MATLAB中cvx工具箱的使用
  • 三次翻转实现数组元素的旋转
  • 深入了解Python模拟负载均衡器:将请求高效分发至多个服务器
  • Emacs折腾日记(四)——elisp控制结构
  • Django 模板分割及多语言支持案例【需求文档】-->【实现方案】
  • springboot 3 websocket react 系统提示,选手实时数据更新监控
  • Flask内存马学习
  • (12)YOLOv10算法基本原理