diff --git a/CLAUDE.md b/CLAUDE.md index 2941616..64b8350 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,6 +15,7 @@ 補足: **Step 8-8 完了**.linear 評価モードに mean 揃え正規化を導入(`evaluation/normalize.py` の `apply_mean_anchor` + `EvaluationConfig.normalization` スキーマ + CLI 統合 + OETF 排他バリデーション).OETF 露出アンカー(Step 8-3)と同じ思想を linear ドメインに対称展開.`configs/eval_baseline_linear.yaml` で有効化(target_mean=146.7146).形状指標 (CoV/勾配/C/P/max-min) は厳密不変,mean のみ target_mean に揃う.動作確認: α=0.961934 で baseline luminance.npy が mean=146.7146, CoV=0.182577 で評価可能.テスト 62 件追加(既存 441 + 新規 62 = 503 件全通過). 補足: **Step 8-9 完了**.LED 18 個の位置を Fusion 360 設計 STL (`configs/meshes/SmTIASv3.stl`) から抽出して `scene_baseline.yaml` を更新.`scripts/extract_led_positions_from_stl.py` で連結成分分割→ LED/カバー識別→ bottom/right/left 自動命名.主な変更: 下基板 LED 配置を「等間隔 8.7mm」→「3+ギャップ+3 (4.2mm+10.8mm+4.2mm)」へ訂正(実機写真の中央電流制限抵抗 2 個と整合).左右基板も実位置に修正.**形状指標は ~1% 以内のほぼ不変**(多重反射が支配的,LED 数 mm 誤差は ROI 上で平滑化).絶対 mean は +10% 増加(α 値 0.962→0.873).既知差分 #2 をクローズ.TEST_03 比較パネルに Step 8-9 を追加(5 枚並列). 補足: **LED 基板カバーを 3 枚構成に展開**.従来は下基板用 `LED基板カバー.stl` 1 枚のみだったが,`scripts/extract_led_positions_from_stl.py --export-covers configs/meshes/` で SmTIASv3.stl から `LED基板カバー下/右/左.stl` の 3 ファイルを書き出し,`scene_baseline.yaml` の geometry を `led_cover_bottom/right/left` の 3 メッシュエントリに展開.右・左基板の LED が「カバーなしで浮く」状態を解消.多重反射計算への影響は軽微だが物理的に正確. +補足: **Step 8-10 完了**.LED 配光を `spot` emitter (cutoff=90°/beam=30°,FWHM 120° 一致,flux 保存 I_0=0.026784 W/sr) で表現する試行.`SpotLight` クラスを `schema.py` に追加,`lights.py` に spot 分岐実装,`configs/scene_baseline_spot.yaml` を並列保持.**目視で X 方向の対称性が部分改善**(下端の明るい領域が中央寄りに集中)だが,数値指標は微悪化(CoV +4%, 勾配 +15%).LED 指向性は形の一部(X 方向)を変えるが,Y 方向の中央ピークは LSC モデル化(Step 8-N)が必要.本格採用は LSC との組み合わせ評価後に判断.付随発見: spot で mean が 14.6× 暗くなる(理由は spot falloff 関数の cos vs 角度ドメイン smoothstep の解釈差等を仮説). 次のアクション(キャリブ再開時): 残差に **LSC(ISP レンズシェーディング)順方向モデル**を当てる(物理動機づけ低自由度形、overfitting 回避・取得パラメータ数明記). 実装済みモジュール: @@ -28,7 +29,7 @@ - `src/sm_tias_lightsim/scene/schema.py` — Pydantic ベース YAML スキーマ + ローダ.SensorConfig は perspective / orthographic 両対応 - `src/sm_tias_lightsim/geometry/loader.py` — STL 読込(trimesh)+ `LoadedMesh` データクラス - `src/sm_tias_lightsim/geometry/transforms.py` — mm⇄m 単位スケール変換 -- `src/sm_tias_lightsim/scene/{lights,materials,sensors}.py` — Mitsuba emitter / BSDF / sensor dict 変換(sensors は orthographic 対応) +- `src/sm_tias_lightsim/scene/{lights,materials,sensors}.py` — Mitsuba emitter / BSDF / sensor dict 変換(sensors は orthographic 対応,lights は `area` / `point` / `directional` / `spot` 対応.Step 8-10 で `SpotLight` を追加) - `scripts/select_white_board.py` — 実測 PNG で白色板境界を OpenCV GUI で選択(Step 8 キャリブ用) - `scripts/visualize_roi_3d.py` — 評価板 STL に ROI を 3D 投影(PyVista,Step 8 キャリブ用) - `src/sm_tias_lightsim/scene/builder.py` — SceneConfig → Mitsuba scene dict(PLY 一時ファイル経由) diff --git a/configs/scene_baseline_spot.yaml b/configs/scene_baseline_spot.yaml new file mode 100644 index 0000000..5c31198 --- /dev/null +++ b/configs/scene_baseline_spot.yaml @@ -0,0 +1,372 @@ +name: baseline_minitias +description: 'MiniTIAS scene with spot emitters (data-sheet config: cutoff=90deg, + beam_width=30deg, FWHM=120deg). LED area light replaced by spot light with flux-preserved + intensity I_0=0.02678 W/sr. Test config for emitter directionality experiment.' +geometry: + meshes: + - id: housing_front + path: meshes/前側.stl + unit: mm + material: printer_white + - id: housing_back + path: meshes/後側.stl + unit: mm + material: printer_white + - id: phone_holder + path: meshes/スマホホルダ.stl + unit: mm + material: holder_dark + - id: led_cover_bottom + path: meshes/LED基板カバー下.stl + unit: mm + material: printer_white + - id: led_cover_right + path: meshes/LED基板カバー右.stl + unit: mm + material: printer_white + - id: led_cover_left + path: meshes/LED基板カバー左.stl + unit: mm + material: printer_white + - id: phone_body + path: meshes/SH-02M.stl + unit: mm + material: phone_body_dark + - id: evaluation_board + path: meshes/照明ムラ補正ボード.stl + unit: mm + material: printer_white +materials: + printer_white: + type: diffuse + reflectance: + - 0.85 + - 0.85 + - 0.85 + holder_dark: + type: diffuse + reflectance: + - 0.5 + - 0.5 + - 0.5 + phone_body_dark: + type: diffuse + reflectance: + - 0.05 + - 0.05 + - 0.05 +lights: +- id: led_bottom_0 + type: spot + position: + - -10.0 + - -71.0 + - -66.1 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_bottom_1 + type: spot + position: + - -5.8 + - -71.0 + - -66.1 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_bottom_2 + type: spot + position: + - -1.6 + - -71.0 + - -66.1 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_bottom_3 + type: spot + position: + - 9.2 + - -71.0 + - -66.1 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_bottom_4 + type: spot + position: + - 13.4 + - -71.0 + - -66.1 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_bottom_5 + type: spot + position: + - 17.6 + - -71.0 + - -66.1 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_right_0 + type: spot + position: + - 65.9987 + - -24.9738 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_right_1 + type: spot + position: + - 65.9987 + - -20.7738 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_right_2 + type: spot + position: + - 65.9987 + - -16.5738 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_right_3 + type: spot + position: + - 65.9987 + - -5.7738 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_right_4 + type: spot + position: + - 65.9987 + - -1.5738 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_right_5 + type: spot + position: + - 65.9987 + - 2.6262 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_left_0 + type: spot + position: + - -66.0067 + - -32.5952 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_left_1 + type: spot + position: + - -66.0067 + - -28.3952 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_left_2 + type: spot + position: + - -66.0067 + - -24.1952 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_left_3 + type: spot + position: + - -66.0067 + - -13.3952 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_left_4 + type: spot + position: + - -66.0067 + - -9.1952 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +- id: led_left_5 + type: spot + position: + - -66.0067 + - -4.9952 + - -66.0243 + direction: + - 0.0 + - 0.0 + - -1.0 + cutoff_angle: 90.0 + beam_width: 30.0 + intensity: + - 0.026784 + - 0.026784 + - 0.026784 +sensor: + type: orthographic + position: + - 0.0 + - 0.0 + - -50.0 + target: + - 0.0 + - 0.0 + - 0.0 + up: + - 0.0 + - 1.0 + - 0.0 + ortho_scale: + - 50.0 + - 52.0 + film: + width: 512 + height: 512 + rfilter: gaussian +render: + variant: llvm_ad_rgb + integrator: path + max_depth: 8 + spp: 1024 diff --git "a/docs/04_SPEC/SPEC_03_\345\237\272\346\272\226\350\250\255\350\250\210\343\201\256\347\211\251\347\220\206\346\247\213\346\210\220.md" "b/docs/04_SPEC/SPEC_03_\345\237\272\346\272\226\350\250\255\350\250\210\343\201\256\347\211\251\347\220\206\346\247\213\346\210\220.md" index 1297526..8b939ff 100644 --- "a/docs/04_SPEC/SPEC_03_\345\237\272\346\272\226\350\250\255\350\250\210\343\201\256\347\211\251\347\220\206\346\247\213\346\210\220.md" +++ "b/docs/04_SPEC/SPEC_03_\345\237\272\346\272\226\350\250\255\350\250\210\343\201\256\347\211\251\347\220\206\346\247\213\346\210\220.md" @@ -98,6 +98,20 @@ - 全光束への換算: `Φ [W] = radiance × area × π`(Lambertian 半球積分) - 1 LED あたりの面積: `2.8 × 3.5 = 9.8 mm² = 9.8e-6 m²` +### Spot emitter による配光表現(Step 8-10 試行) + +`scripts/extract_led_positions_from_stl.py` の `SpotLight` クラスを用いて,LED の配光をデータシート FWHM 120° に従う Mitsuba `spot` emitter で表現する試行を実施.`configs/scene_baseline_spot.yaml` に 18 LED 分の spot 設定を並列保持. + +| 項目 | 設定 | +| --- | --- | +| type | `spot` | +| `cutoff_angle` | 90°(半球内のみ発光) | +| `beam_width` | 30°(中央フラット領域) | +| 半値 (FWHM) | (90 + 30) / 2 = 60° → 配光角 120° と一致 | +| `intensity` (I_0) | 0.026784 W/sr(flux Φ=0.0864 W/LED を保存する数値積分から) | + +**結果**: X 方向の対称性が部分改善(目視).数値指標は微悪化.Y 方向の中央ピークは LSC が必要.詳細は [TEST_01 Step 8-10](../06_TEST/TEST_01_キャリブレーション検証履歴.md#step-8-10-led-配光を-spot-emitter-で表現x-方向の部分改善).LSC モデル化との組み合わせで本格採用可否を判断する. + ### 配置 実機の LED 基板は **前側のでっぱりの直角部分** に取り付けられている.3 基板を Z 軸まわりに ±90° 回転で配置: diff --git "a/docs/06_TEST/TEST_01_\343\202\255\343\203\243\343\203\252\343\203\226\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\346\244\234\350\250\274\345\261\245\346\255\264.md" "b/docs/06_TEST/TEST_01_\343\202\255\343\203\243\343\203\252\343\203\226\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\346\244\234\350\250\274\345\261\245\346\255\264.md" index 906d1bc..966a11f 100644 --- "a/docs/06_TEST/TEST_01_\343\202\255\343\203\243\343\203\252\343\203\226\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\346\244\234\350\250\274\345\261\245\346\255\264.md" +++ "b/docs/06_TEST/TEST_01_\343\202\255\343\203\243\343\203\252\343\203\226\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\346\244\234\350\250\274\345\261\245\346\255\264.md" @@ -339,6 +339,49 @@ - SPEC_03 既知差分 #2 をクローズ - 本筋 LSC モデル化 ([Step 8-N](#step-8-n-候補-lsc-順方向モデル未着手)) に進む +### Step 8-10: LED 配光を spot emitter で表現(X 方向の部分改善) + +- **日付**: 2026-05-28 +- **背景・問題意識**: Step 8-1〜8-9 で「形を変えない変更」を網羅し,emitter 指向性が形に効くかどうかを次に検証.データシート LP-2835D90VW の配光角 120° (FWHM) は数学的に厳密 Lambertian(現状の `area` emitter)と整合するが,Mitsuba の `spot` emitter で smoothstep 補間を使うと「中央集中度」が違うため,多重反射経由の評価板への分布が変わる可能性. +- **仮説(物理動機)**: + - LED の指向性プロファイルが変わると,LED 直下の壁の局所照度が変わる + - 多重反射経由でも評価板への分布パターンに微変化が出る可能性 + - ただし「Y 軸単調勾配」(下基板のみ)は LED 配置の物理的偏りなので emitter 指向性では変えられない予想 +- **試した変更**: + - `src/sm_tias_lightsim/scene/schema.py` に `SpotLight` クラスを新規追加(type='spot', position, direction, cutoff_angle, beam_width, intensity).`LightConfig` Union に組み込み + - `src/sm_tias_lightsim/scene/lights.py` に spot 分岐を追加(look_at で to_world transform 構築 → Mitsuba `spot` emitter dict) + - `configs/scene_baseline_spot.yaml` を新規作成:18 LED を area → spot に変換(flux Φ=0.0864 W per LED 保存) + - パラメータ: `cutoff_angle=90°`,`beam_width=30°` (FWHM 120° と一致する最も標準的な設定).数学的に半値(I/I_0=0.5)は θ=60° に出る + - intensity `I_0=0.026784 W/sr`(smoothstep 配光積分 K=3.227 sr で `I_0 = Φ/K`) +- **期待した効果**: LED 指向性で X 方向(左右対称)の偏りが変わる可能性.Y 方向は不変予想. +- **実際の結果**(`output/baseline_spot/`,linear + normalization 評価): + + | 指標 | Step 8-9 (area) | Step 8-10 (spot) | 差 | + | --- | --- | --- | --- | + | mean (pre-normalize) | 167.95 | **11.49** | **-93%(spot は約 14.6× 暗い)** | + | α (mean 揃え係数) | 0.873 | **12.77** | × 14.6 | + | mean (post-normalize) | 146.71 | 146.71 | 同(アンカー) | + | CoV | 0.181 | 0.189 | +4.4%(**数値悪化**) | + | 勾配 % | 24.13 | 27.69 | +14.7%(**数値悪化**) | + | C/P | 1.318 | 1.383 | +4.9%(**数値悪化**) | + | max/min | 4.27 | 4.15 | -2.8%(誤差レベル) | + +- **目視観察(重要)**: 数値指標は悪化したが,**輝度マップを目視で比較すると下端の明るい領域がやや中央寄りに集中し,左右端は他のシミュより暗い**.X 方向の対称性が部分的に改善された.[TEST_03 比較パネル](TEST_03_輝度マップ形状追跡.md)で「他のシミュ 4 種と spot の 5 番目」を並べると spot のみ「下中央集中型」と判別できる. +- **解釈**: + - spot の中央集中型配光(cutoff=90, beam=30)で LED 真下の壁が局所的に強く照らされる + - その結果,ROI 下中央が他より明るく,下端左右は相対的に暗く + - **X 方向に「中央集中型」のパターン**が形成された(実機の中央ピークの X 方向成分に部分的に符号一致) + - Y 方向の偏りは LED 配置(下基板のみ)の物理的事実なので解消されない(**LSC モデル化が依然必要**) +- **副次的な発見(未解明)**: flux 保存で I_0 を計算したのに mean が 14.6× も暗くなった.仮説:(1) Mitsuba `spot` の falloff 関数が cos ドメインの smoothstep(私の計算は角度ドメイン smoothstep)で I_0 計算が不正確,(2) 点光源 vs 面光源で多重反射の光路長が違う,(3) spot の集中性で「LED 真下の壁」に光が偏り多重反射数が増えて減衰.本筋ではないため深掘りしない. +- **学び**: + - **数値指標と「形」の改善は別軸**.「同心円状に近づいた」と目視で気付くことは CoV/勾配の集約値だけでは捕捉できない.[TEST_03](TEST_03_輝度マップ形状追跡.md) の「2 軸評価」の妥当性が再確認された + - **emitter 指向性は形の一部(X 方向)を変える**.これは Step 8-1〜8-9 の「形を変えない変更」とは違う性質の変更 + - ただし **全方向で実機と一致するには LSC モデル化が依然必要**(Y 方向の中央ピーク).spot は LSC と並列して使える補完手段 +- **採用判断**: scene_baseline.yaml の area light はそのまま残し,**`configs/scene_baseline_spot.yaml` を並列保持**.本格採用するかは LSC モデル化との組み合わせ評価後に判断.SpotLight クラス・lights.py 分岐は将来の IES 実装等の基盤として残す. +- **次のアクション**: + - 本筋 LSC モデル化([Step 8-N](#step-8-n-候補-lsc-順方向モデル未着手))に進む + - LSC + spot の組み合わせ評価は LSC 実装後 + ## Step 8-N 候補: LSC 順方向モデル(未着手) - **位置付け**: 8-3 OETF 適用後の残差 **1.6〜1.9×**(CoV 0.089 vs 実測 0.043,勾配 12.57 vs 6.10)の **主因候補**. diff --git "a/docs/06_TEST/TEST_02_\343\202\267\343\203\237\343\203\245\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\345\256\237\350\241\214\350\250\230\351\214\262.md" "b/docs/06_TEST/TEST_02_\343\202\267\343\203\237\343\203\245\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\345\256\237\350\241\214\350\250\230\351\214\262.md" index d582425..242681a 100644 --- "a/docs/06_TEST/TEST_02_\343\202\267\343\203\237\343\203\245\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\345\256\237\350\241\214\350\250\230\351\214\262.md" +++ "b/docs/06_TEST/TEST_02_\343\202\267\343\203\237\343\203\245\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\345\256\237\350\241\214\350\250\230\351\214\262.md" @@ -28,6 +28,7 @@ | `output/sweep_reflectance/r075/` 〜 `r092/` | 8-2 反射率スイープ | `configs/scene_baseline.yaml` の `printer_white.reflectance` を 0.75 / 0.80 / 0.85 / 0.90 / 0.92 に振った 5 種 (d=12, spp=256) | linear 評価 | → 下表 `sweep_summary.csv` | TEST_01 / SPEC_03 の反射率スイープ表の元データ | | `output/sweep_reflectance/sweep_summary.csv` | 8-2 | — | — | 5 反射率の評価結果まとめ | 反射率スイープのサマリ表 | | `output/baseline_v3_leds/` | 8-9 LED 位置正確化 | `configs/scene_baseline.yaml`(Fusion STL から抽出した新 LED 位置で更新済み, R=0.85, d=8, spp=1024) | `configs/eval_baseline_linear.yaml` (linear + normalization) | 146.71 / 0.1815 / 24.13 / 1.318 | TEST_01 Step 8-9 検証.輝度マップ追跡 (TEST_03) パネル右下(α=0.873434) | +| `output/baseline_spot/` | 8-10 LED spot 配光 | `configs/scene_baseline_spot.yaml`(18 LED を spot に変換, cutoff=90/beam=30, I_0=0.02678 W/sr, flux 保存) | `configs/eval_baseline_linear.yaml` (linear + normalization) | 146.71 / 0.1887 / 27.69 / 1.383 | TEST_01 Step 8-10 検証.X 方向の対称性が部分改善(目視).α=12.77 で mean が約 14.6× 暗くなる | ### sweep_summary.csv の値(参考、TEST_01 line と一致確認用) @@ -53,6 +54,8 @@ | LED 位置を Fusion STL から再抽出 | `uv run python scripts/extract_led_positions_from_stl.py configs/meshes/SmTIASv3.stl --output configs/leds_extracted.yaml`(その後 scene_baseline.yaml の lights セクションを置換) | | baseline_v3_leds レンダ生成 | `$env:DRJIT_LIBLLVM_PATH = "C:\Program Files\LLVM\bin\LLVM-C.dll"; uv run sm-render --scene configs/scene_baseline.yaml --output output/baseline_v3_leds/` | | baseline_v3_leds 評価 | `uv run sm-evaluate --image output/baseline_v3_leds/luminance.npy --config configs/eval_baseline_linear.yaml --output output/baseline_v3_leds/` | +| baseline_spot レンダ生成 (Step 8-10) | `$env:DRJIT_LIBLLVM_PATH = "C:\Program Files\LLVM\bin\LLVM-C.dll"; uv run sm-render --scene configs/scene_baseline_spot.yaml --output output/baseline_spot/` | +| baseline_spot 評価 | `uv run sm-evaluate --image output/baseline_spot/luminance.npy --config configs/eval_baseline_linear.yaml --output output/baseline_spot/` | ## 過去の整理実績 (Cleanup History) diff --git "a/docs/06_TEST/TEST_03_\350\274\235\345\272\246\343\203\236\343\203\203\343\203\227\345\275\242\347\212\266\350\277\275\350\267\241.md" "b/docs/06_TEST/TEST_03_\350\274\235\345\272\246\343\203\236\343\203\203\343\203\227\345\275\242\347\212\266\350\277\275\350\267\241.md" index 19e2a83..1b03ac5 100644 --- "a/docs/06_TEST/TEST_03_\350\274\235\345\272\246\343\203\236\343\203\203\343\203\227\345\275\242\347\212\266\350\277\275\350\267\241.md" +++ "b/docs/06_TEST/TEST_03_\350\274\235\345\272\246\343\203\236\343\203\203\343\203\227\345\275\242\347\212\266\350\277\275\350\267\241.md" @@ -44,6 +44,7 @@ | Step 8-3 (OETF 適用後,sRGB) | 数値指標は半減(CoV 0.183 → 0.089)したが,**輝度マップの形は出発点と区別がつかない**.OETF はピクセル値の単調変換で**相対順序を変えない**ため形は構造的に不変 | Step 8-1 と全く同じ形.CoV だけが半減(OETF の非線形圧縮で分布が縮む)が,飽和パターンは継続 | | Step 8-7 (発光面 size 1.5×1.5,linear + mean 正規化) | 絶対値スケールは 2.83× 上昇したが,**形は出発点と同じ Y 軸単調勾配**.形状指標がほぼ不変だったことと整合 | Step 8-1 と区別不能の形.mean 正規化で表示スケールが完全に揃う | | Step 8-9 (LED 位置を Fusion STL から正確化,linear + mean 正規化) | LED 位置を「3+ギャップ+3」の実機配置に直したが,**形は依然として Y 軸単調勾配**.数値指標 CoV/勾配/C/P も 1% 以内の微変化 | Step 8-1 と区別不能の形.LED 位置の数 mm 誤差は多重反射で平滑化される | +| Step 8-10 (LED 配光を spot emitter で表現,cutoff=90/beam=30) | 数値指標は悪化(CoV +4%, 勾配 +15%, C/P +5%)が,**目視で下端の明るい領域が中央寄りに集中**.**X 方向の対称性が部分的に改善**.Y 方向の偏りは不変 | 他の 4 シミュは下端が X 全幅で明るいが,spot は下中央のみが集中して明るい.左右端はやや暗く,**実機の中央ピーク楕円の X 方向成分に部分的に符号一致** | ## 学び (Findings) @@ -64,8 +65,9 @@ | 8-7 発光面サイズ検証 | 局所幾何との縮退(絶対値変化)+ 点光源近似 | 形不変 | | 8-8 linear mean 正規化 | 表示用の線形ゲイン(指標は不変) | 形不変 | | 8-9 LED 位置の Fusion STL 反映 | LED 数 mm 移動,多重反射で平滑化 | 形不変 | +| 8-10 LED 配光を spot emitter | LED 中央集中型配光で X 方向の対称性に変化 | **X 方向の対称性が部分改善**(Y 方向は不変) | -これらはすべて **線形ゲイン・単調変換・局所幾何依存のいずれか** であり,**「ピクセル間の空間構造」を変える物理を含まない**. +8-10 まで **「形を変える可能性のある変更」は LED 指向性のみ**であり,これは数値指標を悪化させたが目視で X 方向の対称性向上を確認.これが Step 8-1〜8-9 の「形を変えない変更」とは性質が違う(線形ゲインや単調変換ではない).ただし依然として **Y 方向の中央ピーク**は LSC モデル化が必要. ### 3. 実機の形 = シミュの形 + 動径補正 diff --git a/scripts/build_luminance_evolution_panel.py b/scripts/build_luminance_evolution_panel.py index f62b4a4..e31a750 100644 --- a/scripts/build_luminance_evolution_panel.py +++ b/scripts/build_luminance_evolution_panel.py @@ -124,6 +124,12 @@ "title": "Step 8-9: LED Positions from Fusion STL", "subtitle": "Accurate LED layout (3+gap+3 per board), linear + mean normalization", }, + { + "loader": _load_sim_linear, + "args": (ROOT / "output" / "baseline_spot" / "luminance.npy",), + "title": "Test: Spot Emitter (data-sheet config)", + "subtitle": "cutoff=90, beam_width=30 (FWHM 120deg), flux-preserved", + }, ] diff --git a/src/sm_tias_lightsim/scene/lights.py b/src/sm_tias_lightsim/scene/lights.py index 8b842fe..b8907ba 100644 --- a/src/sm_tias_lightsim/scene/lights.py +++ b/src/sm_tias_lightsim/scene/lights.py @@ -8,7 +8,13 @@ import mitsuba as mi from sm_tias_lightsim.scene.materials import _to_rgb -from sm_tias_lightsim.scene.schema import AreaLight, DirectionalLight, LightConfig, PointLight +from sm_tias_lightsim.scene.schema import ( + AreaLight, + DirectionalLight, + LightConfig, + PointLight, + SpotLight, +) def _build_area_light_transform( @@ -117,5 +123,34 @@ }, } + if isinstance(light, SpotLight): + # spot emitter は to_world で位置と光軸方向を指定する. + # Mitsuba の spot は to_world @ (origin=(0,0,0), target=(0,0,1)) を基準とし, + # to_world で世界座標へ変換するので look_at で組み立てる. + cx, cy, cz = [c * position_scale for c in light.position] + dx, dy, dz = light.direction + norm = (dx**2 + dy**2 + dz**2) ** 0.5 + if norm == 0.0: + msg = "SpotLight direction vector must not be zero." + raise ValueError(msg) + dx, dy, dz = dx / norm, dy / norm, dz / norm + if abs(dy) < 0.9: + up = [0.0, 1.0, 0.0] + else: + up = [0.0, 0.0, 1.0] + origin = [cx, cy, cz] + target = [cx + dx, cy + dy, cz + dz] + to_world = mi.ScalarTransform4f().look_at(origin=origin, target=target, up=up) + return { + "type": "spot", + "to_world": to_world, + "cutoff_angle": light.cutoff_angle, + "beam_width": light.beam_width, + "intensity": { + "type": "rgb", + "value": _to_rgb(light.intensity), + }, + } + msg = f"Unsupported light type: {type(light).__name__}" raise ValueError(msg) diff --git a/src/sm_tias_lightsim/scene/schema.py b/src/sm_tias_lightsim/scene/schema.py index d0e4383..d05edd9 100644 --- a/src/sm_tias_lightsim/scene/schema.py +++ b/src/sm_tias_lightsim/scene/schema.py @@ -174,9 +174,27 @@ irradiance: float | Vec3 = Field(..., description="照度(スカラまたは RGB タプル)") +class SpotLight(_FrozenBase): + """スポット光源(円錐配光・点光源ベース). + + Mitsuba 3 の `spot` emitter に対応.LED の指向性をデータシートの配光角に + 合わせて表現するために使用する.`cutoff_angle`(光が 0 になる角度)と + `beam_width`(中央フラット領域)の組み合わせで smoothstep 補間の配光を + 定義する.半値全幅(FWHM) = `(cutoff_angle + beam_width) / 2 × 2`. + """ + + id: str = Field(..., description="シーン内ユニーク ID") + type: Literal["spot"] + position: Vec3 = Field(..., description="位置 [mm]") + direction: Vec3 = Field(..., description="光軸方向ベクトル") + cutoff_angle: float = Field(..., description="光が完全に 0 になる角度(度,0 < cutoff ≤ 90)") + beam_width: float = Field(..., description="中央フラット領域の半角(度,0 ≤ beam_width ≤ cutoff_angle)") + intensity: float | Vec3 = Field(..., description="軸上強度 I_0 [W/sr](スカラまたは RGB タプル)") + + # type フィールドで識別される discriminated union LightConfig = Annotated[ - PointLight | AreaLight | DirectionalLight, + PointLight | AreaLight | DirectionalLight | SpotLight, Field(discriminator="type"), ]