はじめに
さまざまな独創的なアルゴリズムを適用することによるランドスケープ生成のテーマは、ハブで広くカバーされていました(1、2、3 、そして無限に続くことができます)。 これらの例は、最終的なゲーム製品のリアリズムを高めるための抽象的な地形のランダム生成に関するものです。 しかし、実際の地形をシミュレートする場合はどうでしょうか?
このテーマについても、ネットワーク上には非常に多くの種類の出版物があります。 ただし、それらの多くは、Unityの有料アプリケーションまたは拡張機能の使用に依存しています。 「安い」方法の説明がありますが、それらの大部分はいわゆるハイトマップの取得に焦点を当てています-グレーのグラデーションが特定のポイントでの高さの相対的なレベルを決定するエリアの白黒の正方形の画像。 たとえば、 GDALツールキットを使用して、このような高さマップを生成する方法はいくつかあります。 そして、このアプローチには、高さマップを作成し、結果の領域にバインドするためのかなり面倒な手順に関連する欠点がないわけではありません。 したがって、この記事ではいくつかの代替アプローチが述べられ、興味のある人は猫の下で招待されます。
1.ジオデータマイニング
NASAは、地球の表面をレーダーでカートン化するためのSRTM(Shuttle Radar Topography Mission)プログラムを完了したことに感謝する必要があります。 ミッション中に取得したデータは、 ここで公開されています 。 このリンクをたどり、ゾーンに分割されたネイティブの「ボール」を観察します。
説明した手法を実証するために、「世界の屋根」エベレスト山の領域を選択します。 これを行うには、目的のマップをクリックして、「ダウンロード」ボタンをクリックします。
ダウンロードしたアーカイブを展開すると、特にGeoTIFF形式のsrtm_54_07.tifファイルが見つかります。 GeoTIFFは、地図作成情報を保存するためのオープンスタンダードです。 このファイルを分析することにより、地球の表面上のポイントの高さに関するデータを取得できます。 形式はオープンなので、Unityなどから直接作業を整理することは難しくありませんが、ここでは簡単にするために、短いパスを使用し、小さくても便利な3DEMプログラムを使用します。 このsoftinkaは無料で、公式サイトから簡単にダウンロードできます。 それをダウンロードして、私たちが取得したエベレスト地域のGeoTIFFを開きます。 開くと、ジオデータ形式を選択し、GeoTIFFを選択するよう求められます。
次に、上記のファイルを選択して、次の図を参照してください。
表示される領域は非常に広いです。 中央の長方形は、エベレストの関心のあるエリアを示しています。 目的のピースをカットするには、F8を押し、関心のある領域を選択してEnterを押します。 緯度と経度でサイトの境界を明確にするよう求められます。
必要に応じて指定し、[OK]をクリックして、画面上で関心のある領域を取得します。
必要に応じて、この手順を繰り返します。 最後に、目的の領域を選択し、 [ファイル]→[地形マトリックスの保存 ]のパスに沿ってメインメニューに移動します 。 保存するデータの形式を選択するよう求められます。
面倒が少ないので、バイナリ形式のフロートを選択します。 次に、保存したファイルのパスと名前を選択します。 排気として、2つのファイルを取得します
- everest.hdr-保存された領域とデータストレージ形式の説明を含むテキストファイル
- everest.bin-単精度浮動小数点形式の高さの2次元配列であるデータ自体
3.ジオデータストレージ形式の説明
3DEMに付属のPDFドキュメントから入手するのは難しくありません。 主な情報は* .hdrファイルにあり、次のようになります。
file_title = everest data_format = float32 map_projection = Lat/Lon left_map_x = 86.859001 lower_map_y = 27.899000 right_map_x = 87.014999 upper_map_y = 28.046000 number_of_rows = 177 number_of_columns = 188 elev_m_unit = meters elev_m_minimum = 4574 elev_m_maximum = 8794 elev_m_missing_flag = -9999
タスクの最も重要なフィールドについて説明します
- file_title-* .binデータファイルの名前
- data_format-データ表示形式
- left_map_x、lower_map_y-プロットの左下隅の経度と緯度(この条件付きの「長方形」の上部が北に見えると思われます)、度
- right_map_x、upper_map_y-プロットの右上隅の経度と緯度(度単位)
- number_of_rows-データ行列の行数。 各線は、等間隔の範囲の角度の座標で指定された座標からの固定緯度での高さを示します。
- number_of_columns-データ行列の列数
- elev_m_unit-高さの単位
- elev_m_minimum-このセクションの参照楕円体レベル(簡単にするために-海面)からの最小の高さ
- elev_m_maximum-参照楕円体上の最大高さ
したがって、バイナリファイルの読み取り方法が明らかになります。これは、ファイルに1行ずつ書き込まれるフロートのストリームです。 このマトリックスは、私たちの場合、選択されたエリアの177 x 188 = 33276ポイントの高さを格納します。これに対して、経度と緯度の制限がわかっています。 このエリアの高さの範囲もわかっています。
3.標高データをUnityに保存する
Unity Terrainクラスにはpublic terrainDataフィールドがあり、これを介して次のフィールドにアクセスできます
- heightmapResolution-高さマップの解像度。 これは2の累乗の倍数で、128、256、512の行から選択され、1が追加されます。 デフォルトでは、513 x 513です
- size-Unityの任意の単位でのテレーンの実際のサイズ。 このフィールドはVector3型で、一度にすべての軸に沿って地形のサイズを設定します。
- SetHeights(int xBase、int yBase、float [、] heights)-高さマップデータをロードするためのメソッド。 xBase、yBase-エンジンが高さ配列から高さマップを読み込むインデックス。
高さマップは、非常に特定のサイズの正方配列です。 ジオデータマトリックスは長方形の配列です。 ファイルからそれを引くことは技術的な問題であり、そのようなコードは非常に単純なので、具体的に持ってくることは意味がありません。 主な問題は、GeoTIFFデータを次の要件を満たす高さマップに変換することです
- 正方行列、2の累乗+ 1
- マトリックスに格納された高さは、0.0〜1.0の範囲で正規化されます
4.ジオデータを高さマップに変換します
高さの正規化は非常に簡単です。
ここで、レベル[i、j]は正規化された高さです。 h [i、j]はポイント(i、j)の高さです。 h_min、h_max-サイトの最小および最大の高さ。 マトリックスサイズを設定要件に合わせるには、座標に応じて高さの近似値が必要です。 最も単純な線形近似を使用します。 高さをセグメント上の点の座標の関数とする 。 次に、基点を選択します 特定の座標グリッドのノードの1つと一致し、この関数をテイラー級数で展開します
明らかな複雑さで、式はC#コードによって簡単に実装されます
float getHeight(float x, float z) { float height = 0.0f; // float dx = terrain_data.x_range / (terrain_data.numder_of_rows - 1); float dz = terrain_data.z_range / (terrain_data.number_of_columns - 1); // int i = (int) (x / dx); int j = (int) (z / dz); // if ((i >= terrain_data.numder_of_rows) || (j >= terrain_data.number_of_columns)) return 0.0f; // (i, j) float dydx = (terrain_data.normalize_data[i + 1, j] - terrain_data.normalize_data[i, j]) / dx; float dydz = (terrain_data.normalize_data[i, j + 1] - terrain_data.normalize_data[i, j]) / dz; // height = terrain_data.normalize_data[i, j] + dydx * (x - i * dx) + dydz * (z - j * dz); return height; }
この関数を使用して、高さマップのマトリックスを簡単に作成します
terrain_data.height_map = new float[resolution, resolution]; float dx = terrain_data.x_range / resolution - 1; float dz = terrain_data.z_range / resolution - 1; for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { terrain_data.height_map[i, j] = getHeight(i * dx, j * dz); } }
5. Unity Editorのプラグイン
作業を楽にするために、 UnityGeoDataLoaderエンジンのエディター用の小さなプラグインを作成しました。これにより、問題を解決できます。 この製品とそのソースコードは、GNU GPL v2.0の下でライセンスされています。 指定されたリンクで両方を取得できます。 このツールを使用するための指示もあります。
プラグインはエディターに組み込まれ、「ツール->ジオデータのロード」という項目をメインメニューに追加します。 インスペクターでテレーンを選択し、このメニュー項目をクリックして、* .hdrファイル(データのある* .binファイルの隣)へのパスを指定し、出来上がり-Unityエディターでエベレスト山を参照
次に、この地形を使用して、私たちは心の望みを何でもします-テクスチャリング、シェーダーの適用、他のオブジェクトの配置。 これはあなたの裁量です。
おわりに
私は自分自身を批判します。 GeoTIFF標準が公開されている場合は、良くありません。サードパーティのツールを使用して地理データをマイニングします。 良い方法では、プラグインにすべてのコードを配置し、座標によって領域を選択する機能を追加する必要があります。 しかし、完璧に制限はありません。主なことは、実際のランドスケープをエンジンにロードするタスクには、完全に受け入れられるシンプルなソリューションがあることです。
ご清聴ありがとうございました!