Three.js プログラムによる 3D 都市モデリング [OpenStreetMap]

Howest での研究プロジェクトでは、Lucas Bebber の「インタラクティブ ストーリーテリングのためのアニメーション マップ パス」プロジェクトの 3D バージョンを構築することにしました。

OSM のベクター シルエットを使用して建物の形状を押し出し、アニメーション化する 3js シーンに追加します。

ここに画像の説明を挿入

推奨事項: NSDT エディターを使用して、プログラム可能な 3D シーンを迅速に構築する

1. 開発環境の設定

Node および npm パッケージを操作するために、Vite.js を使用することにしました。Vite は、最新の Web プロジェクトに、より高速で無駄のない開発エクスペリエンスを提供するように設計されたビルド ツールです。これは 2 つの主要な部分で構成されます。

  • 開発サーバーは、非常に高速なホット モジュール交換 (HMR) など、ネイティブ ES モジュールを超える豊富な機能拡張を提供します。
  • コードをロールアップにバンドルするビルド コマンド。実稼働用に高度に最適化された静的アセットを出力するように事前構成されています。

Vite を選択したのは、過去にいくつかの Vue.js プロジェクトで Vite を使用したことがあり、使い慣れており、高速で信頼性が高いことが証明されているためです。

Three.js はその人気のため、このプロジェクトの最適なフレームワークとして選択され、広範なドキュメントとチュートリアルが作成されました。

将来的にはこの研究プロジェクトを自分の Web サイトに統合できるようにしたいと考え、NPM パッケージとして開発することにしました。これには、2 つの別々のプロジェクトの作成が含まれます。1 つは実際の 3D アプリケーション用で、もう 1 つはアプリケーションを実装するテスト Web サイト用です。

プロジェクト フォルダーでは、npm init コマンドを使用して package.json ファイルが作成されます。このファイルには、名前、バージョン、依存関係、エントリ ポイント、その他の情報などのパッケージのメタデータが含まれています。Index.js はパッケージのエントリ ポイントとして機能し、src フォルダーにはコードが含まれ、example フォルダーにはデフォルトのリソースが含まれます。

計画では、明確さと保守性を高めるために、機能を個別の JavaScript モジュールに分割し、最終的には初期化されるグローバル、都市、アニメーション、パスにすることです。

2. モジュールの初期化

まずモジュールを初期化します。initialize() 関数は、シーン、カメラ、ライト、およびレンダラー オブジェクトを作成および構成し、DOM からキャンバス要素を選択し、それにレンダラーをアタッチします。また、FPS カウンターや Axis ビジュアライザーなどのデバッグ情報を有効または無効にするために使用されます。

このモジュールは、MapControl の初期化とアニメーション ループの作成に使用されますが、詳細については「対話性」セクションを参照してください。

この npm パッケージは Storymap と呼ばれます

3. 管理ページを作成する

新しく作成した npm パッケージが機能するかどうかをテストするには、npm create vite@latest を使用します。

パッケージのコンシューマとして機能する別のデモ プロジェクトを初期化します。

このプロジェクトにローカル パッケージをインストールするには、npm リンクを使用して、デモの node_modules に Storymap へのシンボリック リンクが作成されます。

このプロジェクトは、Web 開発者が新規または更新されたツアーのパスを作成できる管理パネルを作成およびテストするために使用されます。

Index.html ファイルに Canvas 要素を作成し、それをストーリーマップの構成に追加した後、ターミナルで npm run dev と入力し、ブラウザで localhost:3000 に移動すると、空の 3 つの要素が表示され、テストを開始できます。 .js シーン。

ストーリーマップ パッケージはシンボリックリンクされているため、ストーリーマップ パッケージに加えた変更は自動的にデモ プロジェクトに反映されるため、デモ プロジェクトを実行し続けて常にコードを作成し、変更をテストすることができ、効率的な開発プロセスが可能になります。

4.OSMデータ

当初は OSM Buildings を使用してデータを取得する予定でしたが、そのドキュメントが古いことに気づき、より迅速に開発を開始し、OSMB の使用方法を理解できるように Overpass Turbo に切り替えることにしました。OSMB は GeoJSON を使用し、Overpass Turbo ではデータを JSON 形式にエクスポートできます。

