Entagmaのチュートリアルで紹介されていたOSM Importer+SRTMをご紹介いたします。
HOUDINI GAME TOOLS: ASSEMBLING A 3D SCAN OF THE EARTH
Houdini Game Tools: Assembling a 3D Scan Of The Earth from Entagma on Vimeo.
サンプルデータなどは元のEntagmaさんのサイトで公開されているので、そちらをチェックすることをお勧めします。
http://www.entagma.com/houdini-game-tools-assembling-a-3d-scan-of-the-earth/
手順としては、SRTM(Shuttle Radar Topography Mission)を元にしたHeightMapをグリッドに割り当てUVを生成、OSMデータはグリッドのUVを元に高さ情報をグリッドから取得します。
必要データのダウンロード
NASAが無料で公開したSRTM(Shuttle Radar Topography Mission)で集めたデータ(最新のデータではない)を元にDerek Watkins氏が作成したツールを使用することができます。
このツールでは、Shuttle Radar Topography Missionによる30メートルタイルの高度データをダウンロードを手軽に行うことができます。
黄色のタイルをクリックすると、対応するデータがダウンロードされます。
タイルは、NASAサーバーからダウンロードされた緯度/経度投影(EPSG:4326)の1秒(=1/36,00°)解像度(3601x3601ピクセル)で圧縮されたSRTMHGTファイルとなります。
ここでダウンロードしたい地域のタイルをクリックします。
そうするとこのタイルの緯度と経度が表示されます。
例えば横のタイルをクリックしてい見ると・・・
北緯19度、西経156になります。つまり、1タイルで1度ずれていることがわかります。
必要なタイルの緯度経度を確認し、Download DEMをクリックしてファイルをダウンロードします。
※ダウンロードするためには以下でアカウントを作成する必要があります。
https://urs.earthdata.nasa.gov//users/new
ZIPファイルをダウンロードできますので、解凍しておきます。拡張子は.hgtとなります。
次にOpenStreetMapにアクセスします。
https://www.openstreetmap.org/export
「ドラッグして別の領域を選択」をクリックします。これでエクスポートする領域を指定することができます。
先ほどこの領域のデータをダウンロードしましたので
ここに入手したデータを入力します。1タイル分なので、北緯19度から20度、北緯(つまりー)156から155を入力すると同じタイルとなります。
ダウンロードをするためにoverpass APIをクリックします。
ダウンロードファイルはmapという名前で拡張子なしで保存されますので、ダウンロード後に.osmを追加してください。(名前は任意で構いません)
先にダウンロードしておいたSRTMファイルである.hgtの拡張子を.rawに変更します。
.rawに変換したファイルの中をPhotoshopでみてみましょう。
開く際にオプションが表示されます。この時解像度は1タイルである3601 * 3601となります。
チャンネル数は1に設定し、色数は16bitとしてください。
レイヤーをコピーし、「露光」を追加してください。
モードを16bitから32bitに変換します。
モード変換時にレイヤーを統合するか聞いてくるので、統合しないを選択します。
これで露光量を変えてみるとHeightMapとしての高さ情報を確認することができました。
露光データを削除し、開いて修正を全くしていない「背景」レイヤーのみ残します。
この状態で拡張子に「.tif」を選択し、ICCプロファイルをオフにします。
ビット数が32bitになっていることを確認し、保存します。
これで下準備ができました!
Houdiniでデータ作成
Houdiniを開き、Game Dev Toolがインストールされていることを確認します。
インストールされていない場合、Gameシェルフを開き、Update Toolsetをクリックして、最新のGame Dev Toolをインストールしてください。
Geoノードを作成し、中のFileノードを削除、その後、osm Importノードを作成します。
OSM Fileにダウンロードしておいた.osmファイルをロードします。
ビューポートで確認すると以下のようにデータが作成されていることがわかります。
Geometry Spread Sheetsを開き、Primitivesを選択、Viewをクリックすると非常に多くのアトリビュートが存在することがわかります。
それではSRTMデータをロードします。
Gridを作成し、分割をそれぞれ3601にします。(SRTMデータのピクセル数)
次にAttribute from Mapを作成します。Attrib From Mapノードは、画像のカラー情報からPointアトリビュートを作成します。
Export AttributeをCdからaltに変更し、データタイプはFloat、Texture Mapは先ほどPhotoshopで保存した.tifファイルをロードします。
altポイントアトリビュートが追加されますが、これはテクスチャ(Heightmapとしてインポートしている)を評価し、その情報を高さとして保存しておくことにになります。
Filter SettingsでFilterをPointに設定します。
そして、Point Wrangleノードを追加します。ここで、先ほど取得した高さの値をポイント位置のY座標に割り当てていきます。
Point Wrangleでは、altアトリビュートをロードし、オフセットパラメーターとして保持します。
altパラメーターはSRTMからロードされているので、-1から+1までの範囲になっています。そのため、16bitの最大スケールにするため、32767.0を乗算します。
そして、スケールファクターを追加します。実際にハイトマップから読み込んだ値にスケールファクターをかけ、スケールを調整できるようにすることが目的です。
"amp"というフロートスライダーを乗算します。
ampスライダーの値は1にします。
そして、offsetをポイントのYの値に設定します。
クックすると、下記のようになります。
値が大きすぎるため、ビューでみると形状が壊れているようにみえます。
そこで、ampを0.001にし、高さを1/1000にします。
Photoshopで露光量を上げた際に、白点や黒点のノイズが出ていたことに気が付いた方もいらっしゃるかもしれませんが、その部分がデータかけた部分になります。データが欠けている部分が上記の図にて壊れて柱のようになっているため、この部分を修正します。
これで壊れた部分を修復することができました。
ただし、ここでは値を0にセットしただけになってしまうため、エラーポイントの周囲となじませた方がより良い結果となります。先ほどのコードでエラーポイントをerrorというグループにまとめています。
そこで、コードにv@Cd = {1,0,0};を追加してエラーポイントを赤で塗りつぶします。
ビューポートを確認すると、赤く色づけされたポイントを確認することができます。
そこで、Attribute Blurを追加し
errorグループに入ったポイントのaltアトリビュートの値をぼかすようにします。
errorグループはこのブラーをかけたaltのアトリビュート値を使用するため、pointwrangle1を複製します。
複製したものをAttribute Blurの後に追加します。
Point Wrangle2を適用するのは、errorグループと指定します。
さらに既にerrorグループの処理はpoint wrangle1で行っているので、その部分のコードを削除します。
これでpoint wrangle2をクックします。
またデータが壊れているのは、Point Wrangle1で計算されたaltアトリビュートの値がいきているからです。
point wrangle1を選択し、altアトリビュートを0にします。(この後のAttribute Blurで周囲の値とブラーしますので心配いりません)
これで問題を修正することができました。
最後にOSMのデータと統合していきます。
速度を上げるためにGrid1で設定した解像度を360*360に設定します。
OSM Importノードを選択し、Geometry Spread Sheetsを表示し、Detailを有効にします。
ここでmax_lat、Max_lot、min_lat、min_lonを確認することができます。これはOSMをダウンロードした範囲を示しています。
Point Wrangleを追加し、Point Wrangle2を入力0、OSM_importを入力1に接続します。
入力1(osm_import)のアトリビュートをパラメーターとして保存します。
念のため、値を整数にまるめます。
平面の0-1とマップの緯度経度を対応付けする必要がありますが、平面内の相対座標があればこれを計算できます。その相対座標を計算するためにUVを利用します。
pointwrangle2とpointwrangle3の間にuvtextureノードを追加します。
Y軸から割り当てて、Attribute ClassはPointにします。
Geometry Spread SheetsでUVが追加されたことを確認してください。
UVは0.0からはじまり、1.0で終わります。
このUVの値を使用して、緯度経度をリマップしていきます。
fit01関数を使用し、0-1の値(UV)を新しい範囲(緯度経度の範囲)に移動します。
ベクトル型のパラメーターに設定し、それをアトリビュートとして割り当てます。
ここまででGeometry Spread Sheetsを確認すると
latlong[0]は19、latlon[1]は-156から始まり
latlong[0]は20、latlon[1]は-155までとなっていることがわかります。
最後に計算した標高をOSM Importに戻します。
Point Wrangleノードを追加し、入力0にOSM_Importを、入力1にPointwrangle3を接続します。
osm_importノードのInformationを確認すると、latitudeとlongitudeが個別のフロートとして持っていることがわかります。
上記の値をベクトル型パラメーターに保存します。
最後にuvsample関数を使用して、指定したUV座標におけるアトリビュートの値を補間します。
uvsample(<geometry>geometry, string attr_name, string uv_attr_name, vector uvw)
入力1ジオメトリ、サンプリングするアトリビュート”P”(ポジション)、UVを含んだPoint/Vertexアトリビュート”latlon”、アトリビュートをサンプリングするUV(W)空間の位置としてosm_latlonを指定します。
Pointwrangle3のlatlonアトリビュート(UV)と、OSM_Importノードのosm_latlonを使用して相対位置を割り当て、Heightmapからグリッドに割り当てたポイントの位置をOSMデータに補間しました。
ビューポートを確認すると、OSMで作成したデータに高さが追加されていることがわかります。