[Math]两个旋转矩阵(Rotation Matrix)相乘(Multiply)的几何意义
keywords:Math, 线性代数, 旋转矩阵,相乘, Linear Algebra, Rotation Matrix, Multiply
概述
讲之前,先说下如果两个Rotation相加的意义,比如:
FRotator Rot1(0.f, 90.f, 0.f);
FRotator Rot2(90.f,0.f, 0.f);
FRotator Result = Rot1 + Rot2;
得到的结果FRotator Result(90.f, 90.f, 0.f),其意义是: 物体相对空间坐标原点的Rotation为(90.f, 90.f, 0.f),很好理解。
如果两个Rotation转换为Martix并相乘:
UE4
FRotator Rot1(0.f, 90.f, 0.f);
FRotator Rot2(90.f,0.f, 0.f);
FRotator Result = (FRotationMatrix(Rot1) * FRotationMatrix(Rot2)).Rotator();
Unity 方式1:
Quaternion Rot1 = Quaternion.Euler(0, 0, 90);
Quaternion Rot2 = Quaternion.Euler(90, 0, 0);
Quaternion RotNew = Rot1 * Rot2;
Vector3 Result = RotNew.eulerAngles;
Unity 方式2:
Quaternion Rot1 = Quaternion.Euler(0, 0, 90);
Quaternion Rot2 = Quaternion.Euler(90, 0, 0);
Matrix4x4 Mat = Matrix4x4.Rotate(Rot1) * Matrix4x4.Rotate(Rot2);
Vector3 Result = Mat.MultiplyVector(Vector3.forward);
得到的结果Result(0.f, 90.f, 90.f)
,其意义是:
先将物体作Rot1旋转,即:Yaw方向(水平平面)旋转90度,然后再假设该物体相对坐标轴原点的旋转量为(0, 0, 0),即没有作任何旋转,但实际Rotation相对坐标轴原点为(0, 90, 0);然后再将物体进行Rot2旋转,即Pitch方向(垂直于(90, 0, 0)方向的平面)侧翻90度,因为侧翻90度前假设物体的Rotation是(0, 0, 0),所以侧翻时所在的平面不再是Yaw=90的平面(垂直于(0, 90, 0)方向),而是Yaw=0的平面(垂直于(90, 0, 0)方向)。没做相关配图,这段话理解起来有点绕,最好用空间思维想象下,可以用手掌比划。
实际应用:
比如空间中有两个物体:A和B,现在要将A旋转至与B相同的朝向,目前只知道A的相对世界坐标的Rotation Rw(90.f,0.f, 0.f)、B相对A(将A的Rotation当做(0, 0, 0),即局部坐标系)的Rotation Rr(0.f, 90.f, 0.f),求A旋转后的世界坐标Rotation。
此时的计算公式就是:
(FRotationMatrix(Rr) * FRotationMatrix(Rw)).Rotator()
注意:矩阵相乘时,两个乘数的前后位置不同则计算的结果也不同,比如上面例子,如果是(FRotationMatrix(Rot2) * FRotationMatrix(Rot1)).Rotator()
,则结果是Rotation(90, -90, -180)
。
一个典型应用:当角色移动时,不能将默认MoveForward
的实参(比如(1.0, 0.f, 0.f)
)和MoveRight
的实参(比如(0.0, 1.f, 0.f)
)传递给AddMovementInput
,因为InputValue需要相对摄像机的朝向来计算,否则当按下W键,期望角色摄像机正对方向移动,但实际是侧向移动。此时就可以通过旋转矩阵相乘来获取当前摄像机朝向方向下的MoveForward
和MoveRight
方向。
UE4:
FRotator AMyPlayerController::GetInputRotationInWorld()
{
FRotator Ret = FRotator::ZeroRotator;
if (AMyCharacter* Player = Cast<AMyCharacter>(GetPawn()))
{
if(UCameraComponent* CameraComp = Player->GetFollowCamera())
{
FRotator InputRotLS = FVector(ForwardInputValue, RighInputInputValue, 0.f).Rotation();
FRotator CameraRotWS = FRotator(0.f, CameraComp->GetComponentRotation().Yaw, 0.f);
Ret = (FRotationMatrix(InputRotLS) * FRotationMatrix(CameraRotWS)).Rotator();
}
}
return Ret;
}
Unity:
public void FixedUpdate()
{
if (variableJoystick.Horizontal == 0f && variableJoystick.Vertical == 0f)
{
return;
}
Vector3 DireLS = new Vector3(variableJoystick.Horizontal, 0f, variableJoystick.Vertical);
Quaternion RotLS = Quaternion.LookRotation(DireLS, Vector3.up);
//TestObj为临时测试对象,用于复制Camera的Transform,因为unity不支持new Transform()。
Transform CamRotWS = TestObj.transform;
Transform TraWS = CameraComp.transform;
CamRotWS.localPosition = TraWS.localPosition;
CamRotWS.position = TraWS.position;
CamRotWS.localRotation = TraWS.localRotation;
CamRotWS.rotation = TraWS.localRotation;
CamRotWS.localScale = TraWS.localScale;
//如果不是飞行类游戏,需要将Camera Rotation的Roll和Pitch清零,因为此时只需要Yaw
CamRotWS.localEulerAngles = new Vector3(0, CamRotWS.localEulerAngles.y, 0);
Matrix4x4 RotLSMat = Matrix4x4.Rotate(RotLS);
Matrix4x4 RotWSMat = Matrix4x4.Rotate(CamRotWS.localRotation);
Matrix4x4 MoveDirectionMat = RotLSMat * RotWSMat;
Vector3 MoveDirection = MoveDirectionMat.MultiplyVector(Vector3.forward);
rb.AddForce(MoveDirection * speed * Time.fixedDeltaTime, ForceMode.VelocityChange);
}
Video Tutorials
Linear transformations and matrices | Essence of linear algebra, chapter 3
https://www.youtube.com/watch?v=kYB8IZa5AuE
风起于青萍之末,浪成于微澜之间。 ----先秦·宋玉 《风赋》