GeoJSON ファイルには、さまざまな座標やメタデータの配列で表される道路、公園、建物など、地図のさまざまな要素が含まれています。建物には、プロファイル、開口部、断面、高さまたはレベル、その他多くのプロパティがあります。
ここに画像の説明を挿入

建物のモデルとそのメタデータの例

Web 上で JSON が人気があるということは、JSON の解析がネイティブでサポートされていることを意味するため、JSON は良い選択です。OverPass API の使用方法を理解するのには時間がかかりました。ドキュメントではクエリ言語の構造が明確ではなく、使用するのが依然として難しい場合があります。最終的なクエリを以下に示します。

[out:json]
[bbox:{
   
   {bbox}}]
[timeout:30];

(
way["building"]({
   
   {bbox}});
relation["building"]["type"="multipolygon"]({
   
   {bbox}});

way["highway"]({
   
   {bbox}});
relation["highway"]["type"="polygon"]({
   
   {bbox}});

way["natural"="water"]({
   
   {bbox}});
way["water"="lake"]({
   
   {bbox}});
way["natural"="coastline"]({
   
   {bbox}});
way["waterway"="riverbank"]({
   
   {bbox}});

way["leisure"="park"]({
   
   {bbox}});
way["leisure"="garden"]({
   
   {bbox}});
);

out;
>;
out qt;

5. OSM アーキテクチャ レンダリング

建物の生成は都市モジュールに分かれています。GeoJson ファイルを解析して建物を検索します。建物は、建物の輪郭を形成するポリゴンの緯度と経度の座標の配列として表され、最終的には、中庭や張り出しなど、その形状の穴を表すポリゴンがさらに増えます。

最初の大きな課題は、JavaScript 自体と Three.js の座標系によって引き起こされます。シーンの中心は 0,0,0 で表され、そこから離れるほど精度が低くなり、主に JavaScript の浮動小数点精度が低いことが原因で不安定になります。

GeoJSON 座標はグローバルな緯度と経度の座標であり、差が小さい大きな浮動小数点数であるため、精度の問題がさらに悪化します。

したがって、グローバル座標をローカル空間に対して正規化する必要があります。geolib と呼ばれるサードパーティ ライブラリを使用して、領域の中心を占めます

中心から各点までの距離を計算し、その結果、座標が原点に対して正規化され、標準偏差が大きくなります。
ここに画像の説明を挿入

原点を基準とした座標を示すグリッド

この正規化されたデータを使用して、Three.js のシェイプとジオメトリを作成できます。Three.js の組み込み機能を使用すると、輪郭から穴を簡単に「パンチ」することができました。次に、このジオメトリをマテリアルとともに使用してメッシュを作成し、シーンに追加します。

three.jsXYZ の方向のため、X 軸と Z 軸上でそれぞれ 90 度および 180 度回転する必要もあります。この方法を使用すると、非常に平坦な最初のバージョンの都市が完成しました。3D にするために、これらの形状を押し出すための任意の値を掛けた階数を表すレベル プロパティを使用しました。

ここに画像の説明を挿入

ブラウザの中に都市が登場!

6. パフォーマンスとインタラクション

都市が生成されると、問題が発生します。パフォーマンスは最悪で、FPS は 1 桁に低下します。解決策は、建物ごとに個別のオブジェクトを生成するのではなく、すべての建物を 1 つの大きなメッシュに結合することです。このアプローチには、すべての建物のマテリアルが同じになるという副作用がありますが、パフォーマンスが大幅に向上するため、価値があります。この修正は、道路、緑地、水路など、その後に生成されるすべてのジオメトリに適用されます。

Web 開発者は、エリア全体をロードしてナビゲートし、ツアー パスのウェイポイントを設定できる必要があります。これは、three.js マップ コントロールを使用して実現できます。これは、Google マップやその他の一般的なデジタル マッピング ソフトウェアと同様に機能する 3js Orbit コントロールのサブセットで、右クリックしてページの中心を中心にカメラを回転し、左クリックしてカメラをドラッグできます。これらは初期化モジュールでインポートおよび初期化されます。これらが機能するには、フレームごとに更新する必要があるため、アニメーション ループに追加されます。

パスのウェイポイントを作成するには、道路を選択し、2D マウス座標を 3D 空間に変換する方法を選択する必要があります。後者の場合、レイキャスターを使用して、マウスの位置から 3D シーンに光線をキャストし、何が交差するかを確認できます。そこからフィルタリングして道路のみを選択できます。

