Procedural Hard Surface Design With Copernicus
この記事はHoudini Advent Calendar 2024 22日目の記事です
はじめに
Procedural DesignのAkira Saitoです。
Houdini等を使用しロボットなどのメカデザインを自動で行う研究をしています。
今回は、Houdini 20.5でBeta実装されたCopernicusを使用して、メカディティールの生成を行ないました。
UV展開されたモデルをCopernicusに読み込み、そのモデルの立体情報を画像に変換(ラスタライズ)し、その情報を元にメカニカルなテクスチャを生成する過程を説明します。
動作環境
Houdini Indy Version 20.5.410(apple silicon)
macOS Sonoma バージョン14.6.1
MacBook Pro (16-inch, 2021)
プロセッサ Apple M1 Max
メモリ 64 GB
サンプルデータ
以下のファイルを参照しながら読み進めてみてください。
ラスタライズ後のパディングについて
本題に入る前に、UV展開されたオブジェクトに対して、そのUVを元にしたテクスチャ生成の準備工程であるラスタライズ後の処理について、注意すべき場所と私なりの改善点を紹介します。
Copernicusのチュートリアル等で紹介される、UVを元にしたラスタライズは以下の一連の流れで行われます。
SOP Importでのジオメトリの読み込み
Rasterize SetupでのUV形状に変換し元の位置情報は頂点attributerに格納
Rasterize Geometryで頂点アトリビュートを画像に
Extrapolate BoundariesでUV境界の拡張
問題と感じるのは、Extrapolate BoundariesでUV境界の拡張する部分。パディングとも呼ばれる処理で、UVアイランドの境界を拡張する事でミップマップ等で境界外の色がUVアイランド内部に染み出す不具合を解消するものです。
Test Geometry:Pig Headを例に元の位置情報をラスタライズした例で解説します。
しかし、Extrapolate Boundariesのパラメータ調整だけでは制度に問題がある部分が出てきたので、私はVEXでラスタライズの時点でパディングの処理も行う事にしました。
VEXによるラスタライズとパディング
ラスタライズ時にパディングを行うメリットとしては、。画像とマスク情報のみでパディングを行うExtrapolate Boundariesと比べ、ジオメトリ情報にアクセスが可能と言う事です。
コードは極めてシンプルです。
int prim;
vector uv;
//@Pが示す座標から最も近い位置のprimとuvを求める。
float dist = xyzdist(1,@P,prim,uv);
//primとuvから、元の形状の位置を求める
v@origP = primuv(1,'origP',prim,uv);
//0距離の場所をマスク情報として出力
if(dist==0)
{
f@mask = 1;
}else{
f@mask = 0;
}
パディングの距離を設定したいのであれば、distの値を参照する事で可能ですが、殆どの場合UVアイランドの隙間を全てパディングで埋めてしまっても問題がないので、今回は距離を加味していません。
パラメーターの調整の必要もなく安定したパディングが出来ました。
ラスタライズとパディングが無事できたので、メカのデザインの話を進めましょう。
やっぱりWorley Noiseは外せない
Worley Noiseを使うことで、メカニカルな印象のノイズを生成することが可能です。
Worley Noiseの詳しい解説は過去のアドベントカレンダーに投稿してあります。未読の方はそちらを参考にしてみてください。
Procedural Hard Surface ModelingのためのWorley noiseの実装
CopernicusにもWorley NoiseとWorley Noise 3Dが最初から用意されていますが、標準のWorleyノイズは、GLSLなどのシェーダ言語でよく使われるアルゴリズムを採用しているため、デザインの肝となる母点の詳細な制御が出来ません。
GLSLでのセルラーノイズに関しては
The Book of Shadersの解説が分かりやすいのでお勧めです。
そこで、過去にSOPでもWorley Noiseを自作したように、今回もCopernicusでWorley Noiseを自作していきます。
点群はどうするのか
この件に着手した頃は、Copernicusの学習が大きな目的でしたので、点群の生成もCopernicusだけでできないか検討しました。しかし、CopernicusのWrangleにはDetailランオーバーがなく、Pythonもないため、点群を生成し保持する処理が思いつきませんでした。早々にCopernicusだけで点群を生成するのは諦めましたが、点群そのものをCopernicusの外部から取り込むと利便性が失われるため、それだけは避けたかったので、Copernicus内の1ノードだけで点群の生成を行えるようにSOPを内包した仕組みを実装しました。
Copernicusのノードに入力されたgeometryを元に、点群を生成し、geometryとして出力する。
この構造は、標準のCopernicus HDAでも使われている構造で、Rasterize Setupも同様の構造です。Copernicus内でジオメトリに対して何かを行いたい場合はこの構造にすると良いでしょう。
ネットワーク全体
ネットワークの中央にWorley Noiseを生成するWrangleがあり、その右側に前処理、その左側に後処理が配置されています。
前処理としては、これまでに説明したラスタライズとパディングの部分と点群の生成があります。さらに、パディングの後に左右対称のデザインを生成するためにラスタライズされた位置情報を左右反転するWrangleが配置されています。
Worley Noise本体
Worley Noiseの実装wrangleで行います。
入力は3つ
ラスタライズされた位置情報(RGB)
メッシュ(ジオメトリの大きさを参照するため)(geometry)
点群(geometry)
出力は2つ
距離情報(float)
母点情報(ID)
パラメーター
ノイズの種類(int)
0:ユークリッド距離 F1
1:ユークリッド距離 F2-F1
2:マンハッタン距離
3:チェビシェフ距離
int mode = chi('mode');
//母点の数
int seedNum = npoints(2);
//入力メッシュのサイズを求める
vector objSize = getbbox_size(1);
//マンハッタン距離での最長を仮設定
float maxDist = objSize.x + objSize.y + objSize.z;
float f1 = maxDist;
float f2 = maxDist;
//シード値
int id;
//ユークリッド距離 f1 f2
if(mode == 0|| mode == 1){
int pt[] = nearpoints(2,v@pos,maxDist);
vector p1 = point(2,'P',pt[0]);
f1 = distance2(v@pos,p1);
vector p2 = point(2,'P',pt[1]);
f2 = distance2(v@pos,p2);
id = point(2,'id',pt[0]);
}else if(mode == 2|| mode == 3){
//全ての点群を巡回
for(int i=0;i<seedNum;i++)
{
int tmpId = point(2,'id',i);
vector p1 = point(2,'P',i);
vector v0 = p1 - v@pos;
float tmpF1;
if(mode ==2){
//マンハッタン距離
tmpF1 = abs(v0.x) + abs(v0.y) + abs(v0.z);
}else{
//チェビシェフ距離
tmpF1 = max(abs(v0.x),abs(v0.y),abs(v0.z));
}
if(tmpF1 < f1){
f1 = tmpF1;
id = tmpId;
}
}
}
i@id = id;
if(mode == 0){
f@dist = f1;
}else if(mode == 1){
f@dist = f2-f1;
}else{
f@dist = f1;
}
VEXコードです。アルゴリズムや数式の解説は
Procedural Hard Surface ModelingのためのWorley noiseの実装 を参考にしてください。
後処理
Worley Noiseによって生成された距離情報は、各母点からの距離が設定されているので、そのままでは使いづらいことが多いです。最小距離を0、最大距離を1に変換する正規化を行います。
Statisticsで求められた最小値、最大値を元に距離を正規化
サブネットにまとめる
利便性を上げるためにサブネット化します。
入力は1つ
UVが設定されたメッシュ(geometry)
出力は2つ
距離情報(float)
母点情報(ID)
パラメーターは
mode(worley noiseのパターンを設定)
worley ノイズを生成するwrangleから参照します
0:Euclidean(F1)
1:Euclidean(F2-F1)
2:Manhattan
3:Chebyshev
点群を配置するScatter SOPから以下をパブリッシュ
Force Total Count
Global Seed
Relax Iterations
メカディティールの生成
ネットワーク全体
距離情報を高さ情報に変換
ノーマルマップの生成
ベースカラーの生成
モデルにテクスチャを設定し確認
Worley Noiseの種類
ユークリッド距離(F1)
ユークリッド距離(F2-F1)
マンハッタン距離
チェビシェフ距離
作例紹介
元々メカニカルな印象のTest Geometry:Electraを生成テクスチャだけでディティールアップ
注意:Test Geometry:Electraの初期のUVは裏返っていたり密度が不均一なので、UV Unwrap等でUVを作り直しましょう。
まとめ
現時点でのCopernicusは、
まだBetaなので、今後仕様が変わる可能性があるので注意。
Pythonノードが無い。
Wrangleのランオーバーはピクセル単位のみ。
パディングには注意が必要。
PBRに必要な複数のテクスチャをまとめて操作するのが難しい。
SOP Importとcopnet SOPを使う事で、
Copernicus内のGeometryを編集する事ができる。
Mac Apple Silliconは、Vulkanが未対応なのでView Portがしょぼい…
気になる点を挙げてみましたが、2Dと3Dを行き来できるワークフローが非常に優れており、様々な表現が可能になりました。この部分が肝であり、どこまでを3Dで、どこから2Dで行うかを判断するセンスが求められる部分でもあると考えられます。今回は触れませんでしたが、OpenCLを使うことで、高速化や複雑な処理も可能になると思うので、今後色々と試していきたいと思います。
それでは、皆様、良いクリスマスを。