【C#】3D回転入門 〜クォータニオンの理論と実装〜

## はじめに

3Dグラフィックスやゲーム開発において、物体の回転は基本的かつ重要な処理です。

前回の記事では2次元での座標変換について整理しています。

https://qiita.com/Sakai_Path/items/9f75f163f313155959a1

今回は3次元空間での回転、特に**クォータニオン**について紹介します。

### この記事で学べること
- 3D回転の基本概念
- クォータニオンの理論と実装
- オイラー角との比較とジンバルロックの問題
- 実践的なコード例と最適化手法

## 3D回転の基礎

### 従来の回転表現：オイラー角

オイラー角は3つの角度（X軸、Y軸、Z軸周りの回転）で3次元の回転を表現する方法です。

![1000005354.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2253716/4494f418-0176-e015-ee0f-d829c1501d7c.png)

直感的で理解しやすい反面、以下のような問題があります。

- ジンバルロック：特定の角度で自由度が失われる

![1000005351.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2253716/dc7c68a7-f422-e448-ee31-debe491b2f97.png)

- 回転の順序依存性：XYZの順序が結果に影響する
- 補間が難しい

### クォータニオンとは

クォータニオンは4つの要素（w, x, y, z）を持つ数学的な概念で、3次元空間での回転を表現します。

![1000005352.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2253716/ac6953d6-6765-6e06-3f3d-4a423ec3ce30.png)

:::note info
スカラー部が回転の角度を表し、ベクトル部が回転軸を含む
:::

```csharp
public struct Quaternion
{
    public double W { get; set; }  // スカラー部
    public double X { get; set; }  // ベクトル部（i成分）
    public double Y { get; set; }  // ベクトル部（j成分）
    public double Z { get; set; }  // ベクトル部（k成分）
}
```

## クォータニオンの実装

### 基本的な操作

まず、回転軸と角度からクォータニオンを生成する方法を実装します。

```csharp:C#
public static Quaternion FromAxisAngle(Vector3 axis, double angleInDegrees)
{
    // 単位ベクトル化
    double length = Math.Sqrt(axis.X * axis.X + axis.Y * axis.Y + axis.Z * axis.Z);
    axis = new Vector3(
        axis.X / length,
        axis.Y / length,
        axis.Z / length
    );
    
    double angleInRadians = angleInDegrees * Math.PI / 180.0;
    double sinHalfAngle = Math.Sin(angleInRadians / 2.0);
    double cosHalfAngle = Math.Cos(angleInRadians / 2.0);
    
    return new Quaternion
    {
        W = cosHalfAngle,
        X = axis.X * sinHalfAngle,
        Y = axis.Y * sinHalfAngle,
        Z = axis.Z * sinHalfAngle
    };
}
```

### クォータニオンの合成

2つの回転を合成する場合、クォータニオンの乗算を使用します。

![1000005353.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2253716/48cca6d3-b35e-cdb8-d265-f9e7f70ee4ab.png)

```csharp
public static Quaternion Multiply(Quaternion q1, Quaternion q2)
{
    return new Quaternion
    {
        W = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z,
        X = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y,
        Y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X,
        Z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W
    };
}
```

### 点の回転

クォータニオンを使って点を回転させる実装

```csharp:C#
public Vector3 RotatePoint(Vector3 point)
{
    // 共役クォータニオンの計算
    Quaternion conjugate = new Quaternion
    {
        W = this.W,
        X = -this.X,
        Y = -this.Y,
        Z = -this.Z
    };

    // 点をクォータニオンに変換
    Quaternion p = new Quaternion
    {
        W = 0,
        X = point.X,
        Y = point.Y,
        Z = point.Z
    };

    // 回転の適用: q * p * q'
    Quaternion rotated = Multiply(Multiply(this, p), conjugate);

    return new Vector3(rotated.X, rotated.Y, rotated.Z);
}
```

## パフォーマンスの最適化

### SIMDを活用した実装

.NET Core 3.0以降では、System.Numerics.Vector4を使用してSIMD演算を活用できます。