前者については、同様の技術が建物にも使用されます。OSM Buildings の代わりに Overpass Turbo を使用することは、逆に幸運であることが判明しました。OSM の API は建物に対して GeoJSON のみを提供するため、道路データを取得するには追加の API を見つけて実装する必要がありました。

Overpass API クエリは道路を含むように調整され、その座標が正規化されて生成され、都市モジュールのシーンに追加されます。街が殺風景に見える中、OverPass は緑地や水路のデータを取得するためにも使用されています。
ここに画像の説明を挿入

描かれている道

パス モジュールは、Raycaster およびパスの描画とエクスポートに必要なその他のコントロールを実装するために使用されます。Three.js には Raycaster が組み込まれているため、実装は簡単です。これは、ユーザーがパスを選択したかどうかを示すレイを継続的にキャストする OnMouseMove イベント リスナーにリンクされています。

マウスの左クリックはウェイポイントの設定に使用され、さまざまなキーボード ボタンで保存、リセット、元に戻す、やり直しの機能が使用されます。

パスは JSON 配列にエクスポートされます。開発者がパスの作成を開始したら、パスをマウスに追従させる必要があります。したがって、クリックする前に、同じく OnMouseMove イベント リスナーを使用して、パスの最後の点がマウスの位置にリンクされます。

7. クライアント

クライアント実装をテストするために、新しいプロジェクトを作成し、管理プロジェクトと同様の方法で構成しました。vite を使用してプロジェクトをブートストラップし、npm リンクを使用して Storymap パッケージをインストールしました。唯一の変更点は、今回はページ全体を占めるのではなく、シーンが半分を占め、残りの半分が興味のあるポイントに関する情報の表示に使用されることです。

クライアントにとって、インタラクションを制御できるのはブラウザーでのスクロールだけです。ユーザーが Lucas デモでキャンバスを操作できなかったのと同じように、Three.js シーンと直接操作できるべきではありません。

つまり、クライアントがブラウザを下にスクロールすると、パスが表示され、開始位置からアニメーション化され、ユーザーが一番下までスクロールしてカメラが追従したときに終了します。

これはプロジェクトで最も困難な部分の 1 つでした。

ここに画像の説明を挿入

パスの計算方法を示す図

要約すると、パスは複数の点を持つ Line オブジェクトとして描画されます。ユーザーが下にスクロールすると、最後のドットが更新され続け、アニメーションしているように見えます。

ポイントの座標を取得するために、 getScrollPercentage() 関数がスクロール イベント リスナーにアタッチされ、ブラウザーのスクロール位置の計算に使用されます。次に、そのパーセンテージを使用して、どの点のペアを開始点と終了点として選択するかを継続的に操作し、lerpVectors 関数を使用して 2 つの点の間を補間し、現在のパスの最後の点を計算します。

例を挙げてこれを明確にしましょう。11 個の個別のポイントがあるパスを選択しましょう。最初のページは最初から描画されるため、ページの合計の高さ (パーセント単位) を 10 で割る必要があります。

  • これは、10% スクロールするごとにポイントが渡されることを意味します。グローバル スクロール パーセンテージによって、どのポイントのペア間を補間する必要があるかが決まります。
  • ユーザーが 26.53% までスクロールした場合、補間を使用してポイント 3 とポイント 4 の間の 65.3% にあるポイントの座標を計算する必要があります。
  • この座標は、パスの最後の位置を更新するために使用されます。これはユーザーがスクロールするたびに発生し、パスが縮小または拡大しているかのような錯覚を与えます。
  • カメラの位置はパスの片側の後方にオフセットされ、最後のパスの位置がターゲットとして使用され、パスとともに移動します。

ここに画像の説明を挿入

プロジェクト構造

研究の最終結果は、マップ データの生成と解析を担当する Storymap npm パッケージ、マップ上にパスを作成できる管理プロジェクト、およびクライアントの 3 つの異なるプロジェクトで構成されました。

このプロジェクトは、街中に作成されたパスをアニメーション化します。
ここに画像の説明を挿入

ここをクリックしてオンライン デモを表示できます。ソース コードはGitHubから入手できます。


元のリンク: OSM+three.js で 3D 都市を作成する — BimAnt

おすすめ

転載: blog.csdn.net/shebao3333/article/details/132384145