[JavaScript] 3D ROOM制作 8日目 Render・Deno Deploy配置 + スロープ実装

[JavaScript] 3D ROOM制作 8日目 Render・Deno Deploy配置 + スロープ実装

今日やったこと

  • Render に WIRED ROOM をデプロイ(5サーバー目)
  • Deno Deploy に WIRED ROOM をデプロイ(6サーバー目)
  • スロープの上り下り実装(controls.js)
  • HDRI環境マップ + トーンマッピング導入
  • Render の CORS 罠を発見・解決
[JavaScript] 3D ROOM制作 8日目 Render・Deno Deploy配置 + スロープ実装 [JavaScript] 3D ROOM制作 8日目 Render・Deno Deploy配置 + スロープ実装 [JavaScript] 3D ROOM制作 8日目 Render・Deno Deploy配置 + スロープ実装 [JavaScript] 3D ROOM制作 8日目 Render・Deno Deploy配置 + スロープ実装

Render デプロイ(5サーバー目)

Render は Static Sites を選んで GitHub リポジトリを接続するだけ。Vercel と同じ流れ。

  • Static Site を選択
  • GitHub の ROOM_RENDER リポジトリを接続
  • Publish Directory を . に設定
  • Build Command は空欄
  • Deploy

初回デプロイで全ファイルが 404 になる問題が発生。原因はキャッシュ。「Manual Deploy」→「Clear build cache & deploy」で解決。

Render の CORS 罠

_headers ファイルが効かない。

Netlify や Cloudflare Pages と同じ形式の _headers ファイルをルートに置いたが、Render の Static Sites では無視される。

