zoukankan      html  css  js  c++  java
  • Siki_Unity_3-16_3D数学基础

    Unity 3-16 3D数学基础

    任务0-1:课程介绍

    课程大纲:
      1. 3D数学介绍
      2. Unity中的几种坐标系:
        全局坐标系、屏幕坐标系等
        坐标系间的坐标转换:比如屏幕坐标转换到世界坐标
      3. 向量的基本概念:
      4. 向量运算:
        计算长度
        向量与向量之间的加减乘,向量与标量之间的乘法,点乘和叉乘
      5. 矩阵
      6. 变换

    任务1-1&1-2&1-3&1-4:3D数学介绍 -- 坐标系基础知识

    内容:
      1D -- 数轴
      2D -- 笛卡尔坐标系
      3D -- 空间直角坐标系
      左手坐标系和右手坐标系

    1D:关于计数和度量的数学

    数学上,数轴是一维的图,数轴是一条规定了原点、方向和单位长度的直线

    2D:笛卡尔坐标系 Cartesian coordinates

    两条直线可以确定一个唯一的平面(异面直线通过平移)
    相交于原点的两条数轴,构成了平面放射坐标系
    若两条数轴的单位长度相同,则为笛卡尔坐标系

    是的,笛卡尔坐标系是笛卡尔直角坐标系和笛卡尔斜角坐标系的统称

    3D:空间直角坐标系

    三个轴互相垂直
    空间直角坐标系和Unity中的直角坐标系z轴方向是相反的
      Unity为左手坐标系;
    坐标(x, y, z)表示分别到三个平面的有符号距离

    左手坐标系和右手坐标系:

    空间直角坐标系:右手坐标系
    OpenGL:右手坐标系
    Direct3D:左手坐标系
    Unity3D:左手坐标系

    任务1-5:习题

    1.3D笛卡尔坐标系中有一个点(2,3,3),如果不改变它的位置,将坐标系更改为OpenGL中使用的坐标系和Unity3D中使用的坐标系,则它的坐标值将分别变为多少?

    2.在3DMAX中,使用的坐标系的方向为:+x向右,+y向前 (屏幕向内),+z向上。这个坐标系是左手坐标系还是右手坐标系?

    解答:

    1. 3D笛卡尔坐标系为右手坐标系,OpenGL也是使用右手坐标系,Unity3D使用的是左手坐标系
      因此OpenGL的坐标和3D笛卡尔坐标系相同,为 (2, 3, 3)
      Unity3D坐标的z值取负,为 (2, 3, -3)

    2. 分别将拇指向右,食指向前,中指向上 -- 右手可以做到:右手坐标系

    任务2-1&2-2&2-3&2-4:四种坐标系
    (全局坐标系、局部坐标系、屏幕坐标系、视口坐标系)

    内容:
      1. 全局坐标系 World Coordinate System
      2. 局部坐标系 Local Coordinate System
      3. 屏幕坐标系 Screen Space
      4. 视口坐标系 ViewPort Space
      5. 坐标系之间的关联和相互转换

    全局坐标系(世界坐标系)

    Unity中,如果一个游戏物体没有父物体,则Inspector中transform显示的为全局坐标
    Unity中,可以用transform.position取得一个物体的世界坐标

    局部坐标系(物体坐标系、模型坐标系)

    每个物体都有自身独立的物体坐标系
    当物体移动或改变方向时,和该物体相关联的局部坐标系本身将随之移动或改变方向
    Unity中模型Mesh保存的顶点坐标均为局部坐标系下的坐标,因此物体的改变并不会影响顶点的坐标

    Unity中,如果一个游戏物体没有父物体,则它的本地坐标 
    如果一个游戏物体是另一个的子物体,则Inspector中transform显示的为局部坐标
    可以通过transform.localPosition取得一个物体在其父物体的局部坐标系中的坐标

    屏幕坐标系

    建立在屏幕上的二维坐标系
    以像素为单位,屏幕的左下角为(0, 0),右上角为 (Screen.width, Screen.height)
      z轴坐标为相机的世界坐标中z轴的负值
    鼠标位置坐标属于屏幕坐标 -- Input.mousePosition
      超出屏幕边缘也是有返回值的(负值)
    手指触摸屏幕的坐标属于屏幕坐标 -- Input.GetTouch(0).position

    视口坐标系

    将Game视图的屏幕坐标系单位化,即左下角为(0, 0),右上角为(1, 1)
    z轴坐标是相机的世界坐标中z轴坐标的负值

    好处:得到的是比例,无需知道屏幕的大小
    坏处:不能直接得到,需要通过其他坐标系进行转换

    任务2-5:世界坐标系局部坐标系之间的关联和相互转换
    &2-6:transform.forward和Vector3.forward

    关联:

    移动一个游戏物体时:
      transform.Translate(translation: Vector3, relativeTo: Space = Space.Self)
      如果relativeTo=Space.Self (默认),则移动是按照局部坐标系来的
      如果relativeTo=Space.World,则移动是按照世界坐标系来的

    例:
      transform.Translate(new Vector3(1, 0, 0) * Time.deltaTime); -- 局部坐标系的x轴方向
      transform.Translate(new Vector3(1, 0, 0)*Time.deltaTime, Space.World); --世界坐标系

    相互转换:

    坐标的转换:
      局部坐标系 -> 全局坐标系:Transform.TransformPoint(Vector3 position);
      全局坐标系 -> 局部坐标系:Transform.InverseTransformPoint(Vector3 position);

    方向的转换:
      局部坐标系 -> 全局坐标系:Transform.TransfromDirection(Vector3 direction);
      全局坐标系 -> 局部坐标系:Transform.InverseTransformDirection(Vector3 direction);

    向量的转换:
      局部坐标系 -> 全局坐标系:Transform.TransfromVector(Vector3 vector);
      全局坐标系 -> 局部坐标系:Transform.InverseTransformVector(Vector3 vector);

    transform.forward和Vector3.forward:

    transform.forward, transform.right, transform.up:
      当前物体的物体坐标系的三个坐标轴(z轴, x轴, y轴)在世界坐标系上的指向
      注意:不是简单的在物体坐标系中的指向

        比如:
          transform.Translate(transform.forward * Time...., Space.World);
          先求出物体坐标系中的z轴正方向在世界坐标系中的坐标表示
          然后将该坐标的值放在物体坐标系中,进行移动
        例子:
          

    物体顺时针旋转90°,因此物体坐标系为世界坐标系顺时针旋转90°
    transform.Translate(transform.forward * Time.deltaTime, Space.Self);
      transform.forward为物体的z轴方向,在世界坐标系中为x轴方向(1, 0, 0)
      因为relativeTo=Space.Self,将(1,0,0)放入物体坐标系中,即物体坐标系x轴方向
      故物体朝该方向移动 -- (一共进行了两次转换)

    transform.Translate(transform.forward * Time.deltaTime, Space.World);
      transform.forward为物体的z轴方向,在世界坐标系中为x轴方向(1, 0, 0)
      因为relativeTo=Space.World, 方向在世界坐标系中为(1, 0, 0)
      故物体朝该方向移动 -- (一共进行了一次转换)

    再举个栗子,如果物体是顺时针旋转30°,那么
      如果relativeTo=Space.World,那么就是朝着物体的z轴方向移动(30°方向)
      如果relativeTo=Space.Self,则要在World情况下再旋转30°,即为60°
        因为物体坐标系相对于世界坐标系是顺时针旋转了30°的
    再举个栗子,如果物体是顺时针旋转45°的,那么
      Space.World情况为朝着(45°)方向
      Space.Self情况为朝着(90°)方向

    Vector3.forward, Vector3.right, Vector3.up:
      分别为(0,0,1) (1,0,0) (0,1,0)的缩写
      在transform.Translate()中使用时:
        若不标明坐标系,则为物体的局部坐标,即物体自身的前右上方向
        若relativeTo=Space.World,则为世界坐标系,即世界坐标的前右上方向

    任务2-7:屏幕坐标系与全局坐标系之间的关联和相互转换

    camera.WorldToScreenPoint(Vector3 position) -- 将世界坐标转换为屏幕坐标camera.ScreenToWorldPoint(Vector3 position) -- 将屏幕坐标转换为世界坐标

    例子:

    世界坐标转换为屏幕坐标:
      Camera.main.WorldToScreenPoint(transform.position);
      若该物体位于屏幕中心显示,且MainCamera的z轴坐标为-5
      则Debug.Log的结果为(Screen.Width/2, Screen.Height/2, 5) -- z轴值正负相反

    屏幕坐标转换为世界坐标:
      Vector3 mousePos = Input.mousePosition;
      Vector3 sToW = Camera.main.ScreenToWorldPoint(mousePos);
      此时Debug.Log的结果为 (0, 0, -5) -- 并不是我们想要的
        因为ScreenToWorldPoint()是将mousePos投影在一个平面上
        因此mousePos的坐标为(x, y, 0),与相机是重合的,因此不能视为平面,而是一个点
          所以返回结果就是相机的坐标(0, 0, -5)
        如果设置为离相机1000的平面,且鼠标是在屏幕中点位置,则为(0, 0, -5+1000)

      使用例子:
        Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, 500);
        即为鼠标所在屏幕坐标的位置在距离mainCamera 500的平面的投影
        返回结果为(..., ..., 495); // 需要加上camera的z轴坐标为-5,
          // 正中间为 (0, 0, 495),往左下为负,往右上为正

    任务2-8:屏幕坐标、世界坐标与视口坐标之间的关联和相互转换

    屏幕坐标转换为视口坐标:camera.ScreenToViewportPoint(Vector3 position);
    视口坐标转换为屏幕坐标:camera.ViewportToScreenPoint(Vector3 position);

    世界坐标转换为视口坐标:camera.WorldToViewportPoint(Vector3 position);
    视口坐标转换为世界坐标:camera.ViewportToWorldPoint(Vector3 position); 
    -- 与屏幕坐标和世界坐标之间的转换相似,只不过多了一步屏幕坐标与视口坐标之间的转换 

    任务2-9:习题

    1. 一个Cube的Transform信息如下:Position(0,2,5), Rotation(0,0,0), Scale(1,1,1)。它的父物体的Transform信息如下:Position(3,0,0), Rotation(0,90,0), Scale(1,1,1)。请问这个Cube的世界坐标为多少?

    2. 实现功能:让游戏物体在世界坐标系中按每秒移动(2,0,0)个单位的速度向右移动,在到达屏幕边界时停止。

    答案:

    1.    父物体的Transform为(3, 0, 0), 围绕y轴旋转90°,即x轴为世界坐标系z轴负方向
      故父物体的局部坐标系的z轴为世界坐标系的x轴正方向
      子物体相对于父物体的局部坐标系的坐标为(0, 2, 5)
      子物体的世界坐标为(3, 0, 0) + (5 ,2 ,-0) = (8, 2, 0)

      对于旋转而言,父物体的Rotation为(0, 90, 0)
      子物体相对于父物体没有旋转,因此子物体的相对于世界坐标的旋转也为(0, 90, 0)

    2.   // 按世界坐标系移动 -- transform.Translate(Vector3.right, Space.World);
      // 每秒移动(2, 0, 0) -- * Time.deltaTime * 2;
      // 到达屏幕边界时停止 -- 判断camera.main.WorldToViewportPoint(transform.position)

    void Update() {
        Vector3 viewportPos = Camera.main.WorldToViewportPoint(transform.position);
        if(viewportPos.x < 1) {
            transform.Translate(Vector3.right * Time.deltaTime * 2, Space.World);
        }
    }

    任务3-1&3-2:向量的定义 && 点和向量的关系

    向量的基本概念
    内容:
      向量的定义
      点和向量的关系
      Unity中的点和向量

    向量的定义:
      向量,也成为矢量,具有大小和方向
      向量的大小就是向量的长度,也成为模
      向量的方向描述了空间中向量的指向

    向量的书写:
      行向量:水平书写的向量,如 [1,2,3]
      列向量:竖直书写的向量,如 (写不出来)

    通常用x, y来代表2D向量的分量,用x, y, z来代表3D向量的分量
      分别表示向量在对应维度上的有向位移
      (点的分量表示到对应轴的距离)
       

    点和向量的关系:

    点表示一个位置,没有大小、方向的概念
    向量表示一个有向位移,有大小和方向,可以形象化地表示为有向线段

    求向量的值:终点坐标 - 起点坐标

    任务3-3:Unity中的点和向量

    在Unity中,只有Vector2, Vector3类,统一表示点和向量

    比如:
      transform.Translate(Vector3),此处为向量
      transform.forward,此处为向量
      camera.WorldToScreenPoint(Vector3),此处为点
      transform.position,此处为点

    计算两点之间的距离时,即为计算以两点为起点终点的向量的大小

    任务3-4:习题

    1. 一个向量的长度为4,它的起点A的坐标为(1,0),终点B的坐标(x,0),求x的值,并写出这个向量。

    2. 实现功能:让一个游戏物体匀速移动到另一个游戏物体所在的位置。

    答案:

    1.    向量可以表示为终点减起点,即(x,0) - (1,0) = (x-1, 0)
      (x-1, 0)的长度为4,x-1=4,即x=5,向量为(4, 0)

      注意,(x-1, 0)的长度为4的另一种情况是 x-1=-4,此时x=-3,向量为(-4, 0)

    2.   // 移动功能
      transform.Translate((other.position - transform.position) * Time.deltaTime * speed, Space.World);
      我向你移动,向量方向为我指向你,终点减起点,故为other.position - transform.position
      // 到位置后的判断
      if(Vector3.Distance(other.position, transform.position) > 0.01f;

    Vector3 startPos = transform.position;
    Vector3 endPos = other.position;
    Vector3 offset = endPos - startPos;
    
    if(Vector3.Distance(transform.position, endPos) > 0.01f {
        transform.Translate(offset * Time.deltaTime * speed, Space.World);
    }

    任务4:向量的运算
    4-1&4-2&4-3&4-4:零向量、负向量、向量长度、向量与标量的乘法/除法、单位向量
    &4-5:向量的加减法

    零向量:唯一一个大小为零的向量,唯一一个没有方向的向量
      (0, 0) 或 (0, 0, 0)
      在Unity中用Vector3.zero表示3D零向量

    负向量:每个向量都有一个负向量
      定义:一个向量和它的负向量相加等于零向量
        与原向量大小相等,方向相反 (即原向量乘以标量-1)
        将向量的每个分量都变负即可

    向量的长度:
      即向量的大小/ 向量的 |AB|
      如何求长度:向量各分量平方和的平方根 

      在Unity中,vector3.magnitude即为向量的长度
        vector3.sqrMagnitude为向量长度的平方
        Vector3.Distance(A, B)可以计算两点之间的长度,即向量(A-B)或(B-A).magnitude

    向量与标量的乘法/除法:

    把向量的每个分量分别与标量相乘/相除即可
      注意除法的前提是非零标量

    几何含义:重复进行相同的有向位移

    Unity中使用*来计算向量与标量的乘法,用/来计算向量与标量的除法

    单位向量/ 标准化向量:
      
    大小为1的向量
      用处:在只关心向量方向,无所谓大小时,可以使用单位向量

      归一化 Normalization:对于任意非零向量,求其单位向量的过程
        求得长度length后,用向量除以标量length即可求得它的单位向量

      在Unity中可以使用vector3.Normalize()来归一化向量 -- 原向量变成归一化后的单位向量
        使用vector3.normalized 来获得该向量对应的单位向量 -- 原向量不变
        例子:
          vector.Normalize();
          Vector3 new = vector.normalized;

    向量的加法和减法:

    两个向量的加减法前提:两个向量的维度相同

    如何加减:将向量的各个对应分量进行相加/相减

    与标量相同
      加法满足交换律:a + b = b + a
      减法不满足交换律:a - b = -(b - a)

    几何角度看:
      向量加法是向量的拼接
      向量减法是向量与另一个向量的负向量相加

      

    在Unity中,向量的加法用+表示,减法用-表示

    任务4-6&4-7:向量的点积

    向量的点积/ 内积

    向量与向量的乘法有两种方式
      一种是点积,也成为内积,表示为 a·b

    点积计算公式:对应分量乘积的和,结果为标量
      a·b=(ax,ay)·(bx,by)=axbx+ayby
      比如:(0,2)·(3,3) = 0×3+2×3 = 0+6 = 6

    点积满足交换律:
      a·b = b·a

    点积计算公式2:通过两个向量之间夹角的cos值
      a·b = |a| |b| cosθ 

    点积的几何含义以及应用:
      1. 点积的结果越大,表示两个向量越相近 -- 从cos的函数可知
      2. 常被用来计算两个向量的夹角大小

    3. 通过向量的点积的符号来判断两个向量的位置 (方向)关系
      cos函数正负 -- 
        >0 : 夹角 0~90,方向基本相同
        =0 : 夹角 =90,方向垂直
        <0 : 夹角 90~180,方向基本相反

    4. 通过点积求得一个向量在另一个向量上的投影

     

      向量b在向量a上的投影的长度可以表示为:
        
        (解释: a·b = |a| |b| cosθ)

      向量b在向量a上的投影可以表示为:

        
        (解释: p的方向与a的单位向量 a/|a|相同,因此 p=a/|a| * |p|
            |p| = a·b/ |a| --> p = (a / |a|) * (a·b / |a| ))
            a·a = |a| * |a|, 因为同方向,cos = 1
            注意向量和标量)

    在Unity中,用Vector3.Dot(v1, v2)来计算两个向量的点积(返回值是float标量)
      用Vector3.Angle()来计算两个向量之间夹角的大小(结果在0~180之间)

    任务4-8:向量的叉积

    向量的叉积/ 外积

    表示为 a×b

    叉积的结果是一个向量

    叉积仅可应用于3D向量
      a×b=(ax,ay,az)×(bx,by,bz)=(aybz-azby,azbx-axbz,axby-aybx)
        记忆:(除去x,除去y,除去z);(+,-,+);
      例:(1,2,3)×(-2,-2,3)=(6-(-6),(-6)-3,(-2)+4)=(12,-9,2)

    叉积的大小 (长度):向量的大小与向量夹角sin值的积
      即 |a x b| = |a| |b| sinθ,也等于以a和b为两边的平行四边形的面积大小

    几何含义:
      1. 叉积得到的向量 垂直于 原来的两个向量
        可以通过向量的叉积得到两个向量所在平面的法向量
      2. 叉积的方向可以用来判断b是在a的顺时针方向或逆时针方向
        Unity为左手坐标系,若求得叉积的方向向上,则为顺时针旋转

    在Unity中,通过Vector3.Cross(v1, v2) 来计算向量的叉积

    任务4-9:习题

    1.向量(1,1)是单位向量吗?如果不是的话,将它归一化。

    2.计算点(-1,3,2)与点(-2,3,-1)之间的距离。

    3.已知向量a的长度为2,向量b的长度为3,夹角为60度。求a和b的点积和叉积的长度。

    4.已知Unity中有2个向量a=(1,2,3),b=(-2,-2,3),请问b在a的顺时针方向还是逆时针方向?

    答案:

    1. |(1, 1)| = sqr(12 + 12) = sqr(2) != 1,不是单位向量
      (1, 1).normalized = (1/sqr(2), 1/sqr(2))

    2. v = (-2,3,-1) - (-1,3,2) = (-1,0,-3)
      v.magnitude = sqr(12+32) = sqr(10)
      或Vector3.Distance((-1,3,2), (-2,3,-1))

    3. |a| = 2, |b| = 3
      cos = 1/2 --> a·b = |a| |b| cos = 3
      sin = sqr(3)/2 --> axb = |a| |b| sin = 3*sqr(3)

    4. 通过叉积的方向判断
      a x b = (+(2*3-3*-2), -(1*3-3*-2), +(1*-2-2*-2)) = (12, -9, 2)
      向下,Unity为左手坐标系,故a到b为逆时针方向旋转,b在a的逆时针方向

    任务5:矩阵
    任务5-1&5-2:定义、矩阵和矩阵的加减法、矩阵和标量的乘法

    矩阵内容:
      矩阵的定义
      矩阵和矩阵的加减法
      矩阵和标量的乘法
      矩阵和向量的乘法
      矩阵和矩阵的乘法
      特殊矩阵

    矩阵的定义:
      矩阵是一个按照长方阵排列的复数或实数的集合

    矩阵的维度:矩阵有几行几列
      比如 r x c矩阵:有r行、c列
      (向量的维度是看该向量有几个分量)

    在一个m x n的矩阵中,有m*n个数,这些数为矩阵的元素
      aij位于矩阵的第i行第j列,成为矩阵的(i, j)元
      注:下标 ij 从1开始

    Unity中,
      通过Matrix4x4.SetRow()和Matrix4x4.SetColumn()来设置4x4矩阵的某行或某列
      通过Matrix4x4.GetRow()和Matrix4x4.GetColumn()来获取4x4矩阵的某行或某列
      注意:这里的index从0开始
      -- 具体见任务6

    public Matrix4x4 matrix;
    matrix.SetRow(0, new Vector4(1,0,0,0));
    matrix.SetRow(1, new Vector4(0,1,0,0));
    matrix.SetRow(2, new Vector4(0,0,1,0));
    matrix.SetRow(3, new Vector4(0,0,0,1));
    Debug.Log(matrix);
    Vector4 row3 = matrix.GetRow(3);
    Debug.Log(row3);

    矩阵和矩阵的加减法:

    加减法的前提:矩阵的维度相同

    两个矩阵的相加/相减即:矩阵各相同位置的元素的相加/相减

    在Unity中,没有矩阵之间加减的运算符,一般不会做矩阵和矩阵的加减法

    矩阵和标量的乘法:

    矩阵和标量相乘,得到的是一个相同维度的矩阵

    规则:矩阵的每个元素和该标量的乘积

    任务5-3:矩阵和向量的乘法

    向量可以被当做是一行或一列的矩阵(行向量/列向量)
      因此矩阵和向量的乘法可以看成是矩阵和矩阵的乘法

    矩阵和矩阵相乘的前提:第一个矩阵的列数 = 第二个矩阵的行数
      r x n矩阵 * n x c矩阵 = r x c矩阵
      --> 一般情况下:行向量 * 矩阵,得到的还是行向量 1 x n
              矩阵 * 列向量,得到的还是列向量,n x 1

    乘法规则:
      矩阵和向量相乘的结果向量中,每个元素都是原向量和矩阵中单独行或者列的点积


    在Unity中,一般会将向量转换为矩阵后,再与矩阵相乘

    任务5-4:矩阵和矩阵的乘法

    矩阵A和矩阵B相乘,得到的矩阵C中的任意元素Cij等于A的第i行向量与B的第j行向量的点积

    例:

    任务5-5:单位矩阵

    特殊矩阵:

    方块矩阵
    对角矩阵
    单位矩阵
    转置矩阵
    逆矩阵

    方块矩阵:行数 = 列数,即 n x n矩阵
      在方块矩阵中,行号=列号的元素成为对角元素

    对角矩阵:前提是方块矩阵
      除了对角元素之外的所有元素都为0

    单位矩阵:前提是对角矩阵
      当对角矩阵的对角元素全为1

    单位矩阵的性质:
      任意矩阵乘以单位矩阵,都将得到原矩阵
      A I = A;  I A = A;

    在Unity中
      通过 Matrix4x4.zero得到一个4x4的所有元素都为0的矩阵
      通过 Matrix4x4.identity得到一个4x4的单位矩阵
      通过 matrix4x4.isIdentity来判断一个矩阵是否为单位矩阵

    任务5-6:转置矩阵

    转置矩阵是对原矩阵进行转置运算后得到的矩阵

    r x c的矩阵进行转置后,得到一个 c x r的矩阵。
    转置运算即将原矩阵的第i行变为第i列,第j列变为第j行 -- 沿对角线翻折

    在Unity中,通过Matrix4x4.Transpose(matrix) 得到矩阵matrix的转置矩阵

    任务5-7&5-8:逆矩阵

    逆矩阵:一个矩阵A和它的逆矩阵B满足 AB = I
      即 AA-1 = I  或  A-1A = I  或  AA-1 = A-1A = I

    逆矩阵的前提:只有方块矩阵才可能有逆矩阵

    可逆/ 非奇异:一个矩阵有相应的逆矩阵,则这个矩阵是可逆的/ 非奇异的
    不可逆/ 奇异:一个矩阵没有相应的逆矩阵,则这个矩阵是不可逆的/ 奇异的

    如何判断可逆:
      如果一个矩阵的行列式不为0,则是可逆的

    行列式的计算方法:

    二阶行列式的计算方法:对角线的乘积相减
      

    三阶行列式的计算方法:代数余子法
      行列式可以按某一行或某一列展开成元素与其对应的代数余子式的乘积之和

      行列式某元素的余子式:
        行列式删去该元素所在的行和列的各元素,由剩下的元素组成的新的行列式

        

      行列式某元素的代数余子式:
        行列式某元素的余子式与该元素对应的正负号的乘积

        正负号:

        

         

    例子:

     

      这里选择的是第一行元素的代数余子式之和
      下面通过第一列元素求行列式

        

      因为行列式为0,该矩阵不可逆,是奇异的

    逆矩阵的求解:

    4x4的逆矩阵求解: 


      |A| 表示A的行列式
      a11c表示a11的代数余子式
      注意:是转置矩阵

    例子:

    行列式的计算 |A| :

    逆矩阵的求解:

    四阶矩阵的逆矩阵更加复杂,就不说明了

    在Unity中,通过Matrix4x4.Inverse(matrix)得到一个矩阵的逆矩阵
      如:Matrix4x4 matrix1 = Matrix4x4.Inverse(matrix);  // 得到逆矩阵A-1
        Matrix4x4 matrix2 = matrix * matrix1;  // AA-1
        Debug.Log(matrix2.isIdentity); // AA-1 = I;输出true

    任务5-9:习题

    1.判断下列运算是否合理,合理的话计算结果。
      (1) 

      (2)

    2.已知矩阵A,(1)求矩阵A的转置矩阵,(2)如果A有逆矩阵的话,求A的逆矩阵。
      A=

    答案:

    1. (1) 合理,2x2 2x2
      [ 2*1+1*2  2*4+1*6]       [4  14]
      [ 3*1+2*2  3*4+2*6]  =  [7  24]

       (2) 不合理,3x3 2x1

    2. AT=   [4  -1  0]
        [1   2   1]
        [0   3  -1]

     |A|,以第一行元素为标准
      = 4 * | 2  3 | - 1 * | -1  3 | + 0
          | 1  -1 |    | 0  -1 |
      = 4*(-5) - 1*(1)
      = -21
      != 0 --> 可逆

       求逆矩阵A-1
      

    任务6:变换
    任务6-1:齐次变换

    变换内容:
      齐次变换
      2D变换
      3D变换
      Unity中的变换

    齐次变换:
      将一个原本是n维的向量用一个n+1维的向量来表示
        (x,y,z) -> (x,y,z,1)

    优点:在投影几何中,笛卡尔坐标对变换来说不方便
      而笛卡尔坐标是适用于欧式几何中的

      齐次坐标系是计算机图形学的重要手段之一
      既能用来明确区分向量和点,同时也易于进行仿射几何变换

    详解:

    对于标准的空间直角坐标系:基向量是a=(1,0,0) b=(0,1,0) c=(0,0,1),原点为O
      坐标系中任意一个向量v(vx,vy,vz)的坐标都可以用基向量来表示v=vxa+vyb+vzc
      坐标系中任意点的位置(px,py,pz),可以视为原点的平移,需要考虑原点位置O,
        表示为P=pxa+pyb+pzc+O

    而上面的表示可以用矩阵的形式写出:
       

    任意3D坐标系可以表示为:

      

      基向量a=(ax,ay,az), b=(bx,by,bz), c=(cx,cy,cz), 原点坐标(Ox,Oy,Oz)
      因为是点,所以右下方为1

    点的齐次坐标表示为 (x, y, z, 1)
    向量的齐次坐标表示为 (x, y, z, 0)

    用齐次坐标进行运算
      点与点之间的减法,得到一个向量 -- 起点指向终点的向量
      向量与向量之间的加减法,得到一个向量 -- 向量的叠加
      点和向量之间的加法,得到一个点 -- 点的平移

    任务6-2:仿射变换

    仿射变换:包括了线性变换平移

    代数上:线性变换指可以保留矢量加和标量乘的变换
      需要满足这两个条件:
        -- v和u是矢量,s是标量

    几何上理解,线性变换需要满足的条件:
      1. 变换前是直线的,变换后依然是直线
      2. 线段长度的比例保持不变
      3. 变换前是原点的,变换后依然是原点

    旋转、缩放都满足线性变换的这三个要求
    但是,平移满足了1、2,不满足要求3

    而因为仿射变换包括了线性变换和平移
      因此可得:平移、旋转、缩放都属于仿射变换

    仿射变换的几何角度的要求是:
      1. 变换前是直线的,变换后依然是直线
      2. 线段长度的比例保持不变

    仿射变换可以用矩阵和向量相乘的方式来表示
      计算一个点或者一个向量,经过平移、旋转、缩放等仿射变换后的值

    一个三维的变换可以表示为:
       -- 前面的矩阵成为变换矩阵,c为点或向量的值
      变换矩阵的前三列用来表示旋转和缩放,又因平移不是线性变换,因此最后一列表示平移

    任务6-3&6-4&6-5:2D变换(平移变换、缩放变换、旋转变换)

    2D平移矩阵:

      
      最后结果为[px,py,1] + [tx,ty,0] -- 即为点加向量的形式

    2D缩放矩阵:

    --> 可以求得最后的形式为 s * [px, py, 1],即缩放比例乘以点坐标

    2D旋转矩阵:

    例子:把点(3,2)围绕O点逆时针旋转90°


    注意,这里面x->y角度为正

    推导:


      

    任务6-6:2D变换的逆矩阵

    平移矩阵的逆矩阵:

       -- 平移 (-tx, -ty)的距离

      解释:

    缩放矩阵的逆矩阵:

       -- 缩放 (1/sx, 1/sy) 倍数即可

    旋转矩阵的逆矩阵:

       -- 往反方向旋转相同度数即可

    解释:

    任务6-7:物体变换和坐标系变换

    有些时候的变换是物体在变换,有些时候的变换是变换了一个坐标系


      F为坐标系,M为变换矩阵,c为点坐标或向量
      如果是物体变换,则可写为 F(Mc) -- 得到在F中c'的值
      如果是坐标系变换,则可写为 (FM)c -- 得到F'的值

    物体变换:

    --[b1, b2, b3, O] 表示坐标系,bn分别表示基向量,O表示原点

    例子:

     

    坐标系变换:

    -- 将变换应用到坐标系,得到新坐标系[b1', b2', b3', O']

    例子: 


      将坐标系i变换M --> 得到坐标系i'
      P点坐标不变,仍为[2,2],但因为所在坐标系变了,因此P点位置变了

    但是不管是什么变换,不管是物体变换还是坐标系变换:(FM)c = F(Mc)

    任务6-8:复合变换

    复合变换:将平移、旋转、缩放结合起来的复杂的变换过程

    因为矩阵乘法不满足交换律,因此变换矩阵的先后顺序很重要
      (画外音:结合律是满足的,比如上一节所说的FMc的例子)
      比如先旋转再移动,和先移动再旋转,结果是不一样的

    例子:

    c: (2,2) 在标准笛卡尔坐标系中,先平移 (-2,1), 再旋转90°

     

    法1 -- 物体变换:
      

    法2 -- 坐标系变换:

    蓝色为F,黑色为F',紫色为F''

    任务6-9:3D变换

    3D平移矩阵:

    3D缩放矩阵:

    3D旋转矩阵:

     

      绕着a轴旋转,则a轴的值不会发生变化,比如绕着x轴旋转,则第一行第一列不变
      2D旋转可以视为在3D中绕着z轴旋转 -- 右手坐标系
      绕x轴旋转--y到z正方向; 绕z轴旋转--x到y正方向; 绕y轴旋转--x到z负方向 --> 正负号

    任务6-10:Unity中的变换

    Matrix4x4.Translate(Vector3 translation) -- 返回一个平移矩阵

    Matrix4x4.Rotate(Quaternion rotation) -- 返回一个旋转矩阵

    Matrix4x4.Scale(Vector3 scaler) -- 返回一个缩放矩阵

    Matrix4x4.TRS(Vector3 t, Quaternion r, Vector3 s)--返回一个移动+旋转+缩放的复合矩阵

    matrix4x4.MultiplyPoint(vector3) -- Transforms a position by this matrix (Generic)
    matrix4x4.MultiplyPoint3x4(vector3) -- (Fast)
      Returns a position v transformed by the current fully arbitrary matrix.
      If the matrix is a regular 3D transformation matrix, it is much faster to
      use MultiplyPoint3x4 instead. 
      MultiplyPoint is slower, but can handle projective transformations as well.

    public class ExampleClass : MonoBehaviour {
        public float rotAngle;
        public float stretch;
        private MeshFilter mf;
        private Vector3[] origVerts;
        private Vector3[] newVerts;
        void Start() {
            mf = GetComponent<MeshFilter>();
            origVerts = mf.mesh.vertices;
            newVerts = new Vector3[origVerts.Length];
        }
        void Update() {
            Quaternion rot = Quaternion.Euler(rotAngle, 0, 0);
            Matrix4x4 m = Matrix4x4.TRS(Vector3.zero, rot, Vector3.one);
            Matrix4x4 inv = m.inverse;
            int i = 0;
            while (i < origVerts.Length) {
                Vector3 pt = m.MultiplyPoint3x4(origVerts[i]);
                pt.y *= stretch;
                newVerts[i] = inv.MultiplyPoint3x4(pt);
                i++;
            }
            mf.mesh.vertices = newVerts;
        }
    }

    遍历Mesh上的所有点,分别进行TRS变换 -- 物体位置没有变化,但Mesh的显示变化了

    matrix4x4.MultiplyVector(Vector vector) -- 将一个向量按照matrix4x4进行变换

    任务6-11:习题

    1. 将一个游戏物体先进行大小为(2,3,3)的缩放,再绕y轴旋转60度,最后再移动(-1,2,5)个单位,写出这个复合变换的变换矩阵(不用计算)。

    答案:

    Ts = [ 2  0  0  0 ]
       [ 0  3  0  0 ]
       [ 0  0  3  0 ]
       [ 0  0  0  1 ]
    Tr = [cos60   0  sin60  0 ]
       [   0       1     0    0 ]
       [-sin60  0  cos60 0 ]
       [   0       0     0     1 ]

    Tt = [ 1  0  0  -1 ]
       [ 0  1  0  2  ]
       [ 0  0  1  5  ]
       [ 0  0  0  1  ]

    TRS = Tt Tr Ts pos

     

     

  • 相关阅读:
    [图解算法] 最短路径算法之 “Dijikstra”
    [前端随笔][CSS] 伪类的应用
    [前端随笔][JavaScript] 实现原生的事件监听<Vue原理>
    [图解算法] 最短路径算法之 “Floyd”
    [前端随笔][JavaScript][自制数据可视化] “中国地图”
    [前端随笔][JavaScript] 懒加载的实现(上划一次加载一部分)
    [前端随笔][CSS] 制作一个加载动画 即帖即用
    ThinkPHP下隐藏index.php以及URL伪静态
    PHP基础语法3
    PHP基础语法2
  • 原文地址:https://www.cnblogs.com/FudgeBear/p/8869241.html
Copyright © 2011-2022 走看看