LOPsとUSDの概要


この記事ではHoudini18で追加された、LOPsとUSDについて、その簡単な紹介を行います。

概要

Universal Scene Description(USD)は、3Dアセットとシーンを効率的に構築およびコラボレーションするためにPixar社が開発したオープンソースシーンディスクリプションであり、Houdiniでは、LOPsとして実装されています。 LOPs経由でUSDを使用すると、複数のユーザー/部署が同じシーンで非破壊的に作業を行うことが可能です。スタジオパイプラインのフレームワークだけでなく、実際のインフラストラクチャを提供して、ステージ(シーン)の構築、プレゼンテーション、検査、編集の高速かつ効率的な方法となります。

Houdiniのライト及びルックデブのコンテキストはLOPです。これはライティングオペレーターの略です。 LOPネットワークは既存のオペレータの中ではSOPに似ています。SOPsでHoudiniのジオメトリモデルを作成するネットワークするように、LOPsはUSDを作成、編集するネットワークを構築します。

本来、USDは非常に包括的なフレームワークであるため、単純な構成ではありません。しかしHoudiniのLOPsは、基になるUSDのフレームワークを知らなくても良いように設計されています。 LOPを通じて公開されるUSDワークフローにより、テイクやマテリアルスタイルシートなどのHoudiniの一部の機能は、LOPsを使ったワークフローでは不要になります。

また、USDのワークフローでは独自の用語が利用されますが、既存のHoudiniの用語、従来のCGの用語、または標準的な英語と競合する場合があります。明確化のためにSideFX社用語集を参照してください。

シングルユーザーまたは小規模スタジオの場合、LOPsに労力をかける価値があるかどうか疑問に思うかもしれません。しかし特別な理由が何もなければ、検討する価値はありますので、従来のIFDファイルではなくUSDを消費するHoudiniレンダラーであるKarmaを使用することをお勧めします。
パイプラインまたはショット環境の概念については、最初は明白ではないように見えるかもしれません。しかし一度LOPsを習得すしてしまえば、制作にはLOPsが必要不可欠になるでしょう。

 

プリミティブパターン

LOPsを効果的に使用するために最初に知っておく必要があることがいくつかあります。まず1つは、LOPノードを操作するためのUSDプリミティブを指定する方法です。 ほとんどのLOPノードでは、これは単一の文字列パラメータ(通常はプリミティブパターンと呼ばれます)で設定されます。 ここでは、このプリミティブパターンの構文について紹介します。

パターンマッチングの基礎は、他のHoudini文字列マッチングフィールドで利用されている構文と同じです。
(スペースで区切られたトークンのリストです。各トークンのマッチング結果は結合されます。)

