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键,期望角色摄像机正对方向移动,但实际是侧向移动。此时就可以通过旋转矩阵相乘来获取当前摄像机朝向方向下的MoveForwardMoveRight方向。

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


风起于青萍之末,浪成于微澜之间。 ----先秦·宋玉 《风赋》