unity变换计算
系统库介绍
-
核心库
- GeometryUtility
-
插件库
- Unity.Mathematics
这里面包含了很多变换计算,可以参考 matrix.cs 中的 float4x4
float4x4 可以跟 Matrix4x4 隐式转换,参考 math_unity_conversion.cs
- Unity.Mathematics
向量
Vector3.up 是 y 轴正向 , Vector3.right 是 x 轴正向,Vector3.forward 是 z 轴正向
-
点积(Dot)
Vector3.Dot(a,b) = |a||b|cosθ
可用来检测2个向量是否同方向- Vector3.Dot(a,b) == 0 表示a跟b垂直
- Vector3.Dot(a,b) > 0 表示a跟b同方向,也就是夹角小于90
- Vector3.Dot(a,b) < 0 表示a跟b反方向,也就是夹角大于90
-
叉积(Cross)
|Vector3.Cross(a,b)| = |a||b|sinθ 0 表示 a 平行 b
叉积得到的向量与原来2个向量构成的平面垂直,并且遵循左手法则
即手掌从a转向b(要取角度最小的方向转)握紧,大拇指方向就是叉积的方向
可用来获取平面的法向量- Vector3 normal = Vector3.Cross(a,b).Normalized();
可用来判断向量平行 - Vector3.Cross(a,b).magnitude == 0 说明向量平行
可用来判断向量的位置关系,假设 a,b 位于平面p上,p的法线量是n(代表平面上方向) - var d = Vector3.Dot(Vector3.Cross(a,b),n);
- d == 0 表示 a 跟 b 平行
- d > 0 表示 a 在 b 左侧
- d < 0 表示 a 在 b 右侧
- Vector3 normal = Vector3.Cross(a,b).Normalized();
-
计算2个向量的夹角
// 获得夹角
float angle = Vector3.Angle(fromVector, toVector);
// 获得法线向量
Vector3 normal = Vector3.Cross(fromVector, toVector);
// 获得夹角的正负值
angle *= Mathf.Sign(Vector3.Dot(normal, upVector));
四元数操作
-
欧拉角转换
在unity中欧拉角是按照 yxz 的顺序进行旋转的
绕y轴旋转 degree 度
Quaternion rot = Quaternion.Euler(0, degree, 0);
等价于
Quaternion q = Quaternion.AngleAxis(degree, Vector3.up); -
旋转叠加
q=qa*qb; // 顺序是有影响的,先旋转qb再旋转qa -
相乘顺序
q=qa * qb * qc=( qa * qb ) * qc=qa * ( qb * qc ) -
相乘求反
( qa * qb ).inverse = qb.inverse * qa.inverse; -
*=的运算顺序
a *= b * c; // 等价于 a = a * ( b * c ); -
向量旋转
通过 rotation 获得当前向上向量,本质上就是把向上向量旋转 rotation
Vector3 newUp = rotation * Vector3.up;
其它方向也是一样的
可以直接参考 Transform.up -
绕轴旋转
Quaternion q = Quaternion.AngleAxis(angle,axis)
表示的意义是左手法则: 左手大拇指跟旋转轴同向,则握拳的方向就是旋转的方向 -
旋转到向量
比如向上旋转到 newUp ,刚好是上面向量旋转的逆操作,则旋转角度为
Quaternion q = Quaternion.FromToRotation(Vector3.up, newUp); -
通过2个方向获得当前旋转
比如 通过 forward 和 upwards 确定方向// 这里 forward 和 upward 不要求垂直,这里保证计算出的方向 forward 对齐 // 因为实际上是 left = upward*forward,用 forward 和 left 来计算旋转 // Z 轴将与forward对齐,X 轴与 forward 和 upwards 的差积对齐 Quaternion rotation = Quaternion.LookRotation(forward, upward); // 如果要保证 upward 对齐,有3种方法(摘自 QuaternionUtil) // 1. 把要精准对齐的当成 forward,另一个当成 upward,用 Quaternion.LookRotation 算出方向后,再把坐标轴还原 public static Quaternion LookRotationYZ(Vector3 up, Vector3 forward) { return Quaternion.LookRotation(up, -forward) * Quaternion.AngleAxis(90, Vector3.right); } // 2. 用叉积计算出精准的 forward, 再用 Quaternion.LookRotation 计算出方向 public static Quaternion LookRotationYZ2(Vector3 up, Vector3 forward) { Vector3 left = Vector3.Cross(up, forward); Vector3 forwardNew = Vector3.Cross(left, up); // 重新计算出 forward return Quaternion.LookRotation(forwardNew, up); } // 3. 用平面投影计算出精准的 forward, 再用 Quaternion.LookRotation 计算出方向,参考 LookRotationYZ3 public static Quaternion LookRotationYZ3(Vector3 up, Vector3 forward) { Plane plane = new Plane(up, Vector3.zero); Vector3 forwardNew = plane.ClosestPointOnPlane(forward); // forward 在 up 为法线的平面上的投影 return Quaternion.LookRotation(forwardNew, up); } // 应用举例,比如 场景确定好坐标和向上方向后,要朝向相机 void AdjustSceneTransform(Transform sceneTransfrom, Vector3 pos, Vector3 upword, Vector3 cameraPos ) { Vector3 forward = pos - cameraPos; Vector3 left = Vector3.Cross(upword, forward); forward = Vector3.Cross(left, upword); Quaternion rotation = Quaternion.LookRotation(forward, upword); sceneTransfrom.position = pos; sceneTransfrom.rotation = rotation; }
矩阵操作
-
M行N列矩阵的存储(展开成一维数组)有2种方式:
- 行优先: 先存储第一行,再存储第二行,依次类推,i行j列元素=i*N+j
- 列优先: 先存储第一列,再存储第二列,依次类推,i行j列元素=j*M+i
2种存储方式互为转置关系: M行优先=M列优先.transpose
-
unity 采用的是方案2,也就是列优先,这点在 Matrix4x4 文档中有说明
0 => m00, 1 => m10, 2 => m20, 3 => m30, 4 => m01, 5 => m11, …
平移存储在最后一列,也就是 m.GetColumn(3) ,也就是 m03, m13, m23,也就是 [12] [13] [14]
因此跟 Vector4 相乘时是 M*V ,也就是M在前,V在后,V写成4行1列 -
相乘
m = a * b; // 先进行 a 变换再进行 b 变换 -
相乘求逆
(a * b)-1 = b-1 * a-1 -
TRS
m = Matrix4x4.TRS(t,r,s) = Matrix4x4.Translate§ * Matrix4x4.Rotate® * Matrix4x4.Scale(s);
也就是先平移再旋转最后缩放 -
从矩阵获得旋转
直接获取 Quaternion q = m.rotation;
参考 OpenCVForUnity\org\opencv\unity\ARUtils.csVector3 forward = new Vector3(m.02,m.12,m.22); Vector3 upwards = new Vector3(m.01,m.11,m.21); Quaternion q = Quaternion.LookRotation(forward, upwards);
-
矩阵和Pose的互转
Matrix4x4 m = Matrix4x4.TRS(pose.position,pose.rotation, Vector3.one); // pose转矩阵 pose.position = new Vector3(m.03, m.13, m.23); pose.rotation = m.rotation;
-
变换坐标点
// 对坐标进行变换,等价于 matrix * new Vector4(pos.x,pos.y,pos.z,1) // 只能用于普通的 TRS 矩阵,也就是最后一行是 0,0,0,1 , 不能用于投影矩阵 pos1 = matrix.MultiplyPoint3x4(pos