```csharp:C#
public static Vector3 RotatePointSimd(Vector4 quaternion, Vector3 point)
{
    Vector4 p = new Vector4(point, 0);
    Vector4 q = quaternion;
    Vector4 qConjugate = new Vector4(-q.X, -q.Y, -q.Z, q.W);

    Vector4 result = Vector4.Multiply(
        Vector4.Multiply(q, p),
        qConjugate
    );

    return new Vector3(result.X, result.Y, result.Z);
}
```

### キャッシュ効率の改善

大量の点を回転させる場合、以下のように実装することでキャッシュ効率を改善できます。

```csharp:C#
public void RotatePoints(Vector3[] points, Vector3[] results)
{
    const int batchSize = 1024; // キャッシュラインに合わせたサイズ
    
    for (int i = 0; i < points.Length; i += batchSize)
    {
        int count = Math.Min(batchSize, points.Length - i);
        for (int j = 0; j < count; j++)
        {
            results[i + j] = RotatePoint(points[i + j]);
        }
    }
}
```

## 実践的な使用例

### カメラの制御

カメラの視点制御にクォータニオンを使用する例

```csharp:C#
public class Camera
{
    public Vector3 Position { get; set; }
    public Quaternion Rotation { get; set; }
    
    public void LookAt(Vector3 target)
    {
        Vector3 direction = Vector3.Normalize(target - Position);
        Vector3 up = new Vector3(0, 1, 0);
        
        // 前方ベクトルとアップベクトルから回転を計算
        Vector3 right = Vector3.Normalize(Vector3.Cross(up, direction));
        up = Vector3.Cross(direction, right);
        
        // 回転行列からクォータニオンを生成
        // （実装は省略）
    }
}
```

## まとめ

クォータニオンを使用することで、以下のような利点があります。

- ジンバルロックの回避
- 滑らかな補間が可能
- 数値的な安定性
- 効率的な計算

ただし、以下の点に注意が必要です。

- 直感的な理解が難しい
- デバッグが複雑になりやすい
- 正規化の重要性

# 補足：最新の実装テクニック（2024年版）

## 最新の.NETでの実装

.NET 6以降では、より効率的な数値計算が可能になっています。

```csharp:.NET7/8でのベクトル演算の最適化例
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;

public static Vector3 RotatePointModern(Vector4 quaternion, Vector3 point)
{
    if (Avx.IsSupported)
    {
        // AVXを使用した高速な回転計算
        var vec = Vector256.Create(point.X, point.Y, point.Z, 0f);
        // ... AVXを使用した実装
    }
    
    // 従来の実装にフォールバック
    return RotatePoint(quaternion, point);
}
```

## Unityでの最新の実装（2024年）

Unity 2022.3以降では、Burst CompilerとJob Systemを活用した並列処理が可能です。

```csharp:Burstコンパイル対応の回転処理
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Burst;

[BurstCompile]
public struct RotatePointsJob : IJobParallelFor
{
    public NativeArray<float3> Points;
    public quaternion Rotation;
    
    public void Execute(int index)
    {
        Points[index] = math.rotate(Rotation, Points[index]);
    }
}
```

## パフォーマンスに関する最新の知見

### SIMD演算の活用
- AVX-512命令セットの利用（対応CPUの場合）
- ハードウェアアクセラレーションの活用

### メモリ最適化
メモリアライメントとSpanの活用による効率改善

```csharp:最適化されたクォータニオン構造体
[StructLayout(LayoutKind.Sequential, Pack = 16)]
public struct OptimizedQuaternion
{
    public Vector4 Value;  // 16バイトアライメント
}
```

## クラウドでの3D処理

最近のクラウドサービスでは、GPUアクセラレーションを活用した3D処理が可能になっています。

- Azure Kinect DKのボーントラッキング
- AWSのAmazon Sumerian（3D/AR/VR）
- Google Cloud Platform の3Dレンダリングサービス

これらのサービスと連携する際も、クォータニオンは重要な役割を果たしています。