くり返しを利用したエフェクトを解説します。

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


EDIT_小村仁美 / Hitomi Komura(CGWORLD)

くり返し

今回のモチーフは「テスラコイル」です。エフェクト制作をされている方なら一度は耳にしたことがあるのではないでしょうか。筆者は、Houdiniによるエフェクトは大きく2種類あると考えます。それは、シミュレーションによるエフェクトと、プロシージャルモデリングによるエフェクトです。今回は後者のプロシージャルモデリングを駆使したエフェクトを作成していきたいと思います。

Houdiniにおいて、今回のようなアプローチは古典的ではありますが非常に有用であり、なおかつ基礎を学習するにはもってこいの内容です。特にSOPのみをつかってエフェクトを作成することの優位性としては、リアルタイムでシークができるため、シミュレーションの待機時間なくアニメーションの良し悪しを確認できる点にあります。また、通常のシミュレーションでは再現できないエフェクトなどは、SOPを駆使することで表現可能になることがあります。

筆者としては、シミュレーションによるエフェクトよりもSOPを使ったプロシージャルなエフェクトを作成する方が好みです。これは、Houdiniの良さを十分に発揮できるということもありますし、それ以上に、Houdiniでしか表現できないエフェクトをつくることに喜びを感じるからです。今回のアプローチを参考にしていただければ幸いです。

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

01 All Flow

全体のワークフローは下記のようになります。



  • ノードの全体像(左から右に続きます)


まず、Emitterとなるジオメトリを準備します。ジオメトリはPointのNormalをもっている状態で、Scatter SOPを使用しTrunk(幹)のルートになるPointを発生させます【A】【1】。Pointには、Wrangleなどを使用してPoint番号からidのAttributeを作成します。そのPointをRay SOPを使ってCollisionとなるジオメトリにヒットさせます【B】。ヒットさせたPointと元になったPointをAdd SOPのAttributeから接続します【C】【2】


ヒットしなかったPointはFuse SOPを使って消します【D】。次に、繋いだPolygonに対してUVを作成し【E】、それをResample SOPを使って分割します【F】。その後VOPを使用してNoiseを追加していきますが、この手順の詳細に関しては後述します【G】。Noiseが追加できたら、Polyframe SOPを使ってNormalをTangent方向へ作成し【H】、そのNormalを反転します【I】。続いてVOPを使ってラインの太さのAttributeを作成しますが、こちらも後述します【J】。これで、Trunk部分の完成です【3】



続いて、Branch(枝)の作成をしていきます。ここでのポイントはFor Loopを使用したアプローチになります【K】。Branchは枝から枝が生える世代が重なって構成されます。Trunkのフローを何度もコピーしてくり返せばBranchになりますが、何世代に渡ってくり返すかによって手間が変わってきます。そのため、For Loopを使用して1つのフローをくり返します。



  • Block End【L】の設定は、By Count【4】で、Iterationsで世代の数を決定します【5】。また、最後にくり返したデータを合わせて出力するように、Gather MethodをMerge Each Iterationに設定します【6】。Block Begin【M】からMeta Data【N】を作成し、Branchの数【O】がIterationごとにランダムになるようにExpressionを組みます【7】

その他のながれは概ねTrunkのフローと同じです。若干異なるのがNormalの調整です。これはRay SOPで飛ばした際に横に広がりすぎないような調整をVOPで行います【P】。また、BranchのThicknessを設定するVOPも若干異なりますので、後述をご確認ください【Q】。Measure SOPを使って、そのBranchの長さから、Noiseの強さを設定するようなしくみをつくるのも有用です【R】。これですべてのフローの完成になります【8】


次ページ:
02 VOP Flow

[[SplitPage]]

02 VOP Flow

