筆者環境
・Bifrost2.5.0.0
・Maya2020.2

前回は配列データが入力されると自動的にループになるオートループ機能を紹介しました。今回はループカウンタを使った明示的なループの作成方法をご紹介します。
(当ブログで[]でくくっているものはBifrostGraphEditor内のノード名です)

◆Bifrostでは3種類のループ専用ノードが提供されています。

    1. 1.iterate : 直列処理 / 指定した回数イテレーションを行う
      2.do_while : 直列処理 / 条件を満たすまでイテレーションを行う
      3.for_each  : 並列処理

個人的にはBifrostのループノードの実装は非常に洗練されていると感じていますが、とはいえ通常のグラフ制作とは少し頭の切り替えが必要になります。まずは前回もお伝えした通り一旦オートループで実装できないか検討してみることをお勧めします。

今回はループノード専用の新しいポートアイコンが幾つか登場します。まずは共通して使用するイテレーション回数に関するポートを見てみましょう。

イテレーションリミット


繰り返しの最大実行回数を指定するポートです。
もしループノード内で配列外参照などのエラーが発生した場合、指定回数に達することなくイテレーションが終了します。よってこのポートで設定するのはあくまで”最大”実行回数であるという点に注意してください。

イテレーションカウンタ


ループノードの外側ではループカウンタの初期値を設定するポートとして機能し、内側では現在のループカウンタを返すポートとなります。

[iterate]の紹介の中でサンプルを交えながらもう少し詳しく見ていきましょう。

iterate (直列処理)

指定した回数イテレーションを行うノードです。

使用例を見てみましょう。
下図は初期値100に対してループカウンタの値を5回加算しようとしているグラフです。処理のイメージとしては「100+0+1+2+3+4」という感じです。
グラフのテキストデータは06.zipのsample1.txtです。
(*注 このグラフは後で説明する設定が不足しているため想定した処理にはなりません)

このグラフでのイテレーションリミットとカウンタの動作説明

リミットに”5”が入力されているので、ノード内の処理は5回繰り返し実行されます。
カウンタの入力値は”0″なので0からスタートします。ノード内部のカウンタポートの値は繰り返しの度に0,1,2,3,4と順に変化していきます。この値を加算で使用することで[+0+1+2+3+4]を実行しようとしています。
このグラフをコード化すると下記のようになります。

■python:

val = 100;
for current_index in range (0, 5):
	val = current_index + val

 

■C++:

int val = 100;
for(int current_index=0; current_index<5; ++current_index){
	val = current_index + val;
}

このグラフは一見意図した通りに組めていそうですが最終的な出力値は”104″となります。なぜでしょうか?
原因はループの度にvalポートに入力された値を参照し直していることにあります。つまり前回のループの実行結果が次に引き継がれずに破棄されている状態です。

今回のように結果を次のイテレーションに継承したい時のために、ステートポートという特殊なポートが用意されています。

ステートポート


これは#04で紹介したフィードバックポートのループノード版です。
設定はポートの右クリックメニュー>set port stateで行います。これによってoutputにセットされたデータを次のイテレーションではinput側のポートで取得できる状態になります。

今回のサンプルの場合、outputのポート”sum”に渡された値は次回のイテレーションではinput側のポート”val”から入力データとして使用されます。
最終的にoutputポートから出力する値は110となります。

do_while (直列処理)

ある条件を満たすまでイテレーションを行うノードです。
イテレーションを継続するかを判定するためのdo_while専用のポートアイコンがあります。

ループコンディション



このポートにTrueが渡るとループを継続、Falseで終了となります。ループコンディションの評価はタスク終了時に実行されます。つまり最低1回はタスクが実行されるということですので注意してください。
その他はiterateと同じ仕様です。

下図はループカウンタを順に加算した値が5以上になると終了するグラフです。
グラフのテキストデータは06.zipのsample2.txtです。

for_each (並列処理)

並列でタスクを実行するノードです。
オートループよりもう少し複雑な並列処理を行いたい場合に使用します。

基本的な使い方はiterateと同じですが、繰り返しではなく並列実行なのでステートポートは設定できない点に注意してください。
また値の更新を行いたい配列データに対して以下のイテレーションターゲットの設定が必要です。

イテレーションターゲット



値の更新を行いたい配列データに対して、ノード内でポートの右クリックメニュー>set port iteration targetをtrueにします。
このポートからは配列データ内のループカウンタに該当するデータが取得できます。つまり自動的に[get_from_array]が実行された状態です。一方で出力ポートがこのアイコンの場合はループカウンタで自動的に[set_in_array]が実行された状態になります。
(ポートの形状を見てみると、ノード外部ではハットアイコン(配列データ)で、ノード内部では四角形(単体データ)になっていることからも、抜き出した配列要素にアクセスしていることを表しています)

下図はメッシュの頂点に対して頂点番号が0~3であれば位置を5倍、それ以外は1/2倍するグラフです。
グラフのテキストデータは06.zipのsample3.txtです。

 

最後に

ループノードの説明は以上です。
やはり慣れが必要ですし多重ループや2D/3D配列を扱い始めると難解なグラフになっていきます。繰り返しになりますがオートループで実装できそうであればそちらをお勧めします。

次回からはray_castやclosestといった空間検索ノードを見ていきます。
Bifrostを使用したツール開発のメリットが段々と見えてくると思いますよ。「空間検索【前半】」へどうぞ。

takashima

高島 正規
テクニカルアーティスト

長年シネマティックアーティストとしてフェイスリグやフェイシャルモーションの制作を担当。現在は技術戦略グループに所属し、DCCツールのプラグイン開発のための内製C++ライブラリの構築と、Bifrostを用いたツール開発を行っている。