空と遠景
出典:room-at-dawn(MIT)、src/scene/sky.js、src/scene/parts/exterior.js より。
核となる考え方:空は一度だけ記述する。
- ベイカーが使用するのは解析的な空:地平線から天頂へのグラデーションに、太陽方向のハローを加えたもの。
- 見える空球は同じグラデーションに雲を追加するが、平均輝度をグラデーションとほぼ同じに保つ:雲間の晴天は平均より暗く、雲の端は明るく、雲の中心はほぼ等しい。
- こうすることで、窓から見える空と、部屋に差し込む光が一致する。
measureDomeはこれを測定するためのもの。
ファイル
| パス | 内容 |
|---|---|
scripts/sky.js | SKIES(dawn、day、overcast、golden)、skyRadiance(threejs-baked-gi のワーカーと同じ式)、bakeCfg、skyDome、SKY_U(uTime)、skyLights、skyEnvironment、measureDome |
scripts/backdrop.js | windowVisibility(窓の開口部の可視性と LOD)、hazePatch(大気フォグ、元のシェーダーパッチを保持)、grassTexture、roofTexture、windowTexture、crownTexture、Soup、houses、lawn、gravelPath、hedge、crownWall |
example/ | 2階の部屋(床が庭から 5.5 m 高い)から窓の外を見る:芝生、小道、生垣、3棟の家、木々、遠くの樹壁 |
example/preview*.jpg | 4種類の空を室内から見た様子;窓の前、空を見上げる;外から見下ろす |
references/sky.md | 空のシェーダーの各層、デフォルトパラメータ、調整方法、ベイクとの連携方法 |
references/backdrop.md | 可視性判定、フォグ、各遠景コンポーネント、パフォーマンスデータ、遭遇した問題、他のエンジンへの移植 |
サンプルの木は threejs-procedural-vegetation(相対パス ../../threejs-procedural-vegetation/)を借用しています。
サンプルの実行
cd <スキルフォルダ>/.. && python3 -m http.server 5426 --bind 127.0.0.1
# http://127.0.0.1:5426/threejs-sky-backdrop/example/
(<スキルフォルダ> はこのスキル読み込み時に表示される Base directory;プラグインとしてインストールした場合は ~/.claude/plugins/cache/ 以下にあります)
?sky=dawn|day|overcast|golden:空のプリセット。?cam=window|wide|up|out:部屋から見る/窓の前/空を見上げる/外から見下ろす。?cull=0:可視性カリングを行わない。比較用。?t=5:時間を固定、雲は動かない。
window.__sky には、処理時間、三角形数、葉の数、および解析的空に対する空球の比率が含まれます。
使い方
import { SKIES, skyDome, bakeCfg, skyLights, skyEnvironment, measureDome, SKY_U } from './scripts/sky.js';
const S = SKIES.dawn;
scene.add(skyDome(S)); // ベイクありの室内:display 0.3(デフォルト);屋外:skyDome(S, { display: 1 })
const cfg = { ...bakeCfg(S), portal, room }; // threejs-baked-gi の runBake に渡す
// ベイクなしのシーン:
const { sun, hemi } = skyLights(S); scene.add(hemi); if (sun) scene.add(sun, sun.target);
scene.environment = skyEnvironment(renderer, S);
console.log(measureDome(renderer, S)); // ratio 0.85–1.15
renderer.setAnimationLoop((ms) => { SKY_U.uTime.value = ms / 1000; ... });
必ず守ること
- 空は一度だけ記述する:ベイク、空球、光源はすべて同じプリセットから生成される。空の色を変えたら、再ベイクすること。
- 雲を調整したら
measureDomeを実行する。 空球を float cube map に描画し、cos 加重で上半球の平均を計算し、解析的空と比較する。- 比率は 0.85–1.15 の間であること。そうしないと、窓の外の空と差し込む光が一致しない。
{ pure: true }を指定するとグラデーションのみを描画し、結果は正確に 1.000 になるはず。これにより測定自体が正しいことを確認できる。
- 雲の色は絶対輝度であり、空と同じ単位であり、乗数ではない。グラデーションに合わせてスケーリングするよう試したところ、夜明けの比率が 0.73 に低下したため、元のプロジェクトの記述に戻した。
- ベイクありの室内では、空球を暗く描画する(
display約 0.3)。 露出は室内に合わせてある。空を実際の輝度で描画すると白くなり色が失われる。これは照明用の輝度とは無関係。 - 見えるものだけを構築する。
windowVisibilityは、部屋内の複数の目の位置から、厚みのある窓の開口部を通して外を見る。- 見えない葉:植物スキルの
lodAtに 0 を返させ、少数の大きなカードのみを残す。これによりベイク時にまだ木陰ができる。 - 斜めからしか見えないもの:1 を返し、疎にする。
- 見えない葉:植物スキルの
- 遠景には
hazePatchを追加する。 距離 10 m 以上で、地平線に向かって色をフェードアウトさせる。そうしないと、遠くの家や木が近景と同じ濃さ・彩度で表示され、貼り付けたように見える。
実測(RTX 4090、Chrome headless、1280×800)
| 項目 | 値 |
|---|---|
| 空球 ÷ 解析的空 | dawn 0.934、day 1.080、overcast 1.034、golden 1.034;pure モード 1.000 |
| 遠景(芝生、小道、生垣、家、樹壁) | 31–34 ms |
| 木(主要 4 本、中景 8 本、遠景 26 本) | 生成 0.14–0.16 秒、メッシュ構築 0.31 秒 |
| 可視性カリング | 葉 117,808 → 97,589(−17%)、三角形 42.5 万 → 38.3 万。削減の大部分は外壁に沿って窓の両側にある 2 本の木による |
| 空の環境マップと 2 回の測定 | 約 0.09 秒 |