USD的には階層が変わらないようにすべきですが、それでもどうしてもInstanceに対してRBDをした場合等、階層の維持が困難な場合に。 もちらんリレーションシップを戻すだけなので同一Stage上にマテリアルは必要です。
hip
matwerialReAssign_v002.hiplc - Google ドライブ
Houdini 20.0.506
正攻法
USDは同一階層であれば自動で上書き処理になるので、SOPに一度行ってしまったとしてもアサインがおかしくなることはないです。
SOPから持ってきたキャッシュが2重Transfromかかる場合
これはやりがちなんですが、LOP上でTransformがかかっているものはSOPに持っていった際にTransformがベイクされます。
つまりこのまま何もしないで戻すと再度Transformがかかって2重に移動した位置に行ってしまいます。
LOPの移動情報は、usdxformアトリビュートとして残っているので、LOPに戻す前にTransform by Attribute等で戻してあげることで相殺します。
階層を変えてしまったものにマテリアルを戻す
usdmaterialpath
ですが、本来LOP Importした際にusdmaterialpathアトリビュートとしてアサインされているマテリアルへのパス情報を保持しているので、これを元に再アサインします。
SOP ImportやUSD Export時に以下のパラメーターにusdmaterialpathを指定します。Set Default Valuesも指定しているのはアトリビュートへの不要なTime Sampleを防ぐためです。USD Custom Attributeは、Primvarで読むかAttributeとして読むのかを設定できます。
Re Assign
Attribute Wrangleで以下のようにします。
Primitivesは{ usd_isattrib(0, @primpath, "usdmaterialpath") == 1 }
にすることでusdmateriapathアトリビュートを持っているすべてのPrimitiveを指定しています。
VEXはこんな感じで、usdmateriapathアトリビュートを元にMaterial Bindというリレーションシップを再設定しています。
string materialpath = usd_attrib(0, @primpath, "usdmaterialpath"); //assign usd_setrelationshiptargets(0, @primpath, "material:binding", array(materialpath));
実は...
H19.5からVEXをかかなくても同様のオプションがSOP Importに付きました。使う場合は赤枠の2つのどちらかを推奨。
ですが、2,3番目は上流にMaterialがある場合のみ動作します。つまり事前にUSDキャッシュとして別ファイルに書き出したい場合は使えません。
4,5番目はMaterialの階層を作ってくれます。なので上流にマテリアルがつながっていなくても動作します。※レンダリングに反映させるにはStage上どこかににMaterialは必要
4,5番目の問題は階層"だけ"が作られてしまうので、この後でGraft Branch/Stage等で一つ上にまとめる階層を作ったり、まとめたりができません。
なので書き出したときのPrimitive Pathからの変更が大変になります。
別の問題
SOP Importの設定2~5共通の問題なんですが、Meshに直接Material Bindのリレーションシップを作ってしまうのでデータ効率が非常に良くないです。あと、直接アサインってちょっとデータ的にどうかと思います。
RBDの例でいうと破片の数が増えたら増えた分だけ全てにマテリアルをアサインしている状態です。
その他のRe Assign
最初のRe AssignはSOP Importのアサインと同様のMeshに直接アサインしている状態だったので、ここをもう少しキレイにします。
Parentにアサイン
まずアサインするためにSOPのnameでname/nameというように1階層作ります。これと以下VEXで直接Meshアサインを回避できます。
ただこれは直接アサインは回避できたかもしれませんがアサインしている回数は変わりません。でも少しだけキレイなのでこれでも良いかも。
//get primvar string materialpath = usd_attrib(0, @primpath, "usdmaterialpath"); //check parent string parent = usd_parentpath(0, @primpath); int check = usd_isrelationship(0, parent, "material:binding"); //assign if (check == 0) { usd_setrelationshiptargets(0, parent, "material:binding", array(materialpath)); }
マテリアル名の階層でアサイン
SOP上で以下VEXで等でusdmaterialpathから末尾のマテリアル名を取得して階層を作ります。
string pathSplit[] = split(s@usdmaterialpath, "/"); s@material = pathSplit[len(pathSplit)-1]; s@path = s@path + "/"+ s@material + "/"+ s@name + "/"+ s@name;
この状態で以下VEXをを使ってマテリアル名の階層にMaterial Bindを作成します。
階層が作れること前提ですがかなりMaterial Bindはきれいにできます。
//get primvar string materialpath = usd_attrib(0, @primpath, "usdmaterialpath"); //search material name string pathSplit[] = split(materialpath, "/"); string material = pathSplit[len(pathSplit)-1]; //search material hierarchy int checkName = re_find(material, @primpath); //set materialbind if (checkName == 1) { string parentArray[] = re_split(material, @primpath, 0); string parent = parentArray[0] + material; int check = usd_isrelationship(0, parent, "material:binding"); if (check == 0) { usd_setrelationshiptargets(0, parent, "material:binding", array(materialpath)); } }
断面に別マテリアルをアサイン
どんな場合でも、断面に別マテリアルをアサインしたい場合はGeomSubsetを使用するといいです。階層でinside / outsideを分けると上記のような大量の問題とぶつかりますし、GeomSubsetであればどちらも対応できます。
どう使うのかというと、断面にアサインしたいマテリアルがある状態でAssign Materialノードなので以下のようなマッチングパターンを使います。
すべての階層のSubsetから、insideという名前のSubsetだけを選択するというマッチングパターンです。
%type(UsdGeomSubset) & { usd_name(0, @primpath) == "inside" }
ただこれだと、例えばあるPrimitiveのinsideだけアサインしたい。とかができないのでちょっと改変してこうすると使いやすいと思います。
%descendants(/rubbertoy) & %type(UsdGeomSubset) & { usd_name(0, @primpath) == "inside" }
これで/rubbertoyという階層の下のinsideというSubsetだけにアサインされます。