駐車場デジタルツインの3D描画とモバイル実装
精細な園区モデルと全画面の没入感を維持し、車両インスタンス、保守的LOD、描画解像度の予算、必要時だけ開く情報パネルを実装した記録。
2026-10-03 更新:精細な全画面シーンを維持し、三台の自動走行、四輪の回転、固定距離の追従、元の回路アニメーションを追加しました。
駐車場デジタルツインでは、園区を自由に回転・拡大し、エリアや車両へ移動する体験が中心になります。統計やグラフは常設するのではなく、必要なときに開く設計にしました。

見た目を制約条件にした最適化
元の Angular 18 / Babylon.js 7 プロジェクトには約616 MBのリソースがあり、標準園区 GLB は164,807,820バイトでした。大きな二つのコンポーネントにモデル、カメラ、チャート、イベント、タイマーが混在していました。
最初の軽量化では、テクスチャ削除と箱型モバイルモデルによって細部を失いました。そこで元の建築、窓、道路標示、駐車面、車両材質を維持し、重複する幾何から削減する方針へ変更しました。
| リソース | PC | モバイル |
|---|---|---|
| 園区 GLB | 18,953,916バイト | 16,444,988バイト |
| 車両テンプレート GLB | 1,420,956バイト | 1,350,928バイト |
| 園区の画像テクスチャ | 18 | 18 |
| 車両の画像テクスチャ | 7 | 7 |
| 駐車車両インスタンス | 126 | 126 |
園区と車両の合計は約20.37 MB / 17.80 MBで、元の標準園区ファイルに対して約87.6% / 89.2%削減しました。端末は該当する一組の LOD だけを読み込みます。モバイルでも同じ建物と材質の層を維持します。
精細車両の共有と静的メッシュの集約
元モデルの重複車両から位置と向きを抽出し、一つの精細車両テンプレートで置き換えました。七種類の材質を保持し、四輪とホイールを独立させた各部品へ126台分の変換行列を thin instances として渡します。
const matrices = new Float32Array(placements.matrices.flatMap(matrix =>
Array.from(partTransform.multiply(Matrix.FromArray(matrix)).asArray())));
mesh.thinInstanceSetBuffer('matrix', matrices, 16, true);
mesh.thinInstanceEnablePicking = true;
クリックした車両のインスタンスを識別し、その位置へ滑らかに移動できます。走行デモは元アニメーションから採取した道路軌跡を使い、カメラ追従と一時停止に対応します。
静的な園区は材質ごとに集約し、重複除去・頂点結合・保守的な減面・テクスチャサイズ調整を行います。不変の行列と材質を固定し、カメラと走行車両だけを動的に更新します。三台の走行車と独立した四輪を含む現在のシーンは121メッシュです。

車両の向きと固定距離の追従
元の車両は -Y が前、-Z が上で、幾何にも傾きがあります。四輪の位置から基底を作り、+Z 前方・+Y 上方へ正規化し、駐車行列には逆変換を適用します。タイヤとホイールは独立ノードのまま、移動距離を半径で割った角度で回転します。三つの元軌跡は開始時刻と速度をずらし、一時停止時には位置と回転を同時に止めます。
追従カメラは毎フレーム車両の向きに対する後方の固定距離・固定高さを再計算します。追従中は慣性入力を解除し、終了すると手動操作に戻ります。元の回路アニメーションは名称を匿名化して1080pへ圧縮し、読み込み中のループ、完了後の終了、スキップと動画失敗時の表示を用意しました。
全画面を維持する情報レイヤー
キャンバスは常にブラウザー全体を使います。下部の丸いメニューから、状況、エリア、記録、通知、視点、設定のパネルを開きます。初期状態はすべて非表示で、パネルを開いてもキャンバスの寸法は変わりません。
エリアのピン、通知の位置、駐車先の推薦は共通のカメラ補間を利用します。全景・真上・自動周回・車両詳細を切り替えられ、クリーンモードは情報レイヤーを隠します。復元ボタンと Esc で操作画面へ戻れます。
業務状態と描画は分離しています。signals の共通データから空き数や推薦先を算出し、模擬記録を五秒ごとに更新します。3000回のテストで容量の境界と一台単位の保存則を確認しました。統計の300台はデモ用の論理容量で、126台のモデルとリアルタイムに一対一対応するものではありません。
解像度の予算とモバイルの品質
モデルが精細でも、内部描画サイズが CSS サイズを下回ると画面はぼやけます。標準では CSS ピクセルの二倍で描画し、モバイル400万・PC800万ピクセルの予算を設定しました。1600×900のPC画面は3200×1800、844×390のモバイル画面は1688×780で描画します。
const ratio = Math.max(1, Math.min(desiredRatio,
Math.sqrt(pixelBudget / Math.max(1, width * height))));
engine.setHardwareScalingLevel(1 / ratio);
低フレームレートが続く場合は、追加の発光効果や描画頻度を先に減らし、ネイティブ解像度以下へ自動的に下げません。FPS は実際の render 呼び出しから計測します。測定値は試験環境の観測であり、すべての実機に共通する保証値ではありません。
モバイルは保守的な LOD を使い、建物と車両のテクスチャを維持します。一本指で回転、二本指でズームし、下部ボタンは44ピクセルです。縦向きではシーンを横方向へ回転させ、入力座標も逆変換します。

全画面ボタンはシステムの横向き固定を試みます。方向固定に対応しないブラウザーでも CSS の横向き表示を利用します。標準は WebGL2、?renderer=webgpu で能力検査済みの WebGPU を明示的に選択できます。
匿名化と配信の検証
匿名化では画面上の名称だけでなく、立体看板、建物に焼き込まれた文字、水印、ナンバープレート、メタデータを確認しました。元の看板と二か所の文字を除去し、両側から読める汎用看板を生成します。そのほかのテクスチャは保持し、旧監視映像や固定機器アドレスは公開しません。
場面クラスがエンジン、ResizeObserver、入力と終了処理を所有します。非表示タブでは描画と模擬更新を止め、ルート変更時にエンジン・監視・イベント・タイマーを解放します。読み込みが途切れた場合は精細モデルの再試行を表示します。
/smartParking/ と /smartParking/mobile を Nginx の静的サイトとして配信し、深いURLから直接更新できます。時刻付き release と設定バックアップを用意してリンクを原子的に切り替え、検証失敗時は旧バージョンへ戻します。
型検査、八つのデータ・運動テスト、四つの GLB と車輪・軌跡の契約、走行・固定追従・低速読み込み・縦横タッチ操作を含む四十六のローカルブラウザー検証を実施しました。配信後には HTTPS 入口を別途確認します。保持したい体験を先に決め、重複処理や不要な常設情報を削減することが、品質と性能の両立につながりました。