0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

チュートリアルでつまづいたシリーズ:凹(谷折り、Concave)エッジのPolyExtrude

Last updated at Posted at 2025-05-25

実行環境

Windows11 Houdini 20.5.445 Educational License

記事の内容と対象読者

こんにちは。私は元グラフィックス・エンジニアで、現在は大学教員をしています。Houdiniは研究室で扱っているだけで科目ではまだ扱っていないのですが、CGのアルゴリズムやデータ構造を説明するのに最適な教材はHoudiniだと考えているので、いろいろな教科書や動画記事を参考にしながらHoudiniを扱う科目のシラバスを思案しているところです。

そんなおり、Houdiniの前身であるPrismまで遡る無駄に長い私のプロシージャル道において、何度となくつまづいてきたポイントにまたしてもつまづきました。同じ教科書や動画記事で勉強している私と同じレベルの人のお役に立ったり、詳しい人にはアドバイスを頂けたら大変うれしいので記事にしていきます。よろしくお願いします。

お題の教材

Entagmaさんの初心者向けKineFX/APEX動画3本シリーズの第1回です。英語ですがHIPファイルもありますし、KineFX/APEXの入門として素晴らしくわかりやすいのでおススメです。

で、期待していた人にはすみませんが、私がつまづいたのは本題のKineFX/APEXではなく、プロシージャルモデリングをAPEXネットワークでやってみようという箇所の作例に関してです。この動画はモデリングを意図しているわけではないのでまったく言及されていませんが、嫌な感じのトポロジーが出来ている箇所があってレンダリングがおかしくなってます。

ちょっと気になるので、1分もかからないつもりでそれを修正しはじめました。そして、これまで何度もつまづいたことにまたつまづいて結局1時間くらいかかりました。これは記事にして残しておかないとというわけです。

問題:凹エッジを持つジオメトリをポリゴンプリミティブ単位でバラバラに押し出すといろいろとっ散らかります。

動画では正12面体をポリゴンプリミティブ単位で2回押し出しています。PolyExtrudeSOP.DivideIntoパラメーターがIndividualElementsになっているのを覚えておいてください。
image.png

Animation2.gif

一見問題なさそうですが、よく見ると凹エッジだった部分でPolyExtrudeSOPに押し出された形状同士のインターセクトやオーバーラップが起きています。

image.png

こういう箇所はレンダリングの際に意図しないアーティファクトを生じます。図は問題個所をレンダリングしたものですが、オーバーラップが継ぎ目として表れていますし、インターセクションはジャギーな線として表れてしまいます。

image.png

問題を単純化するとこういう事です。PolyExtrudeSOPは法線に沿って面を押し出すわけですが、IndividualElementsで複数の面を同時に押し出すと面同士の角度によってはお互いにぶつかってしまうわけです。

Animation2.gif

このような箇所は個別に抜き出して、PolyExtrudeSOP.DivideIntoパラメーターをConnectedComponentとしてから押し出す必要があります。

image.png
Animation2.gif

提案:問題解決手法

今回の作例の問題点が整理できましたので、このような手順で修正していきます。

  1. ジオメトリの凹エッジを抽出してエッジグループvalleyを作る
  2. valleyに所属する凹エッジを共有する2枚のポリゴンプリミティブを探し出す
  3. その2枚にプリミティブアトリビュートpairを作成して同じ値にしておく
  4. pairの値が同じ2枚のポリゴンプリミティブを抜き出す
  5. その2枚だけを対象にConnectedComponentとして押し出す
  6. 以下、4と5の繰り返し

手順1:GroupCreateSOP

今回の場合「エッジを共有する2枚のポリゴンプリミティブの角度が90より小さい」ときに凹エッジとして判定してvalleyエッジグループに入れればよさそうです。

image.png

手順2,3:WrangleSOP (RunOverDetail)

VEXでvalleyエッジグループからエッジを一つづつ抜き出してそのエッジを共有する2枚のポリゴンプリミティブに対してアトリビュートpairを作成するプログラムを書きます。アトリビュートはすべてのプリミティブに対してデフォルト値で作成されるので、スプレッドシートを見るとわかりますが、凸エッジしか持たないプリミティブのpairは空文字列になっています。あとでわかりますがこれはとても都合がよいです。

image.png

VEXはこのようになります。エッジグループとは言っても両端点のポイント番号がセットで並んだ配列が帰ってくるだけです。VEXでエッジグループ単位の処理を書くのは、ほかのコンポーネントに比べると少し面倒なのです。

//エッジグループvalleyを取得
int edge_group[] = expandedgegroup(0, "valley");

