3Dモデルをいろいろ作ってみたい

3Dモデルをいろいろ作ろうとがんばっています。苦労した点、役に立ちそうな情報を発信していきます。

立体地図を作る -標高メッシュから直接OBJ形式データを生成する-

なんとか立体地図が作成できるようにはなったものの、いくつかのの県についてパズルのピースを作ってみると手順にいろいろ不具合があることもわかってきました。今回は今まで汎用のソフトウェアを使って行っていたOBJ形式データの生成処理を専用のプログラムで置き換えることで作業の効率化とモデル品質の向上を図ります。

これまでQGISで作成した標高メッシュの情報から3Dモデルのデータ(OBJファイル)を生成するためにOpenSCADというプログラムを利用していましたが、これには2つ大きな問題がありました。

問題1 処理できるデータ量に制限がある

立体地図作成のためにはメッシュごとに単純な直方体の3Dデータを生成していけばよいのですがOpenSCADでは生成する直方体の数(あるいはプログラムの行数)が19,000を超えたあたりでメモリー不足で異常終了してしまいます。PCのメモリ使用状況としては余裕があるので、OpenSCADの処理系内部のどこかで固定でワーク領域を確保しているのではないかと疑っています。異常終了を避けるためにはメッシュデータを分割して3Dデータを生成してから、ZBrushなどの3Dモデリングソフトを使って統合する必要があります。手間がかかることに加えて、(問題2とも関係しているのですが)なかなかきれいに統合できないでいました。

問題2  STL形式のデータしか出力できない

OpenSCADが出力できるのはSTLという3Dプリント出力用のデータのみです。STL形式では三角形の集合で面を表現しています。ZBrushSTL形式を読み込めないので、いったんMeshLabで読み込んでOBJ形式(三角形を含む多角形の集合で面を表現している)に変換してからZBrushに読み込んで前出の統合処理やスムージングなどを行なっていました。3Dプリントのためには最終的にはSTL形式を出力するので、STLをOBJに変換、OBJ形式を読み込んで編集、再びSTL形式で出力という段階をふむ必要があります。くわえて、もともとの地図データは直方体なので四角形の集合で面をきれいに表現できていたのが一度三角形の集合になったものを読み込むと元の四角形の集まりとは微妙に異なってしまいます。問題1の対応のために静岡県のモデルを分割生成してとりこんだときは接合面の頂点ががぴったり合わず形状にスジが残ってしまいました。

 こういった問題を解決するため標高メッシュのデータからOBJ形式を生成するプログラムをつくることにしました。OpenSCADは3Dモデル作成のためにさまざまな基本形状や形状に対する操作をサポートしていますが、立体地図作成に必要なのは単純な直方体形状をグリッド上に配置していく操作のみです。この部分だけなら比較的簡単に実装することができます。

 出力するOBJファイルのフォーマットについてもインターネット上で情報を見つけることができます。改行で区切られたテキスト形式のファイルで"v"で始まる行は頂点、"f"で始まる行が1つの面(多角形)を表すというのが基本です。 頂点を表す行は"v"の後にXYZの座標を示す3つの数字が空白で区切られて続きます。面は多角形の頂点を指すインデックス番号を空白で区切って記述します。下の例で”f 1 4 3 2”の1は1番目の頂点(48.750,100.250,0.000)を示しています。直方体の面はいずれも四角形ですから"f"の後には整数値を4つ記述することになります。面には表と裏があって正式には"vn"で始まる行で法線ベクトルを記述するようですが、1->4->3->2といった頂点を記述する順番(表面からみて反時計回りになるような順番)で裏表を指定できるという仕様(慣習?)がありZBrushも”vn”無しのデータを読み込んでくれました。

 

OBJ形式ファイルの例

v 48.750 100.250 0.000
v 49.000 100.250 0.000
v 49.000 100.500 0.000
v 48.750 100.500 0.000
v 48.750 100.250 5.000
v 49.000 100.250 5.000
v 49.000 100.500 5.000
v 48.750 100.500 5.000

f 1 4 3 2
f 8 5 6 7
f 1 5 8 4
f 3 4 8 7

下のような1個の直方体の情報を表すのには8個の頂点と6個の面の情報を出力すればよいわけです。

f:id:ichidaya:20190204230227p:plain

単純な直方体

実際には隣合う直方体の間では頂点を共有したり、面が別の面で覆われたりすることを考慮しないといけません。下の図で青丸で示すような2つ以上の面で共有されている頂点は各面で同一のインデックス番号で指定されないといけません。同じ座標を持った頂点を複数個出力するのはNGです。面についても互いに接している部分は除いて重なっていない部分(下図の青枠部分)だけの面の情報を出力します。

f:id:ichidaya:20190204234943p:plain

隣接する直方体

上記のような点を考慮してOBJファイルを生成するスクリプトPythonで記述しました(末尾に添付)。

入力は基本、OpenSCADに与えていたのと同じ情報ですが数値の部分だけをカンマで区切ったCSV形式で用意します。

 48.75,100.25,0,0.25,5
49,100.25,0,0.25,5
49.25,100.25,0,0.25,5
49.5,100.25,0,0.25,5

各行、5つの数字で直方体を表しています。最初の3つは左下角の頂点を示すX,Y,Z座標値、4つ目は底面の正方形の1辺の長さ、最後の数字は直方体の高さになります(単位はmmです)。スクリプトにはこのCSVファイルと出力するOBJファイルのパスを引数として指定します。

Python GenObj.py I:\local\3D-Work\Shizuoka.csv I:\local\3D-Work\Shizuoka.obj

厳密なOBJ形式の仕様に則しているかは若干怪しいですがZBrushは出力されたファイルを問題なく読み込んでくれています。

f:id:ichidaya:20190205121136p:plain

静岡県のOBJファイルを読み込んだ画面

この静岡県は30,000弱 の直方体で構成されておりOpenSCADではいちどに3Dモデルを生成できなかったのですが作成したスクリプトでは問題なく処理できました。また、処理時間も数秒でOpenSCADでの処理に比べて大幅に向上しました。

以下にスクリプトのソースを開示します。自分で使っている範囲内でしかテストしていないので、利用する場合は自己責任でお願いします。

GenObj.py