すあまの備忘録

誰得内容の自分のための非営利目的備忘録ブログ(筆者がわかっても内緒にしてください)

Solaris - Primitiveの階層を変えたものにMaterial Bindを戻す

USD的には階層が変わらないようにすべきですが、それでもどうしてもInstanceに対してRBDをした場合等、階層の維持が困難な場合に。 もちらんリレーションシップを戻すだけなので同一Stage上にマテリアルは必要です。

hip

matwerialReAssign_v002.hiplc - Google ドライブ

Houdini 20.0.506

正攻法

USDは同一階層であれば自動で上書き処理になるので、SOPに一度行ってしまったとしてもアサインがおかしくなることはないです。

元の状態

SOPでの破壊処理

Subsetまですべて階層が一致しているのでマテリアルはそのままくる

SOPから持ってきたキャッシュが2重Transfromかかる場合

位置がおかしいデータ

これはやりがちなんですが、LOP上でTransformがかかっているものはSOPに持っていった際にTransformがベイクされます。

つまりこのまま何もしないで戻すと再度Transformがかかって2重に移動した位置に行ってしまいます。

LOPの移動情報は、usdxformアトリビュートとして残っているので、LOPに戻す前にTransform by Attribute等で戻してあげることで相殺します。

階層を変えてしまったものにマテリアルを戻す

元の階層

RBD後の階層

階層が壊れたので自動再アサインはされない

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アサインを回避できます。

ただこれは直接アサインは回避できたかもしれませんがアサインしている回数は変わりません。でも少しだけキレイなのでこれでも良いかも。

name/nameで階層を作る

//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だけにアサインされます。