【转载】Unity中矩阵的平移、旋转、缩放
年代久远,图⽚链接失效了,这⾥重新放⼀个腾讯学院的版本,⾥⾯有图⽚
By:克森
简介
在这篇⽂章中,我们将会学到⼏个概念:平移矩阵、旋转矩阵、缩放矩阵。在学这⼏个基本概念的同时,我们会⽤到 Mesh(⽹格)、数学运算、4x4矩阵的⼀些简单的操作。但由于克森也是新⼿,⽂章的严谨性可能不是很⾼,还请⼤神们多多指教。
创建项⽬
⾸先创建⼀个Unity⼯程,克森把他命名为“Matrix of China”(中国的矩阵),基本配置如下图所⽰:
为了便于查找,让我们在 Asts ⽬录下新建三个⽂件夹,分别命名为“Scripts”、“Shader”、“Materials”,这个不⽤解释,⼤伙们都看得懂吧。如下图所⽰:
cnpm接下来再 Scripts ⽂件夹⾥创建⼀个 C# 脚本,命名为“Triangle”,该脚本⽤于创建⼀个简单的三⾓形。
然后在 Hierarchy ⾯板下创建⼀个空物体,命名为“Triangle”。然后为该物体添加之前创建的“Triangle”脚本,且为该物体添加 Mesh 相关的两个组件,如下图所⽰:
乐知英语怎么样
王长喜好,接下来让我们开始码了个码。
Triangle.cs
swelling⾸先让我们看⼀看完整的代码,然后再⼀步⼀步的分析:
代码解析
相信这段代码对于⼤伙来说都不是很难吧。创建⼀个⽹格的步骤⼀般都是按这个顺便来创建的:
1.为顶点数组赋值
2.为三⾓形数组赋值
在代码中的“vertices”就是所谓的顶点数组、”triangles“就是所谓的三⾓形数组,它的作⽤其实就是对应顶点的索引。⼀般⼀个三⾓形是由三个顶点组成的。
好,让我们回到 Inspector ⾯板修改⼀下“Triangle”脚本的属性,如下图所⽰:
PS:三⾓形数组索引必须从0开始,按顺序然后⾃增 1。且再次强调,三个顶点才能构成⼀个三⾓形。
好,现在让我们点击 Play 进⾏测试⼀下:
Okey,现在我们的三⾓形算是完成了,接下来开始玩弄它了。
脚本中的变换
⾸先创建⼀个 C# 脚本,命名为“MyTransform”,并为我们的“Triangle”物体添加此脚本。代码如下:
代码解析
这段代码很简单,就是声明⼀个 4x4 的矩阵,然后调⽤该 4x4 矩阵的 SetTRS 函数(T 代表 Translate(平移)、R 代表 Rotation(旋转)、S 代表 Scale(缩放))。该函数⽤来设置⼀个平移、旋转、缩放矩阵。在代码中,我们传⼊的是该物体的 position、rotation、localScale,这样做是为了便于观察相应的变换矩阵。
1平移矩阵
这个就是平移矩阵,其中 (Tx,Ty,Tz) 为平移的⽅向向量,有些书上是把 Tx、Ty、Tz 放在第四⾏,当经过克森的测试,Unity的平移矩阵式这样⼦的。下⾯我们就做⼀个简单的测试:
1.修改“Triangle”的 Position 为(1,2,3)
2.点击 Play 按钮,观察⼀下 matrix 属性的变化:
图中画红线的“E03”、“E13”、“E23”正好就是对应上⾯平移矩阵图中的“Tx”、“Ty”、“Tz”,这说明Unity使⽤的正是这种⽅式的平移矩阵。
接下来,我们利⽤平移矩阵做个简单的平移,让我们回到“MyTransform”脚本中添加⼀些代码:
代码解析
之前在 Start 函数⾥的代码注释掉,因为⽂章的后⾯还要⽤到。在代码中,新添加了⼀个 Vector4 类型的变量,这是因为 4x4 的矩阵不能与三维向量相乘。
之后再 Start 函数中初始化了向量和矩阵,变量“v”存放的是当前物体的位置,⽽当前的矩阵为单位矩阵(对于矩阵的乘法和单位矩阵我就不想讲了,请⼤伙⾃⾏百度学习)。
然后找到矩阵中对应的平移的⽅向“Tx”、“Ty”、“Tz”,在代码中,我让物体向 X 轴⽅向平移3个单位、Y 轴⽅向平移4个单位、Z 轴⽅向平移5个单位。
之后执⾏矩阵变换的操作,矩阵的操作如下图所⽰:
最后将计算的结果传给物体的 Position。然后在 Start 函数⾥调⽤⼀下该函数:
PS:记得将“Triangle”物体的 Position 设置为(0,0,0)。现在让我们点击 Play 查看⼀下结果对不对:
好了,值是正确的。⼤家也可以修改⼀下参数玩玩。⾄此,平移矩阵⼤体讲完了,还是有点懵的伙计可以给克森说说。
缩放矩阵
这个就是缩放矩阵,其中“Sx”、“Sy”、“Sz”就是各个轴上的缩放因⼦。缩放矩阵是矩阵表现物体⼤⼩变换的矩阵。如果缩放因⼦⼩于1,表现为物体缩⼩;如果⼤于1,则表现为物体扩⼤,如果等于1则不发⽣变化。
接下来我们做个简单的测试,把取消之前 Start 函数⾥的代码,然后修改“Triangle”物体的 Scale 属性为(1,2,3),点击 Play 查看⼀下结果:
找到对应的各个轴的缩放因⼦,⽬前来说结果是正确的了。
接下来,我们利⽤缩放矩阵做个简单的操作,让我们回到“MyTransform”脚本中添加⼀些代码:
代码解析
这段代码和之前平移的代码差不到哪⾥去。代码⾥也给了注释。主要就是矩阵的缩放操作不⼀样,下⾯弄张图就搞定了,缩放操作相对来说还是蛮简单的。
因此就有:
幼儿动画故事
最后修改⼀下 Start 函数的代码即可:
现在点击 Play 按钮查看⼀下结果是否正确:
Okey,看来结果是正确的。
PS:当各个轴上的缩放因⼦相等时,即:Sx=Sy=Sz 时,则为均匀缩放。
旋转矩阵
上图就是所谓的旋转矩阵。在我们的实践中,我就使⽤沿 x- 轴进⾏旋转做实践即可。
⾸先还是做个简单的测试,让我们修改⼀下脚本,然后将物体的 Rotation参数设置为(45,0,0),最后点击 Play 按钮查看结果,如下图所⽰:
在这⾥我选择了45°⾓,因为 sin45°和cos45° 的值是相等的(虽然在上图中他们的值不相等,但实际上是相等的,⼤伙们可以⽤个 if 语句判断⼀下),让我们看看结果对不对:
the facebook
Okey,看来是正确的。
接下来,我们利⽤旋转矩阵做个简单的操作,让我们回到“MyTransform”脚本中添加⼀些代码:
由于图⽚是拼接的,所有有点别扭
roundtrip
compete代码解析
⾸先声明了⼀个 float 类型的变量“angle”⽤于输⼊需要旋转的⾓度,之后⼜声明了⼀个 emun(枚举) 类型的变量“Axle”,⽤于选择旋转的⽅式。
接下在“MyRotation”函数中初始化了矩阵。接下来的判断语句就是对应各个轴上的旋转(可以与上⾯那张旋转矩阵进⾏对⽐,相信⼤家都能懂吧),在判断语句中主要⽤到了三个函数:Mathf.Sin()、Mathf.Cos()、Mathf.Deg2Rad()。前⾯两个函数⼤家都知道是什么了吧,后⾯那个函数⽤于弧度转⾓度,因为前⾯两个函数接受的是⼀个弧度制的值。
接下来的代码就是做矩阵转为四元数的操作,具体请看下图中的公式:
最后将计算好的值传给物体的 Rotation 即可
All right. 现在让我们回到物体的 Inspector ⾯板中修改“Angle”参数和 Axle 选项,然后点击 Play 按钮进⾏测试即可(在这⾥克森选择的是,绕 X 轴旋转45°):
Perfect. 和我们预期的效果⼀样,⼤伙们可以⾃⾏修改参数进⾏测试⼀番。
最后给⼤家送上⼀个⼩⼯具,只需传⼊⼀个变换矩阵即可帮你完成平移、旋转、缩放的⼯作,然后把值传给物体的 Position、Rotation、Scale:
接下来⽤⼀个例⼦来演⽰这个⼩⼯具怎么使⽤。
回到我们的场景中,找到“Triangle”物体,在 Inspector ⾯板中修改“MyTransform”脚本的“Matrix”属性,如下图所⽰:
红⾊箭头代表平移、绿⾊箭头代表缩放、黄⾊箭头代表旋转,这个变换矩阵表⽰的是:物体在X轴上平移3个单位、Y轴上平移4个单位、Z轴上平移5个单位(position (3,4,5));物体绕着X轴旋转90° (Rotation(90,0,0));物体没有缩放 (1,1,1) 。
然后回到“MyTransform”脚本的Start函数中调⽤⼯具类⾥的⽅法完成变换矩阵的各项操作:
亏损额最后点击 Play 按钮查看结果:
Perfect. 看来和我们预期的效果⼀模⼀样,⼤伙们可以⾃⾏修改参数进⾏测试。
好了,这篇⽂章就到这⾥了。这部分内容也是克森最近刚刚学的,也算是学习笔记吧,如果有什么错误的地⽅还请⼤神们指教指教。
附上代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tringle : MonoBehaviour {
private Mesh mesh;
public Vector3[] vertices;
public int[] triangle;
// U this for initialization
void Start () {
mesh = new Mesh ();
mesh.vertices = vertices;
var meshRender = GetComponent<MeshRenderer> ();
if (meshRender == null)
meshRender = this.gameObject.AddComponent<MeshRenderer> ();
var meshFilter = GetComponent<MeshFilter> ();
if (meshFilter == null)
meshFilter = this.gameObject.AddComponent<MeshFilter> ();
}
// Update is called once per frame
void Update () {
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum Axle
{
X,
Y,
阿喀琉斯英文Z,
}