# 機能設計書 70-SpotLight

## 概要

本ドキュメントは、Three.jsライブラリにおけるスポットライト「SpotLight」の機能設計について記述する。SpotLightは、単一の点から円錐形に光を放射する光源であり、舞台照明やフラッシュライトのような光をシミュレートする。

### 本機能の処理概要

SpotLightは、3D空間内の一点から特定の方向に円錐形で光を放射する光源クラスである。角度、ペナンブラ（半影）、距離減衰などのパラメータを持ち、シャドウマップによる影の生成が可能である。

**業務上の目的・背景**：現実世界のスポットライトやフラッシュライトは、特定の方向に円錐形の光を放射する。SpotLightは、この指向性を持った光源をシミュレートするために使用される。舞台照明、車のヘッドライト、懐中電灯など、多くの実用的な照明シーンで使用される。ペナンブラ機能により、光の境界をソフトにすることも可能である。

**機能の利用シーン**：
- 舞台照明、スタジオ照明のシミュレーション
- 車のヘッドライト、懐中電灯の表現
- ゲームの指向性照明効果
- 建築ビジュアライゼーションのアクセント照明
- テクスチャマッピングによるプロジェクター効果

**主要な処理内容**：
1. 光の方向（position → target）の設定
2. 円錐角度（angle）とペナンブラの設定
3. 距離による減衰計算
4. シャドウマップによる影の生成
5. テクスチャマッピング（map）による光の変調

**関連システム・外部連携**：WebGLRenderer/WebGPURendererと連携。SpotLightShadowによるシャドウマップ生成。

**権限による制御**：特になし（クライアントサイドライブラリ）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 12 | Menubar - Add | 補助機能 | スポットライトの追加 |
| 14 | Menubar - View | 補助機能 | ライトヘルパー表示切替 |

## 機能種別

データ定義 / 光源設定

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| color | number/Color/string | No | 光の色（デフォルト: 0xffffff） | Color.setで受け付ける形式 |
| intensity | number | No | 光の強度（カンデラ、デフォルト: 1） | 数値 |
| distance | number | No | 光の最大到達距離（0は無限、デフォルト: 0） | 数値 >= 0 |
| angle | number | No | 光の円錐角度（デフォルト: Math.PI/3） | 0 < angle <= Math.PI/2 |
| penumbra | number | No | ペナンブラ（半影）の割合（デフォルト: 0） | 0 <= penumbra <= 1 |
| decay | number | No | 距離減衰係数（デフォルト: 2） | 数値 |

### 入力データソース

コンストラクタの引数から直接入力。position、targetで光の方向を制御。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| isSpotLight | boolean | 型判定用フラグ（常にtrue） |
| type | string | オブジェクトタイプ識別子（'SpotLight'） |
| color | Color | 光の色（Lightから継承） |
| intensity | number | 光の強度（カンデラ、Lightから継承） |
| position | Vector3 | ライトの位置 |
| target | Object3D | ライトが向くターゲット |
| distance | number | 光の最大到達距離 |
| angle | number | 光の円錐角度 |
| penumbra | number | ペナンブラの割合 |
| decay | number | 距離減衰係数 |
| map | Texture | 光の変調用テクスチャ |
| shadow | SpotLightShadow | シャドウ設定 |
| power | number | 光束（ルーメン、算出プロパティ） |

### 出力先

レンダラーにスポットライト情報として提供される。シャドウマップが生成される。

## 処理フロー

### 処理シーケンス

```
1. コンストラクタ呼び出し
   └─ super(color, intensity) - Lightのコンストラクタ実行
2. isSpotLightフラグ設定
3. type設定
4. positionをDEFAULT_UP (0,1,0) にコピー
5. updateMatrix()で行列更新
6. target = new Object3D() でターゲット作成
7. distance, angle, penumbra, decay設定
8. map = null（オプション）
9. shadow = new SpotLightShadow() でシャドウ設定初期化
10. シーンに追加（targetもシーンに追加が必要な場合あり）
11. レンダリング時:
    a. position → targetの方向を計算
    b. 円錐形の光を放射
    c. 距離減衰とペナンブラを適用
    d. castShadow=trueの場合、シャドウマップを生成
```

### フローチャート

