# 機能設計書 138-Frustum

## 概要

本ドキュメントは、Three.jsの数学ライブラリにおける視錐台（View Frustum）を表現するFrustumクラスの機能設計書である。

### 本機能の処理概要

Frustumクラスは、カメラの視野範囲を定義する6つの平面で囲まれた錐台形状（視錐台）を表現するためのクラスである。左・右・上・下・近・遠の6つのPlaneによって構成され、主にフラスタムカリング（Frustum Culling）に使用される。フラスタムカリングは、カメラの視野外にあるオブジェクトをレンダリング前に除外し、描画パフォーマンスを大幅に向上させる重要な最適化手法である。

**業務上の目的・背景**：3Dシーンには多数のオブジェクトが存在し得るが、カメラに映らないオブジェクトを描画するのは無駄である。Frustumクラスは、投影行列から視錐台を構築し、オブジェクトとの交差判定を行うことで、レンダリング対象を効率的に絞り込む。これにより、大規模シーンでも高いフレームレートを維持できる。

**機能の利用シーン**：
- WebGLRendererでの視錐台カリング（レンダリング最適化）
- 可視オブジェクトのリスト作成
- LOD（Level of Detail）の判定補助
- ポータルレンダリング
- シャドウマップのカリング
- オクルージョンカリングの事前判定

**主要な処理内容**：
1. 視錐台の生成と初期化（6つの平面の設定）
2. 投影行列からの視錐台構築（setFromProjectionMatrix）
3. オブジェクト・スプライト・球・ボックスとの交差判定
4. 点の包含判定
5. WebGL/WebGPU座標系のサポート

**関連システム・外部連携**：Camera、WebGLRenderer、Object3D、Sphere、Box3、Planeなどと連携。

**権限による制御**：なし（純粋な数学ユーティリティクラス）

## 関連画面

本機能はレンダリングの内部処理として使用される。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | レンダリング時の視錐台カリング処理として使用 |

## 機能種別

計算処理 / ジオメトリ演算 / レンダリング最適化

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| p0-p5 | Plane | No | 視錐台を構成する6つの平面 | デフォルト: 各new Plane() |

### 入力データソース

コンストラクタ引数、または投影行列（Matrix4）からのsetFromProjectionMatrix

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| planes | Array<Plane> | 視錐台を構成する6つの平面の配列 |

### 出力先

メモリ上のオブジェクトとして保持

## 処理フロー

### 処理シーケンス

```
1. 視錐台の初期化
   └─ 6つのPlaneを初期化
2. 投影行列からの構築
   └─ setFromProjectionMatrix: 行列要素から6平面を計算
       ├─ planes[0]: 右平面
       ├─ planes[1]: 左平面
       ├─ planes[2]: 下平面
       ├─ planes[3]: 上平面
       ├─ planes[4]: 遠平面
       └─ planes[5]: 近平面
3. 交差判定
   ├─ intersectsObject: Object3Dの境界球との判定
   ├─ intersectsSprite: スプライトとの判定
   ├─ intersectsSphere: 球との判定
   └─ intersectsBox: AABBとの判定
4. 包含判定
   └─ containsPoint: 点が視錐台内にあるか
```

### フローチャート

```mermaid
flowchart TD
    A[Frustumインスタンス生成] --> B{初期化方法}
    B -->|コンストラクタ| C[6平面を直接設定]
    B -->|setFromProjectionMatrix| D[投影行列から計算]
    D --> D1[行列要素を展開]
    D1 --> D2[6平面の係数を計算]
    D2 --> D3[各平面をnormalize]
    C --> E[視錐台確定]
    D3 --> E
    E --> F{判定種別}
    F -->|オブジェクト| G[intersectsObject]
    G --> G1[boundingSphereを計算/取得]
    G1 --> G2[intersectsSphere]
    F -->|球| H[intersectsSphere]
    H --> H1{全6平面でチェック}
    H1 -->|全て通過| I[true]
    H1 -->|いずれか失敗| J[false]
    F -->|ボックス| K[intersectsBox]
    F -->|点| L[containsPoint]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 平面配置 | planes[0-3]=側面、planes[4]=遠、planes[5]=近 | 全操作 |
| BR-002 | 座標系 | WebGLCoordinateSystemとWebGPUCoordinateSystemをサポート | setFromProjectionMatrix |
| BR-003 | 保守的判定 | 交差判定は「確実に外」のみをfalseとする | intersects系 |
| BR-004 | 逆深度 | reversedDepth=trueで逆深度バッファに対応 | setFromProjectionMatrix |

### 計算ロジック

**投影行列からの平面抽出**:
```javascript
// 4x4投影行列の要素
me = matrix.elements
// 右平面: 4列目 - 1列目
planes[0].setComponents(me3-me0, me7-me4, me11-me8, me15-me12).normalize()
// 左平面: 4列目 + 1列目
planes[1].setComponents(me3+me0, me7+me4, me11+me8, me15+me12).normalize()
// 下平面: 4列目 + 2列目
planes[2].setComponents(me3+me1, me7+me5, me11+me9, me15+me13).normalize()
// 上平面: 4列目 - 2列目
planes[3].setComponents(me3-me1, me7-me5, me11-me9, me15-me13).normalize()
// 遠平面/近平面は座標系とreversedDepthによって異なる
```

**球との交差判定**:
```javascript
for each plane in planes:
  distance = plane.distanceToPoint(sphere.center)
  if (distance < -sphere.radius) return false  // 完全に外側
