# 機能設計書 22-StereoCamera

## 概要

本ドキュメントは、Three.jsにおけるStereoCamera（ステレオカメラ）機能の詳細設計を記述する。StereoCameraはVR/ステレオ3D表示用の左右分離カメラを提供するクラスである。

### 本機能の処理概要

StereoCameraは、単一のPerspectiveCameraから左目用と右目用の2つのカメラを生成し、ステレオスコピック（立体視）効果を実現するための特殊なカメラクラスである。3Dアナグリフやパララックスバリアなどのステレオエフェクトのレンダリングに使用される。

**業務上の目的・背景**：VRやAR、3D映像表示において、人間の両眼視差を利用した立体視効果は没入感を高めるために不可欠である。StereoCameraは、単一のカメラ設定から適切な眼間距離を持つ左右のカメラを自動生成することで、開発者がステレオレンダリングを容易に実装できるようにする。

**機能の利用シーン**：
- VRヘッドセットでの立体視表示
- 3Dアナグリフ（赤青メガネ）表示
- パララックスバリアディスプレイでの表示
- サイドバイサイドステレオ表示
- オートステレオスコピックディスプレイ対応

**主要な処理内容**：
1. 左目用カメラ（cameraL）と右目用カメラ（cameraR）の管理
2. 眼間距離（eyeSep）に基づくオフアクシスステレオスコピック投影の計算
3. 元のPerspectiveCameraのパラメータに基づく投影行列の更新
4. レイヤーシステムを使用した左右別オブジェクト表示の対応

**関連システム・外部連携**：PerspectiveCamera、WebGLRenderer、WebXRControllerと連携して動作する。

**権限による制御**：特になし（ライブラリ機能として権限制御は行わない）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | WebXRサンプル | 補助機能 | VRステレオ表示 |

## 機能種別

計算処理（ステレオ投影行列の計算）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| - | - | - | コンストラクタはパラメータなし | - |

### インスタンスプロパティ

| プロパティ名 | 型 | デフォルト | 説明 |
|-------------|-----|----------|------|
| aspect | number | 1 | アスペクト比の補正係数 |
| eyeSep | number | 0.064 | 眼間距離（メートル単位、デフォルトは平均的な人間の瞳孔間距離） |

### update()メソッドの入力

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| camera | PerspectiveCamera | Yes | 基準となる透視投影カメラ | PerspectiveCameraインスタンスであること |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cameraL | PerspectiveCamera | 左目用カメラ（レイヤー1に設定） |
| cameraR | PerspectiveCamera | 右目用カメラ（レイヤー2に設定） |

### 出力先

- 各カメラの投影行列（projectionMatrix）
- 各カメラのワールド行列（matrixWorld）

## 処理フロー

### 処理シーケンス

```
1. コンストラクタ実行
   └─ cameraL（左目用カメラ）を生成
   └─ cameraRに（右目用カメラ）を生成
   └─ cameraLをレイヤー1に追加
   └─ cameraRをレイヤー2に追加
   └─ 両カメラのmatrixAutoUpdateをfalseに設定
   └─ キャッシュオブジェクトを初期化

2. update()メソッド呼び出し
   └─ キャッシュとの比較で更新が必要か判定
   └─ 必要な場合のみ投影行列を再計算
      └─ オフアクシスステレオスコピック投影の計算
      └─ 左目用投影行列の設定
      └─ 右目用投影行列の設定
   └─ カメラのワールド行列を元カメラから計算
```

### フローチャート

```mermaid
flowchart TD
    A[update開始] --> B{パラメータが<br>変更された?}
    B -->|Yes| C[キャッシュを更新]
    C --> D[eyeSepHalfを計算]
    D --> E[eyeSepOnProjectionを計算]
    E --> F[ymaxを計算]
    F --> G[左目用投影行列を計算]
    G --> H[右目用投影行列を計算]
    B -->|No| I[ワールド行列を更新]
    H --> I
    I --> J[cameraL.matrixWorldを設定]
    J --> K[cameraR.matrixWorldを設定]
    K --> L[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | デフォルト眼間距離 | 眼間距離のデフォルト値は0.064m（64mm） | 常時 |
| BR-02 | レイヤー分離 | 左カメラはレイヤー1、右カメラはレイヤー2 | 常時 |
| BR-03 | 自動更新無効 | 左右カメラのmatrixAutoUpdateはfalse | 常時 |
| BR-04 | キャッシュ最適化 | パラメータ変更時のみ投影行列を再計算 | update()呼び出し時 |

### 計算ロジック

オフアクシスステレオスコピック投影の計算（Paul Bourkeのアルゴリズムに基づく）:

```javascript
// 眼間距離の半分
eyeSepHalf = eyeSep / 2

// 投影面上での眼間距離
eyeSepOnProjection = eyeSepHalf * near / focus

// 垂直方向の視野範囲
ymax = (near * tan(DEG2RAD * fov * 0.5)) / zoom

// 左目用の水平範囲
xmin_L = -ymax * aspect + eyeSepOnProjection
xmax_L = ymax * aspect + eyeSepOnProjection

