unity变换计算
向量
Vector3.up 是 y 轴正向 , Vector3.right 是 x 轴正向,Vector3.forward 是 z 轴正向
-
点积(Dot)
Vector3.Dot(a,b) -
叉积(Cross)
Vector3.Cross(a,b) -
判断向量垂直
Vector3.Dot(a,b)=0 >0表示向量夹角<90,<0表示向量夹角>90 -
计算2个向量的夹角
// 获得夹角
float angle = Vector3.Angle(fromVector, toVector);
// 获得法线向量
Vector3 normal = Vector3.Cross(fromVector, toVector);
// 获得夹角的正负值
angle *= Mathf.Sign(Vector3.Dot(normal, upVector));
四元数操作
-
欧拉角转换
// 绕y轴旋转 degree 度
Quaternion rot = Quaternion.Euler(0, degree, 0);
等价于
Quaternion q = Quaternion.AngleAxis(degree, Vector3.up); -
旋转叠加
q=qa*qb; // 顺序是有影响的,先旋转qa再旋转qb -
相乘顺序
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 -
旋转到向量
比如向上旋转到 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 文档中有说明
平移存储在最后一列,因此跟 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.cs
Vector3 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; -
变换坐标点
pos1 = matrix.MultiplyPoint3x4(pos); // 对坐标进行变换,等价于 matrix * new Vector4(pos.x,pos.y,pos.z,1)
// 只能用于普通的 TRS 矩阵,也就是最后一行是 0,0,0,1 , 不能用于投影矩阵
pos1 = matrix.MultiplyPoint(pos); // 对坐标进行变换,并归一化,适用于投影矩阵,普通 TRS 也能用,但可以用速度更快的 MultiplyPoint3x4
pos1 = matrix * new Vector4(pos.x,pos.y,pos.z,1); // 相乘,效果同 MultiplyPoint3x4,不会进行归