return true
```

**ボックスとの交差判定**:
```javascript
for each plane in planes:
  // 平面法線方向の最遠点を選択
  corner.x = normal.x > 0 ? box.max.x : box.min.x
  corner.y = normal.y > 0 ? box.max.y : box.min.y
  corner.z = normal.z > 0 ? box.max.z : box.min.z
  if (plane.distanceToPoint(corner) < 0) return false
return true
```

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

該当なし（純粋な計算処理クラス）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無効な座標系 | 未サポートの座標系を指定 | Errorをスロー |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- setFromProjectionMatrix: O(1)
- intersectsSphere/intersectsBox: O(1)（6平面のチェック）
- intersectsObject: O(1) + boundingSphere計算（初回のみ）
- メモリ使用量: Plane x 6 = 168バイト程度

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

該当なし（純粋な数学計算）

## 備考

- WebGLRendererのprojectObject()で毎フレーム使用
- frustum.setFromProjectionMatrix(camera.projectionMatrix.clone().multiply(camera.matrixWorldInverse))の形で使用
- 保守的な判定を行うため、境界にあるオブジェクトも描画対象に含まれる
- WebGPUでは異なるクリップ空間（Z: 0-1）を使用するため、座標系パラメータが必要

---

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

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

### 推奨読解順序

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

Frustumクラスは6つのPlaneの配列で視錐台を表現する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Plane.js | `src/math/Plane.js` | Frustumが依存する平面クラス |
| 1-2 | Frustum.js | `src/math/Frustum.js` | クラスのプロパティ定義（planes配列） |

**読解のコツ**: 6つの平面の法線は全て視錐台の内側を向いている。これにより、distanceToPoint < 0で「外側」を判定できる。

#### Step 2: 投影行列からの構築を理解する

setFromProjectionMatrixの実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Frustum.js | `src/math/Frustum.js` | setFromProjectionMatrix()の詳細 |

**主要処理フロー**:
1. **95-102行目**: 行列要素を展開
2. **104-107行目**: 右・左・下・上の4平面を計算
3. **109-132行目**: 遠・近平面を座標系とreversedDepthに応じて計算

#### Step 3: オブジェクトとの交差判定を理解する

各種オブジェクトとの交差判定ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Frustum.js | `src/math/Frustum.js` | intersectsObject, intersectsSphere, intersectsBox |

**主要処理フロー**:
- **146-166行目**: intersectsObject() - 境界球を取得して判定
  - **148-152行目**: object.boundingSphereがある場合
  - **154-160行目**: geometry.boundingSphereを使用する場合
- **193-213行目**: intersectsSphere() - 6平面との距離チェック
- **221-245行目**: intersectsBox() - 最遠コーナーでチェック

#### Step 4: 座標系の違いを理解する

WebGLとWebGPUの座標系の違いに対応する部分を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Frustum.js | `src/math/Frustum.js` | setFromProjectionMatrixの座標系処理 |
| 4-2 | constants.js | `src/constants.js` | WebGLCoordinateSystem, WebGPUCoordinateSystem定義 |

**主要処理フロー**:
- **109-132行目**: reversedDepthとcoordinateSystemによる場合分け
  - WebGL: Z: -1 to 1
  - WebGPU: Z: 0 to 1
  - reversedDepth: 深度バッファの方向を逆転

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

```
Frustum
    │
    ├─ setFromProjectionMatrix(matrix, coordinateSystem, reversedDepth)
    │      ├─ 行列要素を展開 (me0-me15)
    │      ├─ planes[0-3].setComponents(...).normalize()
    │      └─ planes[4-5] を座標系に応じて設定
    │
    ├─ intersectsObject(object)
    │      ├─ object.boundingSphere または geometry.boundingSphere
    │      ├─ _sphere.copy().applyMatrix4(object.matrixWorld)
    │      └─ intersectsSphere(_sphere)
    │
    ├─ intersectsSprite(sprite)
    │      ├─ _sphere.center.set(0, 0, 0)
    │      ├─ _sphere.radius を計算
    │      └─ intersectsSphere(_sphere)
    │
    ├─ intersectsSphere(sphere)
    │      └─ for i = 0 to 5:
    │             if planes[i].distanceToPoint(center) < -radius
    │                return false
    │
    └─ intersectsBox(box)
           └─ for i = 0 to 5:
                  _vector = 平面法線方向の最遠コーナー
                  if planes[i].distanceToPoint(_vector) < 0
                     return false
```

### データフロー図

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

Matrix4 ───────────▶ setFromProjectionMatrix() ─▶ Frustum {planes[6]}
(投影行列)            (平面抽出 & 正規化)

Object3D ──────────▶ intersectsObject() ─────────▶ boolean
                     ├─ boundingSphere取得
                     └─ intersectsSphere()

Sphere ────────────▶ intersectsSphere() ─────────▶ boolean
(境界球)             (6平面との距離チェック)

Box3 ──────────────▶ intersectsBox() ────────────▶ boolean
(AABB)              (最遠コーナーチェック)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Frustum.js | `src/math/Frustum.js` | ソース | 視錐台クラス本体 |
| Plane.js | `src/math/Plane.js` | ソース | 平面クラス（6平面で構成） |
| Sphere.js | `src/math/Sphere.js` | ソース | 境界球（交差判定で使用） |
| Vector3.js | `src/math/Vector3.js` | ソース | ベクトル計算 |
| Vector2.js | `src/math/Vector2.js` | ソース | スプライト中心位置 |
| Matrix4.js | `src/math/Matrix4.js` | ソース | 投影行列 |
| constants.js | `src/constants.js` | ソース | 座標系定数 |
| WebGLRenderer.js | `src/renderers/WebGLRenderer.js` | ソース | カリング処理で使用 |
