本ドキュメントは,実機の単灯(下/左/右)実測データとシミュレーションを 1 灯ずつ突き合わせた結果,シミュ側の面光源(area emitter)変換に座標軸の取り違えバグが見つかり,これが「下灯がほかの 2 倍強い」という長年の不一致の主因だったことの発見・原因・修正・検証を記録する.
⚠️ 本件により TEST_07 LED グループ別動作確認 の結論「下灯 2.1× は物理的に正しい/意図どおり」は誤りであったことが判明した(TEST_07 はバグ込みの出力を物理的に正当化してしまっていた).TEST_07 冒頭にも訂正注記を追加済み.
実機(SmTIAS-Evaluation)で 3 灯を 1 灯ずつ単独点灯して撮影した白板 ROI 輝度マップ一式を入力として受領した(lightsim/:luminance_maps/{bottom,left,right}_*.npy,0〜255 linear / Rec.709 luma / LSC 補正済 / portrait).
ユーザー観察:「シミュでは下側がほかの約 2 倍になっているが,実機では 3 灯ほぼ均等」.これを灯ごとに分解して原因を特定するのが目的.
灯ごとの白板 ROI 平均輝度(実機)と,既存シミュ単灯出力(output/led_group_sanity/,spp=1024)を比較:
| 灯 | 実機 mean | 実機 比 | シミュ(バグ) mean | シミュ(バグ) 比 |
|---|---|---|---|---|
| bottom | 57.75 | 1.00(最暗) | 98.82 | 2.10 |
| left | 61.46 | 1.06 | 47.08 | 1.00 |
| right | 63.55 | 1.10 | 47.10 | 1.00 |
→ 実機は 3 灯ほぼ均等で下灯が最暗.シミュは下灯だけ 2.10×で,しかも左右が 47.08 vs 47.10 と完全対称.傾向が逆転している.
LED 定義(configs/leds_extracted.yaml)は全 18 灯が direction:(0,0,-1)・radiance:2807.25 で完全同一.LED の位置・向き・強度は Fusion 360 STL 由来で実機どおりであり,radiance 差では説明できない.したがって原因は LED 以外(周囲幾何・変換処理)にあると絞り込んだ.
Mitsuba 3 の rectangle 形状を実際に生成して確認した:
中心法線 n = (0, 0, 1) # 面は +Z を向く 面内範囲 = X∈[-1,1], Y∈[-1,1] # XY 平面に寝た 1 辺 2 の正方形 Z 方向 = 厚みゼロ(潰れている)
→ rectangle は「XY 平面・法線 +Z」.サイズを与えるには 横 = X 軸,高さ = Y 軸をスケールしなければならない(Z は潰れているのでスケールしても無意味).
src/sm_tias_lightsim/scene/lights.py _build_area_light_transform:
# 修正前(バグ) scale = mi.ScalarTransform4f().scale([sw / 2.0, 1.0, sh / 2.0]) # 横X 縦Y 奥Z # 横(X) = sw/2 … OK # 縦(Y) = 1.0 … ★ スケールされず基準のまま = ±1 m(2000 mm) # 奥(Z) = sh/2 … 高さを“潰れた厚み方向”に入れてしまい無効
コード上のコメントは「rectangle は XZ 平面上の正方形(Y 軸正方向が前面)」と書かれていたが,実体は XY 平面・+Z 前面.前提を 1 軸勘違いしたまま実装されており,高さ sh を縦(Y) ではなく奥行き(Z) に入れ,縦(Y) は無補正のまま放置されていた.
単位はメートル系(mm→m に position_scale=0.001 済)なので,縦が無補正 = ±1 m.結果:
実物 2.8 × 3.5 mm の小さな LED が,シミュ内では 「幅 ~3 mm × 高さ 2000 mm」の細長い巨大ストリップになっていた.
法線(発光方向)は look_at で正しく direction を向くため,発光の向き自体は正しい(このバグでは形状サイズだけが壊れる).
全 18 灯が同一の look_at(direction=(0,0,-1), up=(0,1,0))を使うため local Y→world Y.つまり全 LED が world Y 方向に 2 m 伸びた縦ストリップになる.各灯列の横位置(X)と評価板の関係:
| 灯 | LED 横位置 X | 評価板 X 範囲 [-49.5, +49.5] との関係 |
|---|---|---|
| bottom | X∈[-10, +18] | 板中央の真裏(中央に密集) |
| left | X = -66 | 板の外(左端より 16.5 mm 外側)+側壁/カバー脇 |
| right | X = +66 | 板の外(右端より 16.5 mm 外側)+側壁/カバー脇 |
2 m 縦長化により,板中央の真裏にある下灯だけが板中央を効率よく照らし,板の外側にある左右灯は伸びた光の多くが壁/カバーに当たって板に届きにくい.本来の小さな ~3.5 mm 面光源ならこの縦長効果は生じず偏らない.バグによる巨大化が,たまたま中央裏にある下灯を過剰に効かせて 2.1× を生んでいた.
TEST_07 はこの 2.1× を「直接光効率の幾何的優位=意図した設計」と解釈してしまったが,実際は変換バグによるアーティファクトだった.
スケールの軸を 1 か所入れ替えるのみ:
# 修正後 scale = mi.ScalarTransform4f().scale([sw / 2.0, sh / 2.0, 1.0]) # 横X 縦Y 奥Z # 横(X) = sw/2 縦(Y) = sh/2 奥(Z) = 1.0(潰れた厚みは無補正でよい)
高さ sh を,誤って入れていた奥行き(Z) から本来の縦(Y) へ移しただけ.これで LED が実物どおりの ~2.8 × 3.5 mm に戻る.
修正後に 3 単灯+全灯合成を再レンダ(単灯 output/sanity_fix_1024/,全灯 output/baseline_fix_1024/,いずれも spp=1024 / max_depth=16).暫定 spp=256 出力は output/led_group_sanity_fix/.
| 灯 | バグ 比 | 修正後 比 | 実機 比 |
|---|---|---|---|
| bottom | 2.099 | 1.000(最暗) | 1.000(最暗) |
| left | 1.000 | 1.028 | 1.064 |
| right | 1.000 | 1.026 | 1.100 |
→ 下灯 2 倍は消滅し,3 灯が ±3% で均等化.さらに**「下灯が最暗」という実機の順序まで一致**した.灯間の振幅バランスは実機パターンに乗った.
| 灯 | バグ peak/mean | 修正後 peak/mean | 実機 peak/mean |
|---|---|---|---|
| bottom | 1.60 | 1.78 | 1.22 |
| left | 1.78 | 1.69 | 1.27 |
| right | 1.73 | 1.75 | 1.20 |
単灯 3 本の和と全灯合成の ROI mean を比較:相対誤差 0.34%.Mitsuba の理論線形性どおりで,レンダの健全性を確認.
| シーン | linear CoV | 対実機残差 | center/周辺比 | gradient |
|---|---|---|---|---|
修正前(baseline_v3_leds,バグ) | 0.1815 | 2.61× | 1.318 | 24.13 |
修正後(baseline_fix_1024) | 0.1641 | 2.36× | 1.311 | 23.71 |
| 実機 | 0.0695 | — | — | — |
(参考:OETF/sRGB ドメインでは修正後 CoV=0.0790)
→ 合成 CoV は 0.1815→0.1641(残差 2.63×→2.36×)と小改善にとどまる.center/周辺比・勾配はほぼ不変.灯間バランスは直ったが,合成の主な不均一(中央明・縦勾配)は灯間バランスではなく各単灯フットプリントの拡散不足に由来するため,本バグ修正だけでは合成 CoV は大きく動かない(ユーザーの事前直感どおり).
output/led_group_sanity{,_fix}/ の索引src/sm_tias_lightsim/scene/lights.py _build_area_light_transformlightsim/(単灯実測.出典 SmTIAS-Evaluation TEST_04)