```mermaid
flowchart TD
    A[new SpotLight] --> B[super - Light初期化]
    B --> C[isSpotLight = true]
    C --> D[type = 'SpotLight']
    D --> E[position = DEFAULT_UP]
    E --> F[updateMatrix]
    F --> G[target = new Object3D]
    G --> H[distance/angle/penumbra/decay設定]
    H --> I[shadow = new SpotLightShadow]
    I --> J[初期化完了]
    J --> K[scene.add]
    K --> L{castShadow?}
    L -->|Yes| M[シャドウマップ生成]
    L -->|No| N[影なし描画]
    M --> O[円錐形ライティング]
    N --> O
    O --> P{map?}
    P -->|Yes| Q[テクスチャで光を変調]
    P -->|No| R[通常の光]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-70-1 | 方向制御 | 光の方向はposition→targetのベクトルで決定される | 常時 |
| BR-70-2 | 円錐角度 | angleの上限はMath.PI/2（90度） | 常時 |
| BR-70-3 | ペナンブラ | penumbra=0でハードエッジ、1で完全ソフト | 常時 |
| BR-70-4 | target追加 | targetの位置を変更するにはtargetをシーンに追加する必要がある | target位置変更時 |
| BR-70-5 | power変換 | power = intensity * π（ルーメン⇔カンデラ変換） | power使用時 |
| BR-70-6 | マップ制限 | mapはcastShadow=falseの場合無効 | map使用時 |
| BR-70-7 | デフォルト位置 | positionのデフォルトは(0,1,0)（上方向） | 初期化時 |

### 計算ロジック

光の円錐計算:
```glsl
// スポットライトの減衰
float spotEffect = dot(lightDirection, spotDirection);
spotEffect = max(spotEffect, 0.0);

// 円錐内外の判定
float spotCutoff = cos(angle);
float spotOuterCutoff = cos(angle * (1.0 - penumbra));
spotEffect = smoothstep(spotOuterCutoff, spotCutoff, spotEffect);
```

power（光束）とintensity（光度）の変換（スポットライト用）:
```javascript
// カンデラからルーメン（スポットライト規約）
power = intensity * Math.PI;

// ルーメンからカンデラ
intensity = power / Math.PI;
```

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

該当なし（クライアントサイドライブラリ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 視覚的問題 | targetがシーンに追加されていない場合、位置変更が反映されない | target をシーンに追加 |
| - | 視覚的問題 | angle > Math.PI/2の場合、意図しない照明になる | angleを制限 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- シャドウマップ有効時はレンダリングパスが追加される
- 複数のSpotLightで影を有効にすると負荷が増加
- mapを使用する場合、テクスチャサンプリングコストが追加される

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

特になし（クライアントサイドライブラリ）

## 備考

- 195行の実装
- DirectionalLightに次いで多機能な光源
- mapプロパティでプロジェクター効果が可能
- SpotLightShadowはPerspectiveCameraを使用し、FOVは光の角度から自動計算

---

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

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

### 推奨読解順序

#### Step 1: 親クラスを理解する

SpotLightが継承するLightクラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Light.js | `src/lights/Light.js` | 親クラスの機能（color、intensity） |
| 1-2 | Object3D.js | `src/core/Object3D.js` | position、DEFAULT_UPの定義 |

**読解のコツ**: Object3D.DEFAULT_UP（1647行目）が(0,1,0)であることを確認。

#### Step 2: エントリーポイントを理解する

SpotLightクラス自体の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SpotLight.js | `src/lights/SpotLight.js` | クラス全体（195行） |

**主要処理フロー**:
1. **1-3行目**: インポート（Light、SpotLightShadow、Object3D）
2. **27行目**: `class SpotLight extends Light` - Light継承
3. **39行目**: コンストラクタ引数（color, intensity, distance, angle, penumbra, decay）
4. **41行目**: `super( color, intensity )` - Lightコンストラクタ呼び出し
5. **50行目**: `this.isSpotLight = true` - 型判定フラグ
6. **52行目**: `this.type = 'SpotLight'` - タイプ識別子
7. **54行目**: `this.position.copy( Object3D.DEFAULT_UP )` - 初期位置設定
8. **55行目**: `this.updateMatrix()` - 行列更新
9. **69行目**: `this.target = new Object3D()` - ターゲット作成
10. **77行目**: `this.distance = distance` - 距離設定
11. **85行目**: `this.angle = angle` - 円錐角度設定
12. **94行目**: `this.penumbra = penumbra` - ペナンブラ設定
13. **103行目**: `this.decay = decay` - 減衰係数設定
14. **116行目**: `this.map = null` - テクスチャマップ
15. **123行目**: `this.shadow = new SpotLightShadow()` - シャドウ設定
16. **133-138行目**: power getter（intensity → lumens、スポットライト用）
17. **141-146行目**: power setter（lumens → intensity）
18. **148-154行目**: dispose()メソッド
19. **156-171行目**: copy()メソッド
20. **173-190行目**: toJSON()メソッド

#### Step 3: シャドウ設定を確認する

SpotLightShadowクラスを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SpotLightShadow.js | `src/lights/SpotLightShadow.js` | PerspectiveCameraでのシャドウ設定 |
| 3-2 | LightShadow.js | `src/lights/LightShadow.js` | シャドウ基底クラス |

**キーポイント**: SpotLightShadowはupdateMatrices()でカメラのFOVを光の角度から自動計算（51行目）。

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

```
SpotLight (継承: Light → Object3D → EventDispatcher)
    │
    ├─ constructor(color, intensity, distance, angle, penumbra, decay)
    │      │
    │      ├─ super(color, intensity) → Light.constructor()
    │      │
    │      ├─ this.isSpotLight = true
    │      ├─ this.type = 'SpotLight'
    │      ├─ this.position.copy(Object3D.DEFAULT_UP)
    │      ├─ this.updateMatrix()
    │      ├─ this.target = new Object3D()
    │      ├─ this.distance = distance
    │      ├─ this.angle = angle
    │      ├─ this.penumbra = penumbra
    │      ├─ this.decay = decay
    │      ├─ this.map = null
    │      └─ this.shadow = new SpotLightShadow()
    │                              │
    │                              ├─ new PerspectiveCamera(50, 1, 0.5, 500)
    │                              ├─ this.focus = 1
    │                              └─ this.aspect = 1
    │
    ├─ get power()
    │      └─ return this.intensity * Math.PI
    │
    ├─ set power(power)
    │      └─ this.intensity = power / Math.PI
    │
    ├─ dispose()
    │      │
    │      ├─ super.dispose() → Light.dispose()
    │      └─ this.shadow.dispose()
    │
    ├─ copy(source)
    │      │
    │      ├─ super.copy(source)
    │      ├─ this.distance = source.distance
    │      ├─ this.angle = source.angle
    │      ├─ this.penumbra = source.penumbra
    │      ├─ this.decay = source.decay
    │      ├─ this.target = source.target.clone()
    │      ├─ this.map = source.map
    │      └─ this.shadow = source.shadow.clone()
    │
    └─ toJSON(meta)
           │
           ├─ super.toJSON(meta)
           ├─ data.object.distance = this.distance
           ├─ data.object.angle = this.angle
           ├─ data.object.decay = this.decay
           ├─ data.object.penumbra = this.penumbra
           ├─ data.object.target = this.target.uuid
           ├─ data.object.map = this.map?.toJSON(meta).uuid
           └─ data.object.shadow = this.shadow.toJSON()