/*
  Access-Control-Allow-Origin: *

このファイルを置いても、レスポンスヘッダーに Access-Control-Allow-Origin が付かない。

$ curl -I https://room-render.onrender.com/aisle/banner.webp
HTTP/2 200
content-type: image/webp
# ← Access-Control-Allow-Origin がない

解決方法:ダッシュボードの GUI で設定する。

Render ダッシュボード → Static Site → Headers → Add Rule

  • Request Path: /*
  • Header Name: Access-Control-Allow-Origin
  • Header Value: *

これで CORS ヘッダーが付くようになった。

$ curl -I https://room-render.onrender.com/aisle/banner.webp
HTTP/2 200
access-control-allow-origin: *

他の5サービス(GitHub Pages, Netlify, Vercel, Cloudflare Pages, Deno Deploy)はすべてファイルベースかコードベースで CORS 設定できるのに、Render だけ管理画面が必須。要注意。


Deno Deploy デプロイ(6サーバー目)

Deno Deploy は他のサービスと根本的に思想が違う。

静的ファイルホスティングではない。 エッジでJavaScriptを動かすサーバーレスプラットフォーム。Cloudflare Workers と同じ系統。

つまり、ファイルを置くだけでは動かない。静的ファイルを配信するためのスクリプトが必要になる。

Deno CLI のインストール

curl -fsSL https://deno.land/install.sh | sh

パスを通して確認:

source ~/.bashrc
deno --version
# deno 2.9.2

配信スクリプト(serve.ts)

たった6行で静的ファイル配信 + CORS 設定が完結する:

import { serveDir } from "https://deno.land/std/http/file_server.ts";

Deno.serve((req) => serveDir(req, {
  fsRoot: ".",
  headers: ["Access-Control-Allow-Origin:*"],
}));

CORS ヘッダーをコード内で設定できるのが Deno Deploy の強み。_headers ファイルもダッシュボード設定も不要。

デプロイ

  1. GitHub にリポジトリ作成(ROOM_DENO
  2. serve.ts を含めて push
  3. Deno Deploy ダッシュボード(dash.deno.com)で GitHub 連携
  4. Entrypoint を serve.ts に設定
  5. Create App

7.3秒でデプロイ完了。URL は https://room-deno.lain-lab.deno.net/

$ curl -I https://room-deno.lain-lab.deno.net/room01/banner.webp
HTTP/2 200
access-control-allow-origin: *
server: deno

CORS もコード通りに動作。

各サービスの CORS 設定比較

サービス設定方法備考
GitHub Pages不要デフォルトで有効
Netlify_headers ファイルファイルベース
Cloudflare Pages_headers ファイルファイルベース
Vercelvercel.jsonJSON設定ファイル
Renderダッシュボード GUI_headers は効かない
Deno Deployserve.ts 内のコードコードで完全制御

スロープの上り下り実装

これまでの衝突判定では、段差=壁として扱っていた。スロープや緩い段差を歩けるように controls.js を改修。

上りスロープ:Floor Follow

移動後にレイキャストで床のY座標を取得し、MAX_STEP(0.5m)以内の高低差なら滑らかに追従する。

// Floor follow (upward slope only)
_rayOrigin.set(S.playerPos.x, S.playerPos.y + S.PLAYER_HEIGHT, S.playerPos.z);
_floorRaycaster.set(_rayOrigin, _rayDown);
const floorHits = _floorRaycaster.intersectObjects(S.colliderMeshes, false);
if (floorHits.length > 0) {
  const groundY = floorHits[0].point.y;
  const diff = groundY - S.playerPos.y;
  const MAX_STEP = 0.5;
  if (diff > 0 && diff < MAX_STEP) {
    S.playerPos.y = THREE.MathUtils.lerp(S.playerPos.y, groundY, 0.3);
    S.velocity.y = 0;
    S.onGround = true;
  } else if (diff <= -0.05) {
    S.onGround = false;  // 重力に任せる
  }
}

下りスロープ:重力 + レイキャスト着地

下りは floor follow ではなく重力で自然に落下させ、レイキャストで着地判定する。

// Gravity + floor snap
_rayOrigin.set(S.playerPos.x, S.playerPos.y + S.PLAYER_HEIGHT, S.playerPos.z);
_floorRaycaster.far = S.PLAYER_HEIGHT + 3.0;
_floorRaycaster.set(_rayOrigin, _rayDown);
const gravFloorHits = _floorRaycaster.intersectObjects(S.colliderMeshes, false);
if (gravFloorHits.length > 0) {
  const groundY = gravFloorHits[0].point.y;
  if (S.playerPos.y <= groundY) {
    S.playerPos.y = groundY;
    S.velocity.y = 0;
    S.onGround = true;
  }
}

_floorRaycaster.farPLAYER_HEIGHT + 3.0 に拡大したのがポイント。元の値(+ 1.0)だと、段差を降りようとした時に低い床が検出範囲外になり、移動自体がブロックされていた。

処理順序

移動(X/Z) → 重力 + 着地判定 → Floor Follow(上りのみ) → カメラ更新

この順序が重要。Floor Follow をカメラ計算の前に持ってくることで、スロープ追従が1フレーム遅れるのを防ぐ。


ビジュアル改善の試行

HDRI 環境マップ

Poly Haven から studio_small_09_1k.hdr(1.6MB)をダウンロードして scene.environment に設定。

import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';

const rgbeLoader = new RGBELoader();
rgbeLoader.load('assets/room/studio_small_09_1k.hdr', (texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping;
  S.scene.environment = texture;
});

scene.environment は背景(スカイボックス)ではなく、PBR マテリアルの反射・間接光に使われる。既存のプロシージャルスカイはそのまま。

トーンマッピング

S.renderer.toneMapping = THREE.ACESFilmicToneMapping;
S.renderer.toneMappingExposure = 1.0;

結果

ローポリモデル(468tri のバンカー)ではほぼ効果なし。マテリアルが単色でメタルネスやラフネスマップがないため、HDRI の反射が効く面がない。

高ポリ + PBR マテリアルのモデル(living room interior 等)では効果が出る。基盤として残しておく。

気づき

モデル選びの基準は tri 数でもファイルサイズでもなくテクスチャのベイク品質。ベイクドテクスチャ(ライティングが焼き込まれたテクスチャ)があれば、ローポリでもリッチに見える。リアルタイムの HDRI やシャドウに頼る必要がない。


6サーバー体制

#ホストURL
1Netlifyhttps://lain-lab.com/room/
2GitHub Pageshttps://fixtan.github.io/WIRED_ROOM/
3Vercelhttps://room-vercel.vercel.app/
4Cloudflare Pageshttps://room-cloudflare.fixjp.workers.dev/
5Renderhttps://room-render.onrender.com/
6Deno Deployhttps://room-deno.lain-lab.deno.net/

すべてポータルで接続済み。VR でサーバー間を歩いて移動できる。


リポジトリ