[Unity]Quaternionの回転、左が先か？右が先か？


```cs
// X軸で90度回転
var rot1 = Quaternion.AngleAxis(90, new Vector3(1, 0, 0));
// Y軸で90度回転
var rot2 = Quaternion.AngleAxis(90, new Vector3(0, 1, 0));

// Quaternionの回転、左が先か？右が先か？
var rot = rot1 * rot2;
```

## 左が先説

Quaternionの掛け算についてUnity公式には以下の記述があります。

> lhs と rhs の回転を組合わせます。
>
> lhs * rhs の積の値で回転させることは、lhs を最初に、それから rhs というように 2 つの回転を順番に行うことと同じです。つまり、回転は、非可換性で、lhs * rhs は rhs * lhs と異なる回転を表すということに気を付けてください。
> https://docs.unity3d.com/ja/2019.4/ScriptReference/Quaternion-operator_multiply.html より

```cs
// https://docs.unity3d.com/ja/2019.4/ScriptReference/Quaternion-operator_multiply.html より

using UnityEngine;
using System.Collections;

public class Example2 : MonoBehaviour
{
    float rotateSpeed = 90;

    // Applies a rotation of 90 degrees per second around the Y axis
    void Update()
    {
        float angle = rotateSpeed * Time.deltaTime;
        transform.rotation *= Quaternion.AngleAxis(angle, Vector3.up);
    }
}
```

ここには計算順は **左が先、右が後** ということ、回転は非可換で逆にはできないということが記載されています。
しかし計算順は本当に **左が先、右が後** なのでしょうか。

## 右が先説

以下のようにX軸で90度回転してからY軸で90度回転させるにはどうしたらいいでしょうか。

![x-y.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42193/6df0b13d-c300-0c46-d431-81094f334647.gif)

これを実現するためのコードを書くとこのようになります。
(アニメーションはしません)

```cs
// X軸で90度回転
var rot1 = Quaternion.AngleAxis(90, new Vector3(1, 0, 0));
// Y軸で90度回転
var rot2 = Quaternion.AngleAxis(90, new Vector3(0, 1, 0));

// X軸で90度回転してからY軸で90度回転
transform.localRotation = rot2 * rot1;
```

これを見ると右(X軸で90度回転)が先で左(Y軸で90度回転)が後のようです。

## どちらが正しいか

**どちらも正しい** と言えます。
左が先説と右が先説では考え方が違います。

左が先説の場合は回転で **回転軸も同時に回転する** と考えます。
前述の例で説明します。

```cs
// X軸で90度回転
var rot1 = Quaternion.AngleAxis(90, new Vector3(1, 0, 0));
// Y軸で90度回転
var rot2 = Quaternion.AngleAxis(90, new Vector3(0, 1, 0));

transform.localRotation = rot2 * rot1;
```

左から回転すると考える場合、まずオブジェクトをY軸で90度回転します。
この時、回転軸も回転するのでX軸が-Z軸になります。
そしてこの回転後のX軸を基準に90度回転します。

![y-x.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42193/37b8e8a9-ae70-8ad6-918d-9635a759b2eb.gif)


右から左の場合は **回転軸は変わらず常に固定** と考えます。

## どちらから回転を掛ければいいのか

オブジェクトを回転させたい場合、どちらから回転を掛ければいいでしょうか。
答えは **何を実現したいか** によって変わります。
既に回転されたオブジェクトを **元々の回転軸で回転させたい** 場合はオブジェクトの回転を左、掛けたい回転を右にしましょう。

```cs
void Start()
{
    // Z軸で30度回転しておく
    transform.localRotation = Quaternion.AngleAxis(30, new Vector3(0, 0, 1));
}

void Update()
{
    var rotateSpeed = 90;
    var angle = rotateSpeed * Time.deltaTime;
    // 元々のY軸で回転
    var rot = Quaternion.AngleAxis(angle, Vector3.up);
    transform.localRotation = transform.localRotation * rot;
}
```

![right.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42193/84650ce0-be27-735d-4d4e-aa42b52f2d84.gif)

既に回転されたオブジェクトを **固定の回転軸で回転させたい** 場合は掛けたい回転を左、オブジェクトの回転を右にしましょう。

```cs
void Start()
{
    // Z軸で30度回転しておく
    transform.localRotation = Quaternion.AngleAxis(30, new Vector3(0, 0, 1));
}

void Update()
{
    var rotateSpeed = 90;
    var angle = rotateSpeed * Time.deltaTime;
    // 固定のY軸で90度回転
    var rot = Quaternion.AngleAxis(angle, Vector3.up);
    transform.localRotation = rot * transform.localRotation;
}
```

![left.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42193/4a0c365f-5c10-6b22-d865-42d0994f41e3.gif)

## まとめ

どちらが先か、という問題については考え方次第で **どちらにも解釈できる** ということが分かりました。
ただしQuaternionによる回転が **任意の軸の周りでの回転** を表現していることを考えると右から左の方が自然に見えます。

```cs
var pos = new Vector3(0, 0, 1);
var xRot = Quaternion.AngleAxis(90, new Vector3(1, 0, 0));
var yRot = Quaternion.AngleAxis(90, new Vector3(0, 1, 0));

// X軸で90度回転する
var pos2 = xRot * pos;
// Y軸で90度回転する
var pos3 = yRot * pos2;

// X軸で90度回転させてからY軸で90度回転する
// 右から左の順
var pos4 = yRot * xRot * pos;

// pos3とpos4は等しい
```



## 参考

[Quaternionによる3次元の回転変換](https://qiita.com/kenjihiranabe/items/945232fbde58fab45681)
[クォータニオン (Quaternion) を総整理！ ～ 三次元物体の回転と姿勢を鮮やかに扱う ～](https://qiita.com/drken/items/0639cf34cce14e8d58a5)
[Quaternion を完全に理解した | VirtualCast Blog](https://virtualcast.jp/blog/2019/11/quaternion/)