ライブで試す 説明
「Bluestone Lane」は、ブラウザで動く 3〜4 分のリアルタイム作品です。夕暮れどき、取り壊しを待つ古い通り。向かいの店はどこもシャッターを下ろし、壁にはペンキの丸で囲んだ「拆」(取り壊し)の字が書かれ、建物の 1 棟はすでに半分取り壊されています。まだ半分開いているシャッターは 1 つだけ。そこは小さな家電修理店で、年老いた修理職人がラジオの筐体、カメラのレンズ、水道管から組み上げたロボットが、いまも作業台の脇のスツールに座っています。
クリックして始めると、カメラは通りから歩くように店へ入り、ロボットの顔の前へ回り込みます。マウスの左ボタンかスペースキーを長押しすると、レンズの絞りが開きます——ただし最初の 1 回は必ず失敗するので、いったん離してもう一度長押ししてください。押したままマウスを動かすと頭がそのあとを追い、その光が当たったところに過去が戻ってきます。
第一幕の舞台は店の中です。光の外は現在の姿。何もかもがほこりをかぶって色あせ、棚にはクモの巣が張り、床には剥がれた漆喰のかけらと割れたガラスが散らばり、段ボール箱の上のテレビも、掛け時計も、卓上扇風機も、真空管ラジオも壊れています。光が当たったところでは、物が昔のままの姿に戻ります。テレビには番組が映り、ラジオはダイヤルが灯って曲を流し、時計は振り子を揺らします。
しばらくすると、同じ曲が通りの向こうからかすかに聞こえてきます。ロボットが首を回して耳を傾けると、視点は戸口まで引いていきます。光は揺らぎ、曲には雑音が混じり、映像は何度も同期を失い、最後にもう一度映像が裂けたところで第二幕へ切り替わります。
第二幕の舞台は通りです。向かいに並ぶ 5 軒の店のうち、光が当たった店は 1990 年代半ばのある夕暮れに戻ります——店内には明かりがともり、ベランダには洗濯物が干され、戸口の低い腰掛けには近所の人たちが座っています——そして、その店の音が聞こえてきます。見つけた店は明かりがともったままになり、まだ見つけていない店は看板と音であなたを呼びます。見ているあいだにも電力は減っていきます。最後のまなざしは、あの曲が流れてくる電器店へと戻り、通り全体が一瞬だけ明るくなります。やがて電力が尽き、光はちらついて消え、曲はフレーズの途中で途切れ、通りは今の姿に戻ります。エンディングでは店の中から外を見ます。ロボットはなおも暗い通りのほうを向き、向かいで明かりがひとつともり、最後の一文はこうです。「明かりのともった通りを、覚えている。」
エンドカードのあとにクリックすると、もう一度見られます。インターフェースは既定で英語で、タイトル画面のリンクから中国語に切り替えられます。スマートフォンでは、画面を長押しして指をスライドさせます。ヘッドホンの使用と、新しいバージョンの Chrome や Edge など WebGPU に対応したブラウザをおすすめします。WebGPU 非対応のブラウザでは自動的に WebGL2 に切り替わります。初回の読み込みではモデルのダウンロードとシェーダーのプリコンパイルを行うため、少し時間がかかります。
Tripo の活用方法
修理店の 20 点を超えるオブジェクトを Tripo で生成しました。ロボット、真空管ラジオ 2 台(修理のために開けたものと閉じたもの)、ブラウン管テレビ 2 台、卓上扇風機、掛け時計、目覚まし時計、カセットレコーダー、電気スタンド、はんだごて、拡大鏡、工具箱、パーツキャビネット、作業台、28 インチの実用自転車、魔法瓶、ホーローのマグカップ、そして予備の腕・脚・胴体です。さらに、壊れたバージョンを 4 点、特別に作っています。部屋の壁と床、棚とその上の段ボール箱、ケーブル、工具を掛けた壁、作業台の上の小物は、コードで組んだジオメトリです。通りの向かいに並ぶ店はモデルではなく、生成した 2 枚の写真(「現在」と「過去」)です。
- 参考画像からモデルへ:どのオブジェクトも、まず無地のライトグレーの背景に置いた製品レンダリング画像を用意し、それを Tripo に渡します。モデリングに入る前に、開いた状態の真空管ラジオで 3 つの方法を比較しました——画像から 3D を直接生成する方法、画像からマルチビューを経て 3D にする方法、手作業で組んだ四面図から 3D にする方法です。大半のモデルは「画像 → マルチビュー → 3D」のルートを採り、ジオメトリとテクスチャは高精細、PBR マップ付きで、テクスチャに焼き込まれたライティングは取り除いています。こうして、電気スタンド、外の夕暮れ、ロボットの光がライティングを担うようにしました。
- ロボット:Tripo から出てきたロボットは、頭と胴体とスツールが一体になった 1 つのメッシュでした。作者は頭を切り離すのではなく、頂点シェーダーで「やわらかい首」を与えました。位置から計算するウェイトは、ラジオの筐体でできた頭で 1、胴体と肩で 0 になり、首とそのまわりのケーブルにかけてなだらかに変化します。そのため頭を回すと、各頂点は回転角にウェイトを掛けた分だけ首を軸にねじれます。どこも切っていないので継ぎ目が開くことはなく、首が回るとケーブルも首に巻きつきます。スツールは Blender のスクリプトで切り離してあるので、第二幕ではロボット全体がスツールの上で回り、通りのほうを向きます。顔のクロームの「上海」エンブレムと胸の「人民电器」(人民電器)の銘板はモデル自体に含まれるジオメトリで、同じデザインから、壁のフックに掛けられたり作業台に立てかけられたりしている予備の腕・脚・胴体も生成しました。
- 壊れたバージョン:光の外の現在と光の中の過去がきちんと対応するよう、段ボール箱の上のテレビ、掛け時計、卓上扇風機、真空管ラジオには、それぞれ 2 つ目の壊れたバージョンを用意しました。参考画像を壊れた見た目に加工し、同じ Tripo の設定でモデル化したものです。2 つのバージョンは同じ場所に置かれ、光が当たったピクセルには無傷のほう、それ以外には壊れたほうが表示されます。
- 生メッシュをウェブへ:Tripo の生モデルは 1 点あたり約 190 万の三角形と 4K テクスチャ(ロボットは 8K)を持っています。Blender でデシメートするとテクスチャの継ぎ目が裂け、近くで見ると白いひびが残るため、作者はその方法を採らず、meshopt で継ぎ目を保ったまま簡略化していくつかの詳細度レベルに分け、レベルごとに誤差を実測しました。実行時には各オブジェクトで、誤差が画面の 1 ピクセル未満に収まり、テクスチャが画面より粗くならないレベルだけを表示します。遠くでは簡略なレベル、近づくにつれて細かいレベルです。公開版では、最も細かいレベルと生メッシュとの差はコンマ数ミリ以内です。粗いレベルから先に読み込み、残りは必要に応じてストリーミングし、ビデオメモリが予算を超えると再び解放します。
- モデルビューアー:アドレスに ?review を付けると検査モードになり、明るい検査用ライティングに切り替わります。ドラッグで周回、スクロールでズーム、1–8 キーでプリセットの視点に切り替えられます。どれかのモデルをダブルクリックするとカメラがそこまで滑るように近づき、左下にそのモデル名と、Tripo の生モデルの三角形数が表示されます。
ブラウザで動く仕組み
ゲームエンジンは使っていません。three.js の WebGPU レンダラーの上に Vite で直接構築しており、WebGPU 非対応のブラウザでは自動的に WebGL2 に切り替わります。
過去はピクセル単位で計算されます。ロボットの目は光の円錐を投げかけ、レンダリング時にすべてのピクセルがその内側にあるかどうかを判定します。円錐の内側では無傷のモデル、電源の入ったテレビ、暖かみのあるカラーグレーディングに、外側ではほこりをかぶって色あせた現在になり、壊れたバージョンと無傷のバージョンもこの同じマスクで入れ替わります。通りの向かいの 2 枚の写真も同じ方法で現在と過去を切り替え、電器店のショーウィンドウの中は本物の小さな 3D の店になっていて、そのテレビにも番組が映っています。
すばやく開き、なめらかに動くのは、3 つの工夫によるものです。シェーダーはすべて非同期かつ並列でコンパイルされ、シェーダーの準備がまだのオブジェクトは、フレームを待たせずにそのフレームだけ描画を飛ばします。ロード画面では、すべてのオブジェクトをすべてのパス(深度プリパス、メインパス、各シャドウマップ)で 1 回ずつ描画しておくので、クリックして始めたあとは物語の途中でコンパイルが走ることはありません。そして、レンダリングは約 210 万ピクセル(おおよそ 1080p)を上限とし、フレーム時間が長くなると自動的に解像度を下げます。モデルのレベルが切り替わるときは、両方のレベルを互いに補い合うランダムなピクセルの組に分けて同時に描画し、0.35 秒かけてクロスフェードさせ、テンポラルアンチエイリアスでなじませるので、近くで見ても「パッ」と切り替わることはありません。アドレスに ?q=low を付けると、低画質モード(アンビエントオクルージョンとスクリーン空間反射なし、シャドウマップは半分のサイズ)になります。
サウンドは、CC0 のフィールドレコーディングに Web Audio の合成音を重ねています。外の夕暮れの通り、蛍光灯と電源のハム音、振り子の刻む音、テレビのテストトーン、ロボットのサーボ音と目が開く音。ラジオから流れる曲は、ペンタトニックによるオリジナルのメロディです。第二幕では、向かいの店それぞれに固有の音があります。
制作の過程で、作者は three.js r186 の WebGPU レンダラーのバグも修正しました。2 つのモデルの違いが法線の格納形式(Int8 か Int16 か)だけの場合、レンダラーはそれらに 1 つのパイプラインを共有させてしまうため、後から描画されるほうが頂点を誤った形式で読み込み、表面のライティングが迷彩柄のように乱れます。WebGL2 には影響しません。