各トークンは標準パスマッチング構文を使用します。
(/ foo/* は/foo の直接の子のみに一致しますが、/foo/ はすべての直接およびネストされた子に一致します)

さらに、次の拡張機能があります。

+  

+ は引数をスペースで区切ってリストするのと同等で、引数の結合を行います。
以下の2行は同等の意味となります。

/geo/* /mat/*
/geo/* + /mat/*

 

–  

-は ^と同等で、最初の引数から2番目の引数を削除します。
以下の2行は同等の意味となります。

/geo/* ^/geo/foo
/geo/* – /geo/foo

 

&は、左引数と右引数の交差を行います。
以下の行では、box3, box4, box5が選択されます。

/geo/box[12345] & /geo/box[34567]

 

引数のグループ

引数のグループを括弧で囲んで、単一の引数として扱うことができます。
以下の行では/geo/fooと/geo/barを除く/geoのすべての子を選択します。

/geo/* – (/geo/foo + /geo/bar)

 

VEX エクスプレッション

VEX エクスプレッションは、中括弧で囲まれたパターンに埋め込むことができます。
以下の行は、2乗したときに4より大きくなるradiusアトリビュートを持つプリミティブにマッチします。

{float r2 = @radius * @radius; return r2> 4; }

以下の行は、シーン内の「mesh」プリミティブにマッチします。

{usd_typename(0、@primpath)== “mesh”}

 

%はコレクションを命名することができます。
以下の2行は同等の意味となります。

/foo/bar.collections:mycollection
%/foo/bar/mycollection

また、コレクションが/collectionsプリミティブの下にある場合、「/collections」プレフィックスを除外できます。
以下の3行は全て同等の意味となります。

/collections.collection:mycollection 
%/collections/mycollection 
%mycollection

 

サブレイヤーとリファレンスとペイロード

サブレイヤー

USDについて最初に理解しなければならないことの1つは、ディスク上の複数のファイルを1つのステージ、またはシーングラフに結合する方法です。 すべてのUSDファイルには、完全なシーングラフが含まれています。 2つのシーングラフを組み合わせるには、2通りの方法があります。

1つ目はサブレイヤーと呼ばれる機能を使う方法です。 このアプローチでは、シーングラフを別のシーングラフの上に直接配置します。 これら2つのシーングラフのシーングラフ階層が重ならない場合、2つのファイルをサブレイヤー化すると、2つのシーングラフが単純に結合されます。各プリミティブは1つのUSDファイルのみで定義されます。 

ここでは以下のような2つの単純なUSDファイルを例にして説明します。

foo.usd:

/foo1
/foo2
  /child1
  /child2

 

bar.usd:

/bar1
/bar2
  /child1
  /child2

 

空のシーングラフにサブレイヤーfoo.usdを配置すると、シーングラフは次のようになります。

/foo1
/foo2
  /child1
  /child2

 

次に、bar.usdを2番目のサブレイヤーとして追加すると、シーングラフは次のようになります。

/foo1
/foo2
  /child1
  /child2
/bar1
/bar2
  /child1
  /child2

 

共通のシーングラフの場所を持つ2つのUSDファイルを一緒にサブレイヤー化する場合、重複する情報はより、強力なレイヤーから取得されます。 以下のような3番目のファイルfoobar.usdを追加します。

foobar.usd:

/foo2
  /child2
  /child3
/bar2
  /child2
  /child3

foo.usdとサブレイヤーfoobar.usdをより強力なサブレイヤーとして追加すると、次のようになります。

/foo1 (from foo.usd)
/foo2 (from foobar.usd)
  /child1 (from foo.usd)
  /child2 (from foobar.usd)
  /child3 (from foobar.usd)
/bar2 (from foobar.usd)
  /child2 (from foobar.usd)
 /child3 (from foobar.usd)

 

「オーバーラップ」は、プリミティブレベルではなく、個々のプロパティまたはメタデータレベルでテストされます。 そのため、弱いレイヤーに、強いレイヤーで定義されていないアトリビュートが含まれている場合、その弱いレイヤーのアトリビュート値は、結合されたステージで表示されます。

 

サブレイヤー化は複数のファイルに対して行うことができます。したがって、bar.usdを上記のステージにサブレイヤー化し、より強力なレイヤーとして追加すると、結果は次のようになります。

/foo1 (from foo.usd)
/foo2 (from foobar.usd)
  /child1 (from foo.usd)
  /child2 (from foobar.usd)
  /child3 (from foobar.usd)
/bar1 (from bar.usd)
/bar2 (from bar.usd)
  /child1 (from bar.usd)
  /child2 (from bar.usd)
  /child3 (from foobar.usd)

 

サブレイヤーでは、元のファイルからのシーングラフの位置は固定されたままであることに注意してください。シーングラフの場所を移動または変更することはできません。このスタイルのレイヤー化は、それぞれがシーン全体に貢献することを意図したファイルを結合するのに非常に便利です。

パイプラインに当てはめると、これは、ショットごとに、各部門が独自のサブレイヤーを持つ、と考えることもできます。最も弱いレイヤーは、アセットが結合されて配置されるレイアウトレイヤーです。
次のレイヤーでは、アニメーション部門がシーンにキャラクターを追加します(既存のプリミティブの一部に強い評価を適用することにより、必要に応じてレイアウトを移動または調整することができます)。
その上に、FX部門が、シーングラフに新しいジオメトリを追加したり、レイアウト部門またはアニメーション部門によって追加された既存のプリミティブの一部にRBDシミュレーションを適用することができます。

これらの各担当は別々のサブレイヤーで作業しているため、単一のファイルの共有や編集の競合について心配する必要はありません。しかし、これらはすべて同じシーングラフ階層で機能しているため、各担当は、自身のレイヤーによって、より弱いレイヤーを作成している箇所に変更を適用できます。

サブレイヤーは、Sublayer LOP、Merge LOP、およびその他のLOPノードによって実行される合成の種類であり、一般にLOPノードは常に空のルートレイヤーの最も強力なサブレイヤーを編集します。さまざまなLOPノードが新しい強力なサブレイヤーを開始し、その後、すべての後続のLOPノードによって変更されます。
これらのさまざまなサブレイヤーを組み合わせて、LOPネットワークの最終的なUSDステージを生成します。

 

リファレンス(参照)

2つのシーングラフを組み合わせるもう1つの方法は、リファレンス(参照)と呼ばれる機能を使う方法です。
サブレイヤーとは異なり、リファレンスは参照ファイルのシーングラフを取得し、リファレンスシーングラフ内の場所に移植します。
これは、参照されるファイルのシーングラフの場所が、ファイルが参照されるパスのサフィックス(
接尾辞)として表示されることを意味します。

リファレンスに関するもう1つの重要な点は、ファイル内の単一のプリミティブを常に参照する必要があり、そのプリミティブをルート( “/”)にすることはできないということです。これは、他のレイヤーから参照されることを意図したレイヤーを作成する場合、単一のルートプリミティブを提供し、この1つのルートプリミティブの下にシーングラフ全体を配置することをお勧めします。この1つのルートプリミティブは、レイヤーの「デフォルトプリミティブ」として指定することもできます。参照元のプリミティブが、ファイル内のどのプリミティブを参照する必要があるかを示していない場合、デフォルトのプリミティブが参照されます。ただし、デフォルトのプリミティブを使用する代わりに、特定のプリミティブ(参照ファイルのシーングラフの任意の場所にある)を選択することもできます。

要約すると、リファレンスを使うときは、1つのファイルから1つのプリミティブを常にリファレンスシーングラフの既存のプリミティブの名前空間に参照します。前述しているサンプルファイルにおいて、foo.usdの/foo2/child1がbar.usd内の/bar2を参照する場合、結果のシーングラフは次のようになります。

/foo1 (from foo.usd)
/foo2 (from foo.usd)
  /child1 (from foo/usd and bar.usd /bar2)
    /child1 (from bar.usd /bar2/child1)
    /child2 (from bar.usd /bar2/child2)
  /child2 (from foo.usd)

 

参照するプリミティブの名前は保持されるため、参照されるプリミティブ名(bar2)はシーングラフに表示されないことに注意してください。ただし、/bar2のアトリビュートは/foo2/child1のアトリビュートとして表示されます。

リファレンスは、小さな個々のアセットを大きなシーンに結合する優れた方法です。特に重要なのは、同じレイヤーファイルを異なる場所の単一のシーングラフに複数回ロードする唯一の方法であることす。サブレイヤーの場合、同じファイルを複数回サブレイヤー化することはできませんが、ファイルはシーングラフ上の様々な場所で何度も参照される場合があります。そのような場合はリファレンスが使われます。

Reference LOPとStage Manager LOPはリファレンスを作成します。 Graft LOPはリファレンスと似ていますが、リファレンスを作成するのではなく、実際には、リファレンスシーングラフデータをメインシーングラフにコピーします。これは「強化された」リファレンスと考えることができます。また、ディスク上のファイルを参照できず、 LOPで作成された他のシーングラフのみを移植できるという点でもリファレンスとは異なります。

ペイロード

USDファイルを既存のシーングラフに取り込む最後の方法は、ペイロードと呼ばれるメカニズムを使用する方法です。
ペイロードはリファレンスとほぼ同じものですが、唯一の違いとして、ペイロードは、特別に要求されるまでペイロードをロードしないようにシーングラフに指示ができることです。 ファイルをペイロードと組み合わせると、シーンのどの部分をメモリにロードするかを簡単に制御でき、作業に必要なシーンの部分のみをロードして集中させることで、メモリの使用と処理時間を抑えることができます。

デフォルトでは、Houdiniはすべてのペイロードをロードします(したがって、リファレンスとまったく同じように動作します)。 ただし、シーングラフツリーのConfigure Stage LOPおよびLoad Masks controlsを使用すると、ペイロードのデフォルトのロードを無効にしてから、ロードしたいペイロードを選択できます。

 

サブレイヤーとリファレンスの組み合わせ

単独でのサブレイヤー化とリファレンスはそれほど複雑ではありませんが、USDは、file1のサブレイヤーがfile2を参照でき、file2がfile3をサブレイヤーし、file4を参照する、などリファレンスとサブレイヤーを複雑に組み合わせることが可能です。

単一のプリミティブリファレンスまたはペイロードを複数のファイルから単一のシーングラフの場所に配置することもできます。その場合、これらの参照およびペイロードは、最も弱いオーダーから順に解決されます。 これらのさまざまなレイヤーとリファレンスが連携して単一のシーングラフの場所でプリミティブにデータを生成していると、シーンの追跡が困難になる可能性があります。このような場合、[Scene Graph Details]ペインの[Layer Stack]タブ及び、[Composition]タブを使用して、プリミティブに寄与するレイヤーと、この最終結果を得るためにこれらの異なるレイヤーがどのように構成されたかを調べることができます。

 

LOPノードのクック

個々のLOPノードの動作を理解するには、一般的にLOPノードがUSDステージを作成する方法や、ステージ、レイヤー、サブレイヤー、リファレンスなどのUSDのコアの概念を理解していることが前提として必要不可欠です。

LOPノードの出力は、完全に構成されたUSDステージとして常にアクセス可能です。 このステージのルートレイヤーは、サブレイヤーのリストを除き、常に空です。 これらのサブレイヤーは、LOPノードによって作成される場合があります。その場合、それらはメモリ内にのみ存在する匿名レイヤーになるか、ディスク上のレイヤーになる場合があります。 LOPノードでは、ディスクからロードされたレイヤーを直接変更することはできません。 そのため、LOPで作業する場合、USDエディットターゲットの概念は適用されません。 ただし、レイヤーの読み込みと編集、レイヤーの置換、ターゲットレイヤーの編集などの特殊なノードを使用して、同様の結果を得ることができます。

一部のLOPノード(上記のようなもの)は、このサブレイヤーのリストを変更します。 たとえば、Sublayer LOPは、ディスク上のレイヤーのパスを取得して、サブレイヤーのリストに追加できます。 ただし、ほとんどのLOPノードは、レイヤーに変更を加えることでUSDプリミティブを変更します。 既に述べたように、LOPノードはディスクからロードされたレイヤーを変更しません。 代わりに、ステージのルートレイヤー上のサブレイヤーのリストで最も強力な匿名レイヤーを常に編集します。 ルートレイヤーは空であるため、これはLOPノードがステージ上で最も強い意見を持つレイヤーを常に編集していることを意味します。 これにより、各LOPノードによって適用された編集は、ステージ内の他の強力な意見によって上書きされないため、常に表示されます。 これにより、LOPノードをチェーンに追加するときの動作が単純で理解しやすくなります。

最も強いサブレイヤーがディスク上のファイルである場合、プリミティブを編集するLOPノードは自動的に新しい匿名レイヤーを作成し、サブレイヤーのリストの先頭に追加して、それを新しい最も強いレイヤーにします。 次に、そのレイヤーに変更を適用します。 最も強力なレイヤーが匿名レイヤーであっても、一部のLOPノード(Configure Layer LOPなど)には、新しい匿名レイヤーを作成してリストの先頭に追加するオプションがあります。 デバッグフラグがオンになっているLOPノードは、常に新しい最強レイヤーを作成します。 ただし、プリミティブ(Transform LOP、Cube LOP、Duplicate LOP、および他のほとんどのLOPなど)を編集するLOPノードのチェーンの通常の操作モードでは、各LOPノードが現在の最強レイヤー(既にある 匿名レイヤー)、既存の最強レイヤーに変更を加えます。 このレイヤーは、アクティブレイヤーとも呼ばれます。

もちろん、多少異なる動作をするいくつかの例外的なノードがあります。たとえば、Stage Manager LOPは、現在ルートレイヤーにあるすべてのサブレイヤー(匿名またはディスク上のファイル)を単一の新しい匿名アクティブレイヤーにフラット化してから、そのレイヤー(現在の唯一のレイヤー)を編集できます。(ルートレイヤー上のサブレイヤーのリスト) [Connected Inputs]モードのサブレイヤーおよびリファレンスノードは、入力ノードのステージでレイヤーの再配置を実行して、ロックステージと呼ばれるものを作成します。 USDファイルをディスクに保存する際の構造がよりクリーンになりました。

Cache LOPを除き、LOPノードは常に一度に1つのフレームのみをクックします。つまり、アニメーション化されたLOPノードを使用している場合でも、LOPノードによって作成された匿名レイヤーには、の単一のタイムサンプルデータ(1フレーム分)しかありません。プレイバーでフレームからフレームに移動すると、LOPノードがリクックし、レイヤーにはまた別のタイムサンプルが保持されます。常に現在のフレーム番号のタイムサンプルが保持されるようになっております。

USD ROPは、LOPネットワークによって生成されたUSDファイルをディスクに書き込む一般的な方法です。保存操作は、レイヤー構造と各レイヤーのデータに対して多数の変換を実行します。これらの変換については、別のセクションで紹介します。

 

パフォーマンスに関する配慮

LOPネットワークのパフォーマンスを最適化するには、チェーン内のLOPノードがUSDステージを共有する可能性があることを知っておくと役立ちます。 つまり、特定のLOPノードの合成ステージを要求する場合、同じチェーン内の別のLOPノードの合成ステージを同時に要求するには、共有ステージの複製を作成する必要があります。 LOPノードがこのようにステージを共有する理由は、LOPノードのチェーンのメモリフットプリントを削減するためです。入力からUSDステージの完全なコピーを所有する各LOPノードの代わりに、アクティブレイヤーのコピーのみを所有しており、チェーン内の1つのノードの構成済みステージから別のノードに移動するには、アクティブレイヤーのコンテンツをスワップアウトするだけです。これはUSDステージで非常に効率的な操作です。

これに加えて、LOPビューポートで構成および表示されるUSDステージは、常に、ディスプレイフラグが設定されたLOPノードの構成ステージのコピーであることを知っておくと役立ちます。ビューポートに表示されるLOPのメモリフットプリントを増加させることには、いくつかの利点があります。 

1つ目は、USDレンダリングフレームワークは、複数の小さな更新を連続して行うよりも、構成されたステージに対して1つの大きな更新を実行する方がはるかに高速になることです。チェーン内のLOPノードはステージを共有するため、たとえば、再生中にこのノードチェーンをクックするには、チェーン内の各LOPノードがクックされ、共有ステージに変更を加える際に、各ラウンドの変更処理が必要になります。

2つ目は、ビューポートとシーングラフツリーが同じLOPノードチェーン内の異なるノードに向けられている場合、シーングラフツリーを表示すると共有ステージが変更され、ビューポートが更新されてシーングラフツリーの内容に一致することです。

3つ目は、ビューポートに独自のUSDステージのコピーを与えることにより、ビューポートの多くのステージ処理タスクをバックグラウンドスレッドで実行できるため、インタラクティブパフォーマンスが大幅に向上します。

4つ目は、USDはステージとレイヤー間は非常に効率的にメモリを共有するため、ビューポートで使用される追加のメモリはシーングラフ階層の複製のみで、ステージのプリミティブに付加されたデータ(ポイント、アトリビュートなど)が複製されてメモリを新たに消費することはない点です。

このステージの複製は、USDステージ変更処理フレームワークを使用してビューポート内のデータにスペアの更新を効率的に適用するビューアーペインにのみ適用されます。 [Scene Graph Tree]や[Scene Graph Details]などの他のペインは、LOPノードのステージが変更されたときにコンテンツを効率的に完全に更新できるため、LOPノードが所有するステージで直接動作します。

 

USD Output ROP

LOPネットワークの出力をディスク上のレイヤーファイルに保存するには、通常、USD Output ROPを使用します(Pythonコードを使用して個々のレイヤーを保存することもできます。また、Inspect StageおよびInspect Active Layerウィンドウでは、USDデータをディスクに書き込むこともできます)。 この保存操作は、ステージのレイヤースタックと各レイヤーに含まれるデータに対して多くの変換を実行する可能性があります。 これらの変換の結果は、LOPノードによって生成されるネイティブステージと見分けがつかないはずです。

USD Output ROPはSave Styleパラメータによってディスク上にレイヤーファイルを生成できるいくつかのモードを切り替えられます。 最も重要な区分は、[Flatten Stage]オプションとそれ以外のオプションという区分です。 [Flatten Stage]オプションは、外部コンポジションアークをなくし、出力ステージ全体を単一レイヤーに平坦化します。 すべてのバリアントの選択肢が強化され、他の利用可能なバリアントは残りません。 リファレンスファイルまたはペイロードファイルは、単一のフラット化されたUSDファイルに統合されます。

[For the other Save Style]の選択では、サブレイヤーを除くすべてのコンポジションアークが保持されます。したがって、最初に発生する変換は、ディスクに書き込まれるステージのルートレイヤー上のサブレイヤーがサブグループに分割されることです。たとえば、[Flatten Implicit Layers]モードでは、各パーティションに1つの明示的レイヤーと、0個以上のより強い暗黙的レイヤーが含まれます。 [Flatten All Layers]モードでは、すべてのレイヤーが単一のパーティションに配置されます。[Separate Layers]モードでは、各レイヤーは個別に保存されます。次に、各パーティションが単一のレイヤーにフラット化されます。

これらの平坦化されたレイヤーが(メモリ内の匿名レイヤーとしても)作成されると、レイヤーは外部ファイルへの参照についてスキャンします。参照される外部ファイルが匿名レイヤーの場合、外部リファレンスのパーティション化、フラット化、およびスキャンは、参照される各レイヤーで再帰的に実行されます。これにより、コンポジションアークが正確に保持され、HoudiniがUSDシーンをusdviewなどの外部アプリケーションにロードするために必要なすべての情報をディスクに書き込むことが保証されます。

参照される外部ファイルがSOPネットワーク内からボリュームプリミティブを指す場合、ボリュームプリミティブも一意の名前でディスクに保存され、そのボリュームへの参照はそれに応じて更新されます。

ある範囲のフレームをディスクに保存する場合、ROPはフレームごとに、「ディスクに保存する準備ができているが、メモリにしか存在しない」一連のレイヤーを生成します。次に、各レイヤーでUSDスティッチ操作を使用して、この新しいフレームのデータを、以前にクックされたすべてのフレームを含むインメモリーレイヤーと結合します。LOPネットワークが大量のデータを生成している場合、スティッチ操作によってフレーム間で変化しないデータが複製されなくても、すぐに非常に大きなメモリフットプリントが発生する可能性があります。 USDファイルを保存するためのインメモリアプローチの結果、メモリが過剰に消費される場合は、代わりにフレームごとに個別のレイヤーファイルを保存することができ、USD StitchまたはUSD Stitch Clips ROPを使用して、事後のさまざまな単一フレームレイヤーを、すべてのタイムサンプルを含むレイヤーまたは一連の値クリップに結合します。

ディスクから読み取られるレイヤーは常に保存プロセスによって変更されませんが、LOPノードによって生成された匿名レイヤーはディスク上の既存のレイヤーファイルを上書きする場合があります。