Noiseを追加するVOPの内容を詳しく解説していきます。パラメータ【1】を確認すればわかると思いますが、UVからRampを使ってNoiseを追加するエリアを決定します【A】。その値をNoiseに対して乗算し、変形させるエリアを設置点以外の部分にします【B】。NoiseはOffsetに対してアニメーションを入れることで、電流がながれているような挙動を調整できます【2】。次に、Branchが発生するエリアを作成します【3】。こちらもUVからRampを使用して作成します【C】【4】。この値をBind Exportを使ってAttribute化します【D】。これで各ラインの設定は完了です。



続いてPolygonのラインの太さを決定するpscaleを作成します。pscaleはUVからRampを使って作成します。Rampでは根本から先端にかけてのシェイプを調整します。その値に対して係数を乗算をして太さを決定し、Bind ExportでpscaleのAttributeにします【E】。これがTrunkの設定です。Branchの場合は、乗算する係数にもともともっているpscaleを使用します【F】。ScatterでPointを散布した場合、そのAttributeを引き継ぐことができるのでそれを利用します。このようにすることで、Branchの根本の太さは発生部分と一致して先端にいくほど細くなる状態をつくり出せます。


ここでは少し変わった調整を行います。Houdiniならではのアプローチだと思います。普通にNoiseをかけた状態でBranchを発生させると、Rayで飛ばした際に横に広がりすぎる可能性が出てきます。そのため、まずCollisionのNormalを反転させてAttribute Transferで移します。このNormalと自身のNormalの内積値をDot Productを使って計算します【G】。この値から寝ているNormalを抽出して、Collision方向に寄せたNormalとSwitchします【H】。これにより、横に広がりすぎないエフェクトにすることができます【5】

次ページ:
03 Rendering

[[SplitPage]]

03 Rendering

最後にレンダリングの設定をします。Mantraの場合、Physically Based Renderingで行います【1】。これによって、放電の光を使ってライティングすることができます。そのためにはLimitsのDiffuse Limitを高く設定します【2】。また、Reflection Qualityを高くすることで、光が反射した際のノイズを抑えます【3】。それでもノイズを消すことは難しいと思いますので、その際はPixel Sampleを高く設定します【4】


これで、ノイズはどんどん減っていきます。さらにこれは細いラインをレンダリングする際に有用な設定で、なめらかなラインをレンダリングすることができます。ただし、Pixel Sampleはレンダリングのコストが累進的に上がっていきますので、上げすぎには注意しましょう。マテリアルの設定はBDSFで作成する必要があります。これはPhysically Based Renderingのため必須です。



  • PBR Emissionなどを使用することでジオメトリライトとしてのマテリアルが作成できます【A】。もちろん、デフォルトで準備されているマテリアルでも十分設定可能です。これでレンダリングが完了します【5】

04 Operator

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

For Loop

For Loopは2つのノードで構成されています。この2つのノードに挟まれている部分を反復することで、くり返しのプロシージャルを作成できます。

Block Begin:Methodを決定します。Feedbackで使用すると、最初の出力以降は反復されたデータが出力されていきます。Pieceは入力されたデータからnameなどを読み取り、エレメントとして切り分けて出力します。Metadataは現在のループに関する情報を含んだエンプティジオメトリを出力します。
Block End:大きくはCountとPiecesに分けられます。Countは決められたIterationsで反復します。Piecesでは入力されたジオメトリがもつ情報からエレメントを切り分けます。
Gather Method:フィードバックをそれぞれのIterationもしくはPieceに対してくり返して処理するか、反復処理を最後にまとめて結合するかを設定できます。

あとがき

今回のフローでは、Branchの世代をCountで決定して、どの程度細かいBranchを作成するか、Iterationの数値だけで調整できます。今回のように10回程度であればノードをくり返し配置することは不可能ではないですが、手間がかかります。もちろん、くり返しの処理が100や1000などになった場合では、ノードの配置によって処理することは難しいです。どのようなシチュエーションによってForが役立つのか、その選択眼をいかに養うかが肝です。