[JavaScript] 3D ROOM制作 2日目(前) 初期設定ウィザードとスカイボックス実装 (Three.js)

[JavaScript] 3D ROOM制作 2日目(前) 初期設定ウィザードとスカイボックス実装 (Three.js)

はじめに

何となく作り始めた 3D ROOMのWEBアプリ制作、2日目。

昨日は、プロトタイプとしてROOMデータを読み込みVRMアバターを動かすまで作ったので、今日は、初期設定画面制作。

その前に、昨日の段階で基本実装とはいえ、かなり実装コードが増えて1000行を越えていた為、リファクタリングから着手。

前回の記事:

[JavaScript] 3D ROOM制作 2日目(前) 初期設定ウィザードとスカイボックス実装 (Three.js) [JavaScript] 3D ROOM制作 2日目(前) 初期設定ウィザードとスカイボックス実装 (Three.js) [JavaScript] 3D ROOM制作 2日目(前) 初期設定ウィザードとスカイボックス実装 (Three.js) [JavaScript] 3D ROOM制作 2日目(前) 初期設定ウィザードとスカイボックス実装 (Three.js) [JavaScript] 3D ROOM制作 2日目(前) 初期設定ウィザードとスカイボックス実装 (Three.js)

リファクタリング

1日目の1000行のapp.jsを4ファイルに分割。

app.js          217行  ← state、init、lights、render loop
room-loader.js  327行  ← GLB/部屋/家具/スカイボックス/ポータル
avatar.js       142行  ← VRM読み込み/アニメーション/更新
controls.js     227行  ← 入力/衝突判定/移動/カメラ/ポータル判定
setup.js        230行  ← 初期設定ウィザード(新規)

共有stateはSTATEオブジェクトとしてapp.jsからexport、各モジュールは関数の引数Sで受け取る。ESMのimport/exportだけで依存管理。バンドラー不要。

初期設定ウィザード

Click to Start画面を4ステップのウィザードに置き換え。

ROOM選択 → アバター選択 → スカイ選択 → 名前入力 → START

ROOM選択はmodels.jsonからサムネイル・クリエイター名・ライセンス・ポリゴン数を読んでグリッド表示。クリックで選択すると、そのモデルのdefaults(scale、pos、spawn)が自動適用される。

アバターは男女2体のデフォルトVRM + カスタムVRMアップロード。アップロードしたVRMはIndexedDB(Dexie)にArrayBufferとして保存。次回起動時にblobURLで復元して読み込む。

設定情報はlocalStorageに保存。2回目以降はウィザードをスキップして直接ルームに入る。

スカイボックス4プリセット

全部Canvas描画のプロシージャルテクスチャ。HDRIは使わない。GPUコストほぼゼロ。

Night:暗い星空 + 月 + ムーングロウ。星は暖色(#fff1ce)で半径0.25以下、低アルファで自然な見た目に。SphereGeometryに貼って低速回転。

Sunset:暖色グラデーション + 雲レイヤー。雲は別のSphereGeometry(半径195)に透明テクスチャで描画。メインの空球体と異なる速度で回転させて奥行き感を出す。

Day:青空 + 白い雲。同じ二重天球方式。

WIRED:黒背景 + 緑グリッドオーバーレイ + 星。lain-labの世界観。回転速度を他より速く。

ノイズは使わない。雲も星も事前にCanvasに描いた静的テクスチャを回転させるだけ。ノイズが活きるのは「毎フレーム変化し続けて同じ模様が二度と出ない」必要がある時だけ。空はゆっくり動いてるように見えればいいので、テクスチャ回転で十分。

export const SKY_PRESETS = {
    night: {
    name: 'Night',
    topColor: '#000008', midColor: '#050515', bottomColor: '#0a0510',
    stars: true, moon: true, rotSpeed: 0.003,
    bgColor: '#050515',
  },
  sunset: {
    name: 'Sunset',
    topColor: '#0a0a3a', midColor: '#cc4400', bottomColor: '#ffaa33',
    stars: false, moon: false, rotSpeed: 0.005,
    clouds: true, cloudColor: 'rgba(60,20,10,0.3)',
    bgColor: '#cc4400',
  },
  day: {
    name: 'Day',
    topColor: '#1a6bcc', midColor: '#55aaee', bottomColor: '#aaddff',
    stars: false, moon: false, rotSpeed: 0.002,
    clouds: true, cloudColor: 'rgba(255,255,255,0.6)',
    bgColor: '#55aaee',
  },
  wired: {
    name: 'WIRED',
    topColor: '#000000', midColor: '#0a0a1a', bottomColor: '#001100',
    stars: true, moon: false, rotSpeed: 0.008,
    grid: true,
    bgColor: '#0a0a1a',
  },
};

Dexie(IndexedDB)

素のIndexedDB APIからDexieに移行。CDN(jsdelivr)からimportmapで読み込み。

import Dexie from 'dexie';

const db = new Dexie('RoomDB');
db.version(1).stores({
  assets: '',
});

3関数(openDB, saveToIDB, loadFromIDB)が2行のput/getに簡略化。今後のPhase 3でセーブスロットや家具配置データの保存が増える時もテーブル追加だけで済む。

今後

Phase 3のエディタ機能。右クリックメニューでROOM調整モード(scale/pos/rotスライダーでリアルタイムプレビュー)、家具配置、メディア配置(画像・動画を壁に貼る)。展示空間としての機能を足していく。