kick the base

Houdiniと、CG技術と、日々のこと。

Houdini: SubdivideやResampleで生成されたポイントをグルーピングする

プロローグ

2019/4/6に第1回HoudiniAidを開催しました。振り返り記事はこちら

本記事ではそこで僕が質問した内容をまとめようと思います。ありがたいことに勉強会やTwitterを通して様々な方がアドバイスをくださり、最適な解を得ることができました。この場を借りてお礼申し上げます。

お題

SubdivideやResampleによって増加するポイントを判定したい

以上。やっていきましょう。 

サンプルファイル

サンプルhip

SubdivideとResampleの性質

まずはSubdivideとResampleの性質について確認しておきます。

Subdivide

Primitiveにアトリビュートを与えた場合がわかりやすいです。

f:id:kickbase:20190426175037g:plain

分割された後もオリジナルのアトリビュートを引き継ぎます。

ポイントアトリビュートの場合はPrimitiveが参照しているPointで最初に見つかったものを引き継ぐのかな?とにらんでいます

Resample

f:id:kickbase:20190426175532g:plain

既存のアトリビュートを線形的に補完します。

これらの挙動が便利なときもありますが、不便なところもあります。

目標

分割前の状態で各Pointに1を与えていたとしたら

f:id:kickbase:20190426175749p:plain

分割後に増えたPointは0を持っていてほしいということです。

f:id:kickbase:20190426175831p:plain

元のシーンファイル

僕が持って行ったシーンでも一応できてはいました。

f:id:kickbase:20190426180610p:plain

流れとしては簡単です。

  1. GroupCreateSOPでold_pointsグループを分割前のジオメトリに付与します。対象はすべてのポイントです。
  2. GroupTransferSOPでグループを転写します。

しかしこのGroupTransferSOPが気に入りません。転写の影響範囲を決定するDistance Thresholdはジオメトリの形状によって手作業で数値を変更する必要があります。

Distance Thresholdの値が0でも反応してくれれば問題ないのですが、0だと転写してくれません。またこの値を0.0001などの小さな値にするというのはダサいので避けたいところです

プロシージャルに、一対一対応できるような実装にしたいですね

試行錯誤期

idtopoint関数を使うのは割とアリな気がしたんですけど、ツイートの通りResampleではできなかったので撃沈。

佐藤さんに上記アドバイスをいただきました。残念ながらLineではうまくいかなかったのですが、オリジナルのポイント総数とポイントナンバーを比較するというアイデアは後々どこかで使えそうな気がします。ありがとうございました!

できたけどカッコ悪い実装

f:id:kickbase:20190426190034p:plain

ロジック的にも間違いはないし、判定はできるけどクールじゃない実装。

特にPointWrangle内でfor回すのがとてもダサいですね。

i@group_newpoints = 1;
for(int i=0; i<npoints(1); i++){
    vector pos_origin = point(1, "P", i);
    if (@P == pos_origin) {
        i@group_newpoints = 0;
        break;
    };
};

コードの考え方は下記の通り。

  1. コードの最初でnewpointsグループを定義
  2. 分割前の総ポイント数だけforを回す
  3. PointWrangle第二入力からポイントの位置を取得
  4. ジオメトリのポイントと位置が同じだったら(つまり分割前と同じ位置だったら)グループから外す

最適解

f:id:kickbase:20190426190654p:plain

大翔士さんにいただいたアドバイスで最適解を得ることができました。うーんスマート。ありがとうございました!

i@group_newpoints = 1;
int np = nearpoint(1, @P, 0);
if (np >= 0) i@group_newpoints = 0;

ループも消えてスッキリしました。これぞVEX!って感じですね。

nearpoint関数はジオメトリに対して位置を指定し、指定した距離を検索し、見つかればポイントナンバーを。見つからなければ-1を返します。

ここでは分割前のジオメトリに対し、分割後のポイントを中心に距離ゼロで検索しているのがポイントです。これで同ポジションかどうかが判定できますね。*1

まとめ

今回僕が持って行った質問は、行き詰ったというよりよりいいやり方があるんじゃないか?というものでした。諸先輩方のアドバイスもあり、Houdiniらしい解決法にたどり着けました。

様々なアプローチがあるところがHoudiniの面白いところですね。

参考リンク

*1:Attribute Transferは距離ゼロでは転写できないので中身が違うのかもしれません