記事の目次

    VOPを使って新しいPrimitiveを作成します。

    TEXT_秋元純一 / Junichi Akimoto(トランジスタ・スタジオ/ディレクター)
    日本でも指折りのHoudini アーティスト。
    手がけてきた作品は数々の賞を受賞している。
    代表作に、HIDETAKETAKAYAMA『Express feat. Silla(mum)』など。
    www.transistorstudio.co.jp
    blog.junichiakimoto.com


    EDIT_小村仁美 / Hitomi Komura(CGWORLD)

    Add Primitive

    今回のモチーフは、プロシージャルに作成されたラインのジオメトリです。同様の働きをするものとしてはAfter EffectsのプラグインPlexusが有名ですが、こういったラインエフェクトは現場でもよく求められます。今回は、そういったプロシージャルなラインエフェクト作成のアプローチのために、基本的な概念から解説していきたいと思います。Houdiniでは、こういったエフェクトを作成するためにすでに様々なHDAなどが準備されていたりもします。ただ、便利がゆえ、それをそのまま使用しているだけでは、実際のプロジェクトの際、細かな応用が効かず、プロシージャルが足かせとなってしまうこともあります。そのような事態を回避するためにも、基本的な概念はしっかりと押さえておくと良いでしょう。

    今回はあえてWrangleではなく、VOPを使って視覚的にわかるように組んでいます。ただ、複雑な調整が必要な場合は、VOPよりもWrangleでコーディングしてしまう方が、デバッグが容易なのも確かです。しかし、Wrangleを見よう見まねで書いてしまうより、VOPで理解しながら組み上げることも、はじめの一歩としては大事になります。今回はそう言ったところに切り込むため、段階を踏んで解説していければと思います。

    今回のHoudiniプロジェクトデータはこちら

    01 Connect Flow

    Pointをつなぐ概念を解説します。

    まず、2つのアプローチを解説します。最初はすべてのPointに対してそれぞれPrimitiveをつくる方法です。VOP【A】はPointに対してRun Overするように設定します。これにより、すべてのPointに対して処理を回すため、イメージとしては、Pointごとにループ処理しているようなかたちになります。

    If Block【B】を用いて、PointがPointの総数よりも小さい場合のみ【C】、Primitiveがつくられるようにします。Primitiveをつくるためには、Add Primitive VOP【D】を使用します。Add Primitive VOPはつくられたPrimitiveの番号を出力します。Primitiveを作成するためにはVertexが必要です。Houdiniでは、Pointに対して必ずVertexが存在しているわけではないので、Pointに対してAdd Vertex VOPを用いてVertexを作成します。このとき、Primitive番号を指定するため、Add Primitive VOPから出力された値を使用します。また、Point番号はptnum【E】とptnumに1を加算したもの【F】で、2つのVertexを作成します。これにより、2つのVertex間をPrimitiveで繋ぐことができました。注意点としては、If Blockの中に格納するために、Constantで0という値を繋いでいます【G】。通常は繋がなくても動作します。

    次に、すべてのPointを1つのPrimitiveにまとめて繋ぐ方法です。このアプローチでは、VOPをDetailにRun Overさせます【H】。このようにすることで、Primitiveは1つしかつくられません。For Loop【I】を使って、ptnum分のループを処理します。indexから取られる番号をPoint番号【J】として、Primitive番号はループ外にいるAdd Primitive VOP【K】から取得します。これにより、すべてのPointが1つのPrimitiveにまとまって繋がります。

    Infoで確認してみると、Pointごとに処理した場合【1】【2】と1つのPrimitiveにまとめた場合【3】【4】で、Primitiveの数とVertexの数にちがいがあることがわかります。このように、シチュエーションに応じたアプローチを用いることができるようになるためには、VOPの概念を知る必要があります。


    次ページ:
    02 Connect Flow:Advance

    [[SplitPage]]

    02 Connect Flow:Advance

    3つ目は応用的なアプローチとして、idのようなAttributeをもっているPoint【L】をidごとに繋ぐ方法を紹介します。この場合、まずVOPはDetailにRun Overで準備します【M】

    大きなFor Loop【N】を作成し、Unique Value Count of Attrtibute VOP【O】を用いて、idの総数を取得します【5】。これにより、idの数と作成されるPrimitiveの数が一致します。

    続いて、少々イレギュラーな方法ですが、idからPoint番号をArrayとして格納する方法を紹介します。まず、Arrayを作成するために、Import Point Attribute VOP【P】を使用して、idの値を取得します【6】

    その際に、For LoopのIndexとImport Point Attributeのptnumをコネクトします。これでidを取得して、Integer to Vector VOP【Q】でComponent1にセットします。これで、Pの代わりにidが横一列に並んでいるような状態になります。このPを用いて、Point Cloud Find VOP【R】でidからPoint番号を探します【7】。Search Radiusは0.5以上であれば問題ありません【8】。また、Max Pointはptnumをセットします【9】。このArrayをArray Sort VOP【S】で順番に並べ直し、これを用いたFor Each【T】を作成します。For Each内にAdd Vertex VOP【U】を作成し、elementをptnumとします。また、For Loop内に作成したAdd Primitive VOP【V】のprimitiveをAdd Vertex VOPに接続します。

    これで、idごとに繋がったPrimitiveを作成することが可能になります【10】。Infoで確認すれば、Primitiveがidの数と一致していることが確認できます【11】。Add SOPでも同じことを簡単に素早く行えますが、今回のようなアプローチを知っておくことで、応用すれば後々重宝すると考えます。

    次ページ:
    03 Practical

    [[SplitPage]]

    03 Practical

    応用した接続方法の解説です。

    さらにこれを応用して、Pointから距離でサーチしたPointと接続を試みます。Point Cloud Find VOP【A】でPointからの距離と数を指定して、近くにいるPointを探します。それをリストアップしてArrayに格納し、それらの値からFor Each【B】で処理します。このとき、余分なPrimitiveがつくられないように、elementとptnumをCompare VOP【C】で比較し、If Block【D】で制御します。その中で、Add Primitive VOP【E】とAdd Vertex VOPを作成します。Add Vertex VOPの1つは、ptnumをPoint番号として【F】もう1つはelementをPoint番号とします【G】。このようにすることで、サーチして格納したPointそれぞれに、1つのPointから繋ぐことができます。これで、ラインエフェクトのベースが完成です【1】

    04 Operators

    主要ノードを解説します。

    Add Primitive VOP【A】はPrimitiveを作成するオペレータです。Run Overで数は変わってきます。例えば、Detailで作成した場合は、1つ作成されます。値はPrimitive Numberを出力します。Add Point VOP【B】は今回使用していませんが、Vertexを作成する際、参照するPointが必要となります。1から全て作成する際は、まずAdd Point VOPでPointを作成してから、ptnumを値として出力する必要があります。作成方法は、座標を指定する方法と元々のPointからCloneする方法があります。Add Vertex VOP【C】は、ヒモ付けするPoint Numberから作成されます。また、VertexによってつくられるPrimitiveも指定する必要があります。このオペレータは、配置するだけで作成される仕様になっているため、フローティングしており、どこにも接続されていない状態で問題ありません。

    例えば、Add Point VOPを使えば、何も存在しない状態から、For Loopを使ってPointを新たに配置して、1つのPrimitiveとして繋げることもできます。この例では、一定方向に分割されたラインを、Noiseに応じて振幅するPointを作成し、それを繋げています。