```

### データフロー図

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

color ─────────────▶ SpotLight.constructor()
intensity                  │
distance                   ├─ Light初期化
angle                      ├─ position = (0,1,0)
penumbra                   ├─ target = new Object3D()
decay                      ├─ distance/angle/penumbra/decay設定
                           ├─ map = null
                           └─ shadow = new SpotLightShadow()
                                         │
                                         ▼
                              SpotLightインスタンス
                                         │
           ┌─────────────────────────────┼─────────────────────────────┐
           ▼                             ▼                             ▼
      position                     target                         shadow
      (0,1,0)                     (0,0,0)                  PerspectiveCamera
           │                             │                             │
           └─────────────────────────────┼─────────────────────────────┘
                                         │
                              lightDirection =
                              normalize(position - target.position)
                                         │
                                         ▼
                              円錐角度 = angle
                              ペナンブラ = penumbra
                              減衰 = decay
                                         │
                                         ▼
                    ┌────────────────────┴────────────────────┐
                    ▼                                         ▼
           円錐形ライティング                          シャドウマップ生成
           (angle内のみ照明)               (PerspectiveCamera、FOV自動計算)
                    │
                    ▼
            ┌───────┴───────┐
            ▼               ▼
      map使用時        map未使用
      テクスチャで      通常の光
      光を変調
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SpotLight.js | `src/lights/SpotLight.js` | ソース | SpotLightクラス定義（195行） |
| SpotLightShadow.js | `src/lights/SpotLightShadow.js` | ソース | シャドウ設定クラス（81行） |
| Light.js | `src/lights/Light.js` | ソース | 親クラス（Light） |
| LightShadow.js | `src/lights/LightShadow.js` | ソース | シャドウ基底クラス |
| Object3D.js | `src/core/Object3D.js` | ソース | 大親クラス、DEFAULT_UP定義 |
| PerspectiveCamera.js | `src/cameras/PerspectiveCamera.js` | ソース | シャドウマップカメラ |
| SpotLightHelper.js | `src/helpers/SpotLightHelper.js` | ソース | 可視化ヘルパー |