//エッジを一つづつ抜き出す
for (int i = 0; i<len(edge_group); i+=2 ){
    
    //エッジを共有するポリゴンプリミティブを探し出す
    int p0s[] = pointprims( 0, edge_group[i] );
    int p1s[] = pointprims( 0, edge_group[i+1] );
    int found[];
    
    foreach( int p0; p0s ){
        foreach( int p1; p1s ){
            if( p0 == p1 ) push( found, p0 );
        }
    }
    
    //ユニークなIDでアトリビュートpairを作成して
    //2枚のポリゴンに埋め込んでおく
    string name = itoa(found[0]) + "_" + itoa(found[1]);
    setprimattrib(0, "pair", found[0], name, "set" );
    setprimattrib(0, "pair", found[1], name, "set" );
} 

エッジはHoudiniのファーストクラスコンポーネントではないので、ほかのコンポーネントと違いVEXの定型処理というのがなく、いくつかやり方があります。今回のようにRunOverDetailで処理をシリアライズするとVEXの並列性を活かせません。RunOverPrimitiveでハーフエッジ検索などした方が早いかもしれませんが、プログラム的に複雑になるので今回はこれにしました。そもそもSOPだけで同じことができるはずだとずっと思っていて試してはつまづいております。詳しい方は、是非、ご教授ください。

ここで試しにExplodedViewSOPを使ってpairアトリビュート単位でプリミティブを分割してやるとこのようになります。意図した分割が出来ていることがわかります。この単位で2回目のPolyExtrudeSOPを個別に適用できればよいわけです。

Animation2.gif

手順4,5: For-EachNamedPrimitive

pairアトリビュートが同じ値のプリミティブを抜き出して、PolyExtrudeSOPを適用し、本体とマージする、を繰り返します。For-EachNamedPrimitiveを作成してSOPのForループを作ります。BlockEnd.PieceAttributeパラメーターをpairにしておくのが重要です。

image.png
image.png

BlockBeginSOPとBlockEndSOPの間に二つ目のPolyExtrudeSOPを挿入し、DivideIntoパラメーターをConnectedComponentにしておきます。

image.png

Forループから出てきたジオメトリーはマージされていますが、先ほどと同様にExplodedViewSOPで見るとわかる通り、トポロジー的にいくつかの島に分かれています。

Animation2.gif

これだと後々いろいろな問題が起きるのでFuseSOPを使ってひとつながりにしておきます。オンオフすると頂点が共有されることでシェーディングの様子が変わるのがわかります。

Animation2.gif

提案手法に関する考察

BevelSOP、NormalSOP、ColorSOPを追加して元の作例と提案手法の作例をSwitchSOPで比較してみます。最初が元の作例で次が提案手法の作例です。
image.png
image.png

一見よさそうなんですが、BevelSOPのパラメーターによっては出てきてしまう意図しないアーティファクトが気になります。レンダリングしたときにアンビエントオクルージョンがかかるところですが、ライトによってはやたらと明るく見えてしまう事があります。

image.png

画角に応じてBevelSOPの値をもっと小さくすればよいのですが、そもそも滑らかにする必要がないエッジにベベルがかかっているのが問題だと思います。平面に収まってるエッジにベベルかける必要ないですよね。

image.png

そこで、GroupCreateSOPでエッジを共有するポリゴンプリミティブの角度がほとんどないエッジグループを作っておきます。

image.png

PolyBevelSOPではそのグループ以外をベベルするようにします。

image.png

これでアーティファクトは消えました。no_bevelエッジグループを作っている箇所をオンオフすると違いがわかります。レンダリングした結果も同様に改善されましたのでこれで解決とします。

Animation2.gif

このように一部だけBevelSOPの処理を除外するとサブディビジョンかけたときにおかしくなりませんか?デバッグ用ブランチにSubdivideSOPを置いたり、ヴューポートサブディビジョンで確認するようにしてますが、今回のもちょっと怪しいです。なんとなく4角形以上ができたときに問題が発生するような気がしています。本題ではないので、ここでやめますが、この辺りもどうするのが正解なのか知りたいところです。
Animation2.gif

まとめと今後

凹エッジを持つジオメトリーを押し出す時に発生する問題と、その対処についてまとめました。「なんだよ、この程度のこと記事にすんな」と思うかもしれませんが、私は今回もSOPの名前、パラメーターの意味、VEXの関数名、あれやこれや「ああ、あの時にやったあれだ。どうやったけなあ?」で結構時間を使いました。「この程度のこと」まで記事にして残しておかないと忘れてしまう人は私以外にもいると信じて「チュートリアルでつまづいたシリーズ」最初の投稿を終えます。今後も「この程度のこと」を懇切丁寧に記事にしていきますのでよろしくお願いします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?