// 右目用の水平範囲
xmin_R = -ymax * aspect - eyeSepOnProjection
xmax_R = ymax * aspect - eyeSepOnProjection
```

## データベース操作仕様

該当なし（クライアントサイドのみの処理）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 明示的なエラー処理なし | 入力検証は呼び出し側の責任 |

### リトライ仕様

リトライ処理は不要

## トランザクション仕様

該当なし（クライアントサイドのみの処理）

## パフォーマンス要件

- キャッシュ機構により、パラメータ変更時のみ投影行列を再計算
- 毎フレームのupdate()呼び出しでも効率的に動作

## セキュリティ考慮事項

特になし（ブラウザのセキュリティモデルに依存）

## 備考

- 眼間距離0.064mは平均的な成人の瞳孔間距離に基づく
- レイヤーシステムを使用して、左右それぞれのカメラに対して異なるオブジェクトを表示可能
- Object3Dを継承していないため、シーングラフに直接追加することはできない

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

StereoCameraは内部で2つのPerspectiveCameraを管理する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | StereoCamera.js | `src/cameras/StereoCamera.js` | クラスの全体構造（147行） |
| 1-2 | PerspectiveCamera.js | `src/cameras/PerspectiveCamera.js` | 左右カメラの型 |

**読解のコツ**: StereoCameraはObject3Dを継承していない純粋なJavaScriptクラス。cameraLとcameraRの2つのPerspectiveCameraを保持する。

#### Step 2: コンストラクタを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | StereoCamera.js | `src/cameras/StereoCamera.js` | 20-77行目 |

**主要処理フロー**:
1. **29行目**: type = 'StereoCamera'を設定
2. **37行目**: aspect = 1（アスペクト比補正係数）
3. **46行目**: eyeSep = 0.064（眼間距離64mm）
4. **54-56行目**: cameraL生成、レイヤー1有効化、matrixAutoUpdate無効化
5. **64-66行目**: cameraR生成、レイヤー2有効化、matrixAutoUpdate無効化
6. **68-76行目**: キャッシュオブジェクトの初期化

#### Step 3: update()メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | StereoCamera.js | `src/cameras/StereoCamera.js` | 85-142行目 |

**主要処理フロー**:
- **89-91行目**: キャッシュとの比較で更新必要性を判定
- **95-101行目**: キャッシュの更新
- **106行目**: 元カメラの投影行列をコピー
- **107-109行目**: オフアクシス投影の計算パラメータ
- **114-115行目**: 左右の位置オフセット設定
- **119-124行目**: 左目用投影行列の計算
- **129-135行目**: 右目用投影行列の計算
- **139-140行目**: ワールド行列の設定

#### Step 4: 行列演算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Matrix4.js | `src/math/Matrix4.js` | 4x4行列クラス |
| 4-2 | MathUtils.js | `src/math/MathUtils.js` | DEG2RAD定数 |

**主要処理フロー**:
- **114行目**: `_eyeLeft.elements[12] = -eyeSepHalf` - 左目のX方向オフセット
- **115行目**: `_eyeRight.elements[12] = eyeSepHalf` - 右目のX方向オフセット
- **139行目**: `cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft)`
- **140行目**: `cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight)`

### プログラム呼び出し階層図

```
StereoCamera
    |
    +-- constructor()
    |       |
    |       +-- new PerspectiveCamera() [cameraL]
    |       +-- cameraL.layers.enable(1)
    |       +-- new PerspectiveCamera() [cameraR]
    |       +-- cameraR.layers.enable(2)
    |
    +-- update(camera)
            |
            +-- cache comparison
            +-- _projectionMatrix.copy()
            +-- calculate eyeSepOnProjection
            +-- set _eyeLeft/Right offsets
            +-- cameraL.projectionMatrix.copy()
            +-- cameraR.projectionMatrix.copy()
            +-- cameraL.matrixWorld.copy().multiply()
            +-- cameraR.matrixWorld.copy().multiply()
```

### データフロー図

```
[入力]                      [処理]                         [出力]

                       StereoCamera構築
                              |
                              v
eyeSep (0.064) ────────> 眼間距離設定 ────────────> cameraL (Layer 1)
                              |                       cameraR (Layer 2)
                              v
PerspectiveCamera ────> update()実行
        |                     |
        v                     v
   fov, aspect,        オフアクシス投影計算 ────> cameraL.projectionMatrix
   near, far, focus          |                   cameraR.projectionMatrix
        |                     v
        +──────────> ワールド行列計算 ─────> cameraL.matrixWorld
                                              cameraR.matrixWorld
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| StereoCamera.js | `src/cameras/StereoCamera.js` | ソース | StereoCameraクラスの実装 |
| PerspectiveCamera.js | `src/cameras/PerspectiveCamera.js` | ソース | 左右カメラの型 |
| Matrix4.js | `src/math/Matrix4.js` | ソース | 行列演算 |
| MathUtils.js | `src/math/MathUtils.js` | ソース | DEG2RAD定数 |
