Houdini 18.5 Solaris

USDの基本

いくつかUSDの概念について取り上げ、それらの概念とHoudini USD対応との関係性について説明します。

On this page

素晴らしい Solarisジャンプスタート チュートリアルを作成してくれたChris Rydalch氏とBlue Sky氏に感謝します。

概要

USDは、 レイヤーを合成 することで3Dシーンを表現できるようにしたソフトウェアおよびファイル形式のシステムです。 例えば、台所シーンを表現したマスターUSDファイル(kitchen.usd)は、プロップ(chair.usd, table.usd)、照明、キャラクタなどを含んだレイヤーファイルを参照して、それら全体を単一 ステージ として合成することができます。

このレイヤーという概念の主力は、 非破壊的編集 ができることです。 既存シーンを開いて、新しいレイヤー上で編集を加えることができます。 ここで加えた編集は、同じシーンを利用している他の人がその新しいレイヤーを取り込まない限りは、その人には何も影響を与えません。 複数のレイヤー(例えば、新しいバージョンのアセットを含んだレイヤーや更新されたライティングを含んだレイヤー)のどれかを置換することで、その置換したレイヤーの上にある既存のすべての変更を自動的に再適用させることができます。 これによって、複数の部署間でお互いに干渉することなく、共同作業、データ共有、アセット更新をすることができます。

HoudiniでのUSD関連のツールとサポートのことを総称してSolarisと呼びます。 Solarisは、ビューポートのUSD対応、新しいネットワークタイプであるLOPs(Light Operators)を含んでいます。 LOPネットワークは、若干SOPsに似ています。 SOPsでは、各ノードが入力ジオメトリを受け取って、それを修正し、新しいジオメトリを出力します。 それに対して、LOPsでは、各ノードが入力USDシーンを受け取って、それを修正し、新しいシーンを出力します。

USDは非常に広範囲なフレームワークなので、そう単純ではありません。 LOPsは、ベースとなっているUSD構造について知る 必要がない ように設計されています。 しかし、あちらこちらでLOPパラメータでUSD用語が使用されており、中にはUSDに組み込まれている機能を簡単にラッパーしているものもあります。 USDの概念と機能の基本を理解すれば、LOPsの理解がもっと楽になることでしょう。

以下のページは、USDの基本について説明し、そこからSolarisがHoudini内でUSDの処理をどのように対応させているのかについて触れています。 LOPノードがUSDをどのようにして生成しているのかに関しては、LOPの挙動を参照してください。

クィックサンプル

普段だと、.usd(または.usdc)ファイルは効率性を良くするためにバイナリエンコードしますが、USDシーン記述フォーマットは、人が解読可能なプレーンテキスト(.usda)にも対応しています。 このドキュメント内では、以下の非常に単純な.usdaサンプルを使って、一部のUSDの概念について説明します:

box.usda

#usda 1.0
def Cube "box" {
    double size = 4.0
}

このサンプル内のdefは、新しいPrimを定義するためのキーワードです(USDには他にもdefと同様のoverオペレータが存在しますが、overは新しいPrimが既存Primを オーバーライド(上書き) する方法を定義します)。 Cubeは、そのPrimの タイプ(型) です。 "Cubeである"が意味する事とそのCubeが用意しているプロパティは、 スキーマ で定義します。 boxは、そのPrimの名前です(このPrimは、シーンツリー内で/boxとして表示されます)。 このCube Primタイプは、ビルトインのUSD Geometryスキーマで定義されています。 他にもボリューム用、シェーディング用、ライティング用、スケルタルアニメーション用、レンダリング用のビルトインスキーマが存在します。 カスタムスキーマを作成して独自のPrimタイプを定義することで、USDを拡張することができます。

中括弧{ }内のコードでは、新しいPrimの 子Primプロパティ を定義します(このCubeにはたくさんのプロパティが用意されていますが、ここで指定しなかったプロパティは、そのタイプスキーマのデフォルト値が適用されます)。

USD用語で言えば、これは"/boxのサイズに関するオピニオンを編集します"と命令した短いコードです。 レイヤーを合成する時に、あるレイヤー内にプロパティ値に関する"オピニオン"が含まれている一方で、 強いオピニオンのレイヤーがそれと同じプロパティに対して別のオピニオン(値)を持っていた場合、弱いオピニオンのプロパティ値は強いオピニオンのプロパティ値で オーバーライド されます。

では、2つ目の.usdaファイルを見ていきましょう:

two_boxes.usda

#usda 1.0
def "box1" ( references = @box.usda@ ) {

}
def "box2" ( references = @box.usda@ ) {
    float size = 2.0
}

このファイルでは、上記のbox.usdaファイルの内容を2回インポートして、/box1/box2の2個のPrimを定義しています。 /box1は、参照レイヤーからsizeアトリビュートを継承します。 /box2は、新しい値でsizeオーバーライド します。

Houdiniでは、一般的にはプリミティブ上のアトリビュート値を直接的に操作することはしないでしょう(たしかにローレベル編集をするためのノードは存在しますが)。 直接的な操作をしなくとも、LOPノードにもっとハイレベルなオペレーション(例えば、"このジオメトリを取り込め"、"このライトがこのオブジェクトを照らせ"、"このプロップをこの机の上に物理学に基づいて配置せよ"といったオペレーション)を指定すれば、そのLOPが自動的に該当するUSDを編集してくれます。

実際にInline USD LOPを使用することで、LOPネットワークで構築されたステージ内にプレーンテキストのUSDコードを挿入することができます。この方法は、時にはデバッグで役に立つものの、普段あなたが作業するべきやり方ではありません!

Tip

拡張子が.usdのファイルは、バイナリ形式でもプレーンテキスト形式でも どちらにも なることができます。 これによって、アセットの監視やバージョン管理を複雑化せずに、ファイルをある表現から別の表現に簡単に切り替えることができます。 拡張子が.usdaのファイルは常にプレーンテキスト形式で、.usdcのファイルは常にバイナリ形式です。

概念

プリミティブ(Prim)

プリミティブはUSDの基本"単位"です。USDの話になった時は、この"プリミティブ"のことを略して"Prim"と呼ぶことが多いです。 HTMLの"エレメント"と同様に、Primは名前を持ち、名前の付いた0個以上の プロパティ と0個以上の 子Prim を持ちます。

例えば、ポリゴンメッシュはPrim、ライトもPrim、マテリアルもPrimです。"xform" Primには、その子Primsに適用されるトランスフォームが格納されています。

Primsは、 ステージ を定義したオブジェクトツリー内の"ノード"に該当します。

プロパティ、アトリビュート、リレーションシップ

"プロパティ"とは、Primに追加可能な以下の2つのタイプの名前の付いたデータの総称です:

アトリビュート

アトリビュートとは、Primを記述した(整数、カラー、トランスフォーム行列、配列などの)型付き値のことです(例えば、float size = 1.0はCube Primの均一サイズを定義します)。

これが最も一般的なプロパティの形式です。

リレーションシップ

リレーションシップは、Primと他のPrim間の関係性を確立します(例えば、メッシュPrimをシェーディングする際に使用するマテリアルを指定したり、コレクション内のPrimを指定します)。

メタデータ

プロパティ自体に名前の付いた値を追加することができます。この値によって、プロパティの挙動を変更することができます。 例えば、メッシュに対してポイントカラーPrimvarが指定されている場合、interpolationメタデータを追加することで、レンダラーがそれらのポイント上のカラーをブレンドする方法を指定することができます。

他にも、メタデータを使って特定のDCCに固有な"特別な"データを追加したり、デバッグやユーザーインターフェースの構築にも役に立ちます。 例えば、アトリビュート毎のドキュメントをメタデータに格納することができます。

レイヤー

.usdファイルは1ファイルにつき1レイヤーです(その一方で、.usdzファイルは複数のレイヤーを単一アーカイブファイルに格納することができます)。 レイヤーは、プロップ、キャラクタ、リグの一部、ライティングセットアップなどのシーンの"構成要素"を表現することができます。 他にも、レイヤーは基準値として参照可能な"デフォルト"を表現することもできます。 例えば、ショットシーケンス用の設定を含んだレイヤーを作成しておけば、そのファイルを参照してシーケンス内の各ショットを表現することができます。

詳細は、レイヤーとステージを整理する方法を参照してください。

Houdiniでは、色々なノードが新しいIn-Memoryレイヤーを作成して、既存データをオーバーライドします。 他にも、Layer Break LOPを使って"手動で"新しいレイヤーを作成することもできます。 通常のLOPネットワークは、たくさんのレイヤーを伴い、一部のレイヤーはディスクからインポートし、他のレイヤーはノードによってメモリ内に生成され、 最終的にそれらのレイヤーが合成されます(ネットワークエディターのオプションの可視化機能によって、ネットワーク内のどのノードがどのレイヤーで動作しているのか表示することができます)。

コンポジションアーク

これは、レイヤーが他のレイヤーを参照することを意味します。 例えば、シーンレイヤーは、プロップ、ライティングなどを含んだレイヤー群を取り込むことができます。 取り込んだそれらのレイヤーもまたサブレイヤーを参照することができ、さらにそれらのサブレイヤーもサブサブレイヤーを取り込むことができます。

レイヤーは合成されて、各レイヤー内のPrimsを結合します。より上層にあるレイヤーのプロパティが下層のレイヤーのプロパティの値を オーバーライド します。

例えば、シーンファイルは、ライティングレイヤーを参照することができますが、ライティング部署が"ベースデフォルト"、"完了したライティング"、"作業中のライティング"を表現した3枚のレイヤーからそのライティングレイヤーを合成していたとします。 他の部署は、そのライティングレイヤーがどのように合成されているのか知る必要はなくて、ただ単にそのトップレベルの"ライティング"レイヤーだけを参照すれば良いだけです。

Reference LOPを使用することで、LOPネットワーク内のノードで構築されているIn-Memoryレイヤーに参照を追加することができます。 Sublayer LOPを使用することで、既存のサブレイヤー(.usdファイル)を現行ステージのレイヤースタックに読み込むことができます。

ステージ

USDファイルを読み込んで、システム側ですべてのレイヤーを一つに合成してPrimsの最終シーングラフを計算したその結果のことを、 ステージ と呼びます。

ステージ は、あくまでその 結果 の名前にすぎません。"ステージファイル"と"レイヤーファイル"に違いはないのです… .usdファイルを"トップレベル"ファイルとして読み込んだら、システム側でステージが生成されます。他の環境では、その同じファイルを別のステージ内のレイヤーとして参照しても構いません。

Houdiniでは、ネットワークのトップにあるLOPノードがステージを生成し、それより下流のLOPノードは、そのステージの内容をプロシージャルに編集して、新しいステージを生成します。 そのため、どのLOPノードに対してもhou.LopNode.stage()を使用すれば、そのノードから出力されたステージオブジェクトを取得することができます。 LOPノードを右クリックして LOP Actions ▸ Inspect Flattened Stage を選択することで、ノードから出力されたステージをusda構文で確認することができます。

アトリビュートとPrimvar

USDにおけるアトリビュートは、Primの"設定"を含んだスカラー値を指すことが多いです。 Sphere Primを例にすると、radiusアトリビュートは球のサイズを制御します。 この意味では、アトリビュートはHoudiniでいうノードパラメータ、Mayaでいうアトリビュートに近いです。

USDでは、ポイント単位の変数などのジオメトリ設定 Prim上のアトリビュートとして格納します。 このようなジオメトリ設定は、1コンポーネントにつき1エレメントの 配列 として格納します。 例えば、メッシュPrim上のpointsアトリビュートには、座標配列でポイントポジションを格納します。 これは、Houdiniのジオメトリアトリビュートと同じです。

他にも、USDは Primvar と呼ばれる特別なタイプのアトリビュートを認識します(このPrimvarはRendermanが語源で、"Primitive variable(プリミティブ変数)"の略です)。

基本ツリーレベルでは、このPrimvarは、単にprimvars:スキーマネームスペース接頭辞が付いたアトリビュートにすぎないです。 このPrimvarが特別なアトリビュートになるのは、Hydraがレンダリング時にそのアトリビュートを扱う時です。 Primvarの主な目的は、マテリアルパラメータをオーバーライドすることです。 とはいえども、このPrimvarの挙動は他にも活かせることができて、マテリアルとは別にジオメトリレンダリングに影響するオブジェクト単位のプロパティを表現する際にも使用します。 例えばKarmaでは、Primvarを使用して、オブジェクト単位でモーションブラーのフレーム数を指定することができます。

Primvarの補間

Primvarは、マテリアルパラメータのオーバーライドもできるという点では、通常のUSDアトリビュートと比べて、従来のHoudiniレンダリングにおけるHoudiniジオメトリアトリビュートに近いです。 Houdiniのジオメトリアトリビュートと同様に、カーブ/サーフェスの特定の部分に対して利用可能なPrimvarデータが存在しなかった場合、システムが最近接の利用可能なPrimvar間で値を補間します。

システムがPrimvarを補間する 方法 によって、そのPrimvarの"レベル"が決まります。 それはHoudiniがVertexアトリビュート、Pointアトリビュート、Primitiveアトリビュート、グローバル(Detail)アトリビュートを持つ方法と同様です。 USDでは、 Constant 補間がHoudiniのDetailアトリビュートに相当します。 Uniform 補間はHoudiniのPrimitiveアトリビュートに相当します。 FaceVarying 補間はHoudiniのVertexアトリビュートに相当します。 Vertex 補間は(紛らわしいですが)HoudiniのPointアトリビュートに相当します。 この補間方法は、Primvarアトリビュート上にメタデータとして格納します。

Primvarの配列値の数は、選択した補間方法に該当するコンポーネントの数と同じでなければなりません。 例えば、キューブ上のUniform(フェース単位)補間のPrimvarには6個の値が必要ですが、Vertex(ポイント単位)補間のPrimvarには8個の値が必要です。 指定した通りにマテリアルパラメータをオーバーライドできるようにPrimvar値を適用する方法は、レンダラーに依存します。

利用可能な値の間を補間するのがPrimvarの主な特徴です。 これこそが、メッシュPrim上のpointsアトリビュートが通常のアトリビュートであるのに対してprimvars:displayColorがPrimvarになっている理由です。 ポイントポジションは"補間"することができません。 その配列のサイズがメッシュ内のポイントの数を定義します。 その一方で、表示カラーはプリミティブ全体で単色にしたり、ポリゴン毎、ポイント毎、頂点毎に異なる値にすることができます。

Primvarの継承

通常のアトリビュートとは異なるPrimvarの他の特徴は、"Constant"補間のPrimvarは シーングラフツリーの下流に継承される ことです。

例えば、/Geometry/propsというPrimのprimvars:displayColor定数 値だった場合、/Geometry/propsより下流のすべての子Primは、オーバーライドしない限りは同じカラーを使用します。

この継承こそが、オブジェクト単位のレンダー設定でPrimvarが使用されている理由です。 ハイレベルのプリミティブ上に値を設定(例えば、Dicing品質を下げたり)することで、シーンの大部分にその影響を与えることができます。

Primvarとインスタンス化

ポイントインスタンサーに"Vertex"(ポイント単位の)配列Primvarが設定されていれば、各インスタンスはその配列内の該当する値を照会して、 その値をそのインスタンス上の"Constant" Primvarとして扱います。 これこそが、USDにおけるポイントインスタンス単位のマテリアルオーバーライドの挙動です。

同様に、インスタンス化可能なPrim上に"Constant" Primvarを設定することで、そのPrimvarが継承されて、そのインスタンス内のプリミティブのマテリアルパラメータに影響を与えることができます(これを設定しなかった場合は、それらのパラメータは編集不可です)。 これは、インスタンス化可能なPrimの外部からのデータがそのインスタンス内のプリミティブの外観に影響を与えることができる唯一の方法です。

コンポジション

どのUSDファイルも完全な"シーン"を含んでいます。 USDファイルは、他のUSDファイルの内容を レイヤー として取り込むことができます。

USDライブラリには、トップレベルのファイルで生成されたフルシーン(ステージ)内に複数のレイヤーをコンポジット/オーバーレイするためのソフトウェアが入っています。 ここで言うコンポジションは、以下のレイヤー間の違いを考慮した結合を含みます:

  • 新しいPrims。

  • Primの順番決め。

  • Primがアクティブなのか非アクティブなのか

  • バリアント

  • 新しいプロパティ(アトリビュートとリレーションシップ)。

  • アトリビュート値(上層のレイヤーほどアトリビュート値を"ブロック"(未設定)しやすいので、アトリビュート値が割り当てられてないかのように見えてしまいます)。

  • メタデータ値。

Houdiniは、色々なディスク上のレイヤーとUSDを編集すると生成されるIn-Memoryレイヤーを自動的に合成します。 他にも、ツールを使用することで、各レイヤーの内容とレイヤーの合成の方法を理解することができます。

サブレイヤー、リファレンス

USDでは、他のUSDファイルの内容を"インポート"するための方法が2つあります。 1つ目の方法が サブレイヤー です。これは、インポートしたファイルのツリーを現行ツリーの上にオーバーレイします。 2つ目の方法が リファレンス です。これは、インポートしたツリーの内容を既存ツリーから分岐させて配置します。

サブレイヤーは、 別バージョンのシーン全体を合成します (例えば、ライティング部署が最終調整したライティングシーンを、初期ライティングのレイアウトバージョンのシーンの上にオーバーレイします)。 リファレンスは、 シーンの一部を追加します (例えば、レイアウト内にプロップをインポートします)。

Sublayer(サブレイヤー)

サブレイヤーは、インポートしたファイルの内容を既存の内容の上にオーバーレイします。 各ツリーの同一パスのPrimsに関する"オピニオン"と(それらのPrims上の)同一名のプロパティ/メタデータは、オピニオン強度(以下参照)に基づいてマージされます。

これは、完全シーンの一部に寄与させることを目的とした各ファイルを結合する時に役立ちます。 例えば、セットレイアウト、プロップ、キャラクタ、エフェクト、ライトを表現したそれぞれのレイヤーを合成する時です。

サブレイヤー化の本来の特徴は、"すべてが同じ場所に存在する"ことです。 新しいファイルのPrimsのパスは、ソースファイルのPrimsと同じパスになります。

各部署がそれぞれ別のサブレイヤーで作業しているので、単一ファイルの共有や編集内容の干渉について心配する必要はありません。 しかし、すべての部署が同じシーングラフ階層で作業しているわけなので、各部署は、他の部署で作成された内容に変更を加えることができます。

Sublayer LOPは、新しいサブレイヤーをシーン内に読み込みます。 一般的には、LOPネットワークは、いつも一番強いサブレイヤーである空っぽのルートレイヤーを編集します。 一部のLOPノードは、新規で強いサブレイヤーを開始することができ、そのサブレイヤーがそのLOPノードより下流のすべてのノードによって編集されます。 これらのネットワーク出力は、このサブレイヤースタックの合成結果です。

サブレイヤーを指定したトップレベルのファイル

#usda 1.0
(
    subLayers = [
            @shotLighting.usd@,
            @shotFX.usd@,
            @shotAnimation.usd@,
            @shotSetDressing.usd@,
            @sequence.usd@
    ]
)

Reference(リファレンス)

リファレンスは、参照したファイル内のツリーを取り込み、そのツリーを現行ツリー内のブランチ上で"graft(接ぎ木)"させます。

例えば、以下のツリーから始めると仮定します:

参照する前のシーンツリー


/Lights/
    light1
    light2
/Models/
    tableside_lamp
        

/Models/tableside_lamplamp.usdを参照し、そのファイルが以下の内容だとします:

lamp.usdの内容


/lamp/
    base
    bulb
    shade
    socket
    switch

このファイルを参照した後のツリーは、以下のようになります。

参照した後のシーンツリー


/Lights/
    light1
    light2
/Models/
    tableside_lamp/
        base
        bulb
        shade
        socket
        switch
  • 参照(/Models/tableside_lamp)を含んだPrimと参照先のPrim(lamp.usd内の/lamp)は、(オピニオン強度で決定したプロパティ値でオーバーライドして)合成されますが、その合成されたPrimは、参照元のPrimの名前を維持します。

  • 内容を参照する時は、参照先のファイル内のPrim(上記の例だと/lamp)を指定しなければなりません。そして、そのPrimはルート(/)にすることができません。そのため、他のレイヤーから参照されることを想定してファイルを作成する時は、単一ルートプリミティブ下にその内容が収まるようにしてください。

    (ファイルのデフォルトプリミティブを指定することができます。ファイルを参照した際に、そのファイル内の特定のPrimを指定しなかった場合には、そのファイルの"デフォルトプリミティブ"が取得されます。)

リファレンスは、個々の小さなアセットを大きなシーンに"graft(接ぎ木)"させるのに役立ちます。 特に、 リファレンスは、同じファイルを何回も別々の場所に読み込むことができる唯一の方法です (サブレイヤー化はツリー全体に対して動作するので、同じファイルを何回もサブレイヤー化しても何の効果もありません)。

単純なゴミ箱アセット

#usda 1.0
(
   defaultPrim = "TrashCan"
)

def Xform "TrashCan" (
   kind = "component"
)
{
   def Cylinder "Can"
   {
       token axis = "Y"
       bool doubleSided = 0
       double height = 2
       double radius = 1
   }
}

セット内の3部屋でゴミ箱アセットを3回参照したファイル

#usda 1.0
()

def Xform "Scene"
{
   def Xform "Set"
   {
       def "BathroomTrashCan" (
           append references = @./trashcan.usda@
       )
       {
           double3 xformOp:translate = (2, 0, 1.4)
           uniform token[] xformOpOrder = ["xformOp:translate"]
       }

       def "KitchenTrashCan" (
           append references = @./trashcan.usda@
       )
       {
           double3 xformOp:translate = (16.01, 5, -43.072)
           uniform token[] xformOpOrder = ["xformOp:translate"]
       }

       def "OfficeTrashCan" (
           append references = @./trashcan.usda@
       )
       {
           double3 xformOp:translate = (-7.12, 0, 11.9)
           uniform token[] xformOpOrder = ["xformOp:translate"]
       }
   }
}

Reference LOPStage Manager LOPが参照を作成します。 Graft Stages LOPも同様ですが、これはファイルから参照するのではなくて、LOPネットワーク内のどこからかのシーングラフツリーにブランチを挿入します。

単一シーングラフ位置におけるプリミティブに対する最終オピニオンを生成するために複数レベルのサブレイヤー化と参照がどのように動作しているのかを追跡するのは困難を極めます。Scene Graph DetailsペインLayer StackComposition のタブを使用することで、どのレイヤーがどのプリミティブに寄与しているのか、それらの異なるレイヤーがどのように合成されているのかを調べることができます。

ペイロード

ペイロードは基本的にはリファレンスなのですが、明示的に要求しない限り、USDがペイロードを読み込まないようにすることができます。 これによって、シーンのどの部分をメモリ内に読み込むのか制御することができるので、気になるシーンの部分に注力することで、メモリ使用量と処理時間を抑えることができます。

Houdiniでは、ファイルを参照するノードのパラメータで、その参照するファイルを通常のリファレンスとして読み込むのか、ペイロードとして読み込むのかを指定します。

デフォルトでは、Houdiniは、すべてペイロードとして読み込みます(この挙動はリファレンスとまったく同じです)。 しかし、Configure Stage LOPとScene Graph Treeペイン内のLoad Masksコントロールでは、このデフォルトですべてのペイロードを読み込む挙動を無効にして、どれをペイロードとして読み込むのかを指定することができます。 詳細は、ペイロードの扱い方を参照してください。

アクティブ化と可視性

非破壊的編集ができるように設計されているので、USDではプリミティブを 削除する ことを許可していません。 代わりに、新しい値でプリミティブを オーバーライド したり、プリミティブを 非アクティブ化 することで、それらのプリミティブの効果をなくすことができます。

さらには、シーン内のオブジェクトの 可視性 を編集することもできます。 デフォルトでは、USD Primsは可視です。 可視性オプションには"inherit(継承)"と"invisible(不可視)"の2つの設定しかありません。 Primを"invisible"としてマークすることで、常にその子Primすべても不可視になります。

Houdiniでは、Scene Graph Treeペイン内でプリミティブのアクティブ/非アクティブ、可視/不可視をインタラクティブに切り替えて、そのプリミティブの効果を確認することができます。 Houdiniは、 未保存 のIn-Memoryレイヤー内でこれらの編集を実行するので、その編集が最終的に生成されるUSDには影響を与えません。

オピニオン強度

アトリビュートは、"強者が勝つ"ルールに基づいて解決されるので、指定されたアトリビュートのすべての値は、一番強いPrimSpecから取得されます。 例えば、デフォルト値の"弱いレイヤー"は、アニメーション付きの強いレイヤーでマスクされます。 例えば、レイアウト部署がシーン内にプロップを配置し、後でアニメーション部署がそのプロップをアニメーションさせたレイヤーを追加します。

オピニオン強度を決定する最も一般的な方法は、レイヤースタックの順番決めです。

例えば、一番弱いレイヤーをレイアウトレイヤーにして、レイアウト部署がアセットを追加して配置します。 次のレイヤーでは、アニメーション部署がそのシーンにキャラクタを追加します(必要に応じて、これらの既存のプリミティブのどれかに強いオピニオンを適用することで、レイアウトを移動させたり調整することができます)。 次のレイヤーでは、FX部署がシーングラフに新しくジオメトリを追加したり、レイアウト部署またはアニメーション部署で配置された既存のプリミティブのどれかにRBDシミュレーションを適用することができます。

オピニオンの順番決めは、LIVRPS("LIVeR PeaS"と読めば覚えやすいでしょう)に準拠します。 LIVRPSは、合成時に 強い順で あるレイヤーが他のレイヤーをオーバーライドすることができる方法です:

Local

プロパティに対して各自のオピニオンを持ったレイヤーは、そのレイヤーが参照している任意のサブレイヤー内の同じプロパティのオピニオンをオーバーライドします。

Localオピニオンが存在しなかった場合は、USDは次を検索します:

Inherits

"Inherits"を使用することで、レイヤーのコンテキスト内でのみPrimのデフォルト値を再定義することができます。 "Inherits"は"Local"と同様ですが、複数レベルの参照を介しても"そのまま"の状態になります。 マテリアルは"Inherits"と"Specializes"を主によく使用します。 以下のInherits(継承)とSpecializes(特別化)を参照してください。

詳細は、PixarのUSD用語集のInherits(継承)を参照してください。

Inheritオピニオンが存在しなかった場合は、USDは次のオピニオンを検索します:

Variants

以下のバリアントを参照してください。現行バリアントは、バリアントセットを含んだPrim上のアトリビュートをオーバーライドします。

Variantオピニオンが存在しなかった場合は、USDは次を検索します:

References

リファレンスは、小さいユニットをシーン内に"インポート"します。 例えば、ドレスセットファイルは本棚プロップファイルを参照し、その本棚プロップファイルは何冊かの色々な本プロップファイルを参照することができます。

詳細は、PixarのUSD用語集のReferences(リファレンス)を参照してください。

Referenceが存在しなかった場合は、USDは次を検索します:

Payloads

まったくReferencesと同様ですが、任意でペイロードをアンロードしてシーンのビューイング/レンダリングを高速化し、後でそのペイロードを再読み込みさせることができます。 (高解像度ポリゴンメッシュスキンなどの)アセットの重い部分をペイロードとして参照すれば、その部分をアンロードすることができます。

ペイロードが読み込まれると、"アンロードされた"バージョンがオーバーライドされます。

詳細は、PixarのUSD用語集のPayload(ペイロード)を参照してください。

Payloadが存在しなかった場合は、USDは次を検索します:

Specializes

Inherits(上記参照)と同様で、オリジナルソースを変更することなく、レイヤー内のPrimのデフォルト値を再定義します。 Specializesは、"Inheris"オピニオンをオーバーライドせずに、これらの変更を伝搬させる方法です。 マテリアルは"Inherits"と"Specializes"を主によく使用します。 以下のInherits(継承)とSpecializes(特別化)を参照してください。

詳細は、PixarのUSD用語集のSpecializes(特別化)を参照してください。

Houdiniでは、 常に一番強いオピニオンで編集 するので、普段はオーバーライドの強度について意識する必要はありません。

詳細は、PixarのUSD用語集のLIVRPS順番決めを参照してください。

"Specializes(特別化)"と"Inherits(継承)"を理解する

Speicalizes(特別化)Inherits(継承) のコンポジションアークの挙動について説明するために、以下のUSDレイヤーファイルを想像してください:

props.usdの内容

# props.usd
/Materials/
    plastic1
/Things/
    Toy/
        geom
        toy_shader (/Materials/plastic1をリファレンスしてオーバーライド)
  • Toyプロップには自身のシェーダが付けられています。

  • toy_shaderシェーダは/Materials/plastic1をリファレンスして、それでオーバーライドされています。

では、上記のUSDレイヤーからToy Primを以下のUSDレイヤーファイルにリファレンスすることを想像してください:

scene.usdの内容

# scene.usd
/Materials/
    plastic1
/Models/
    Props/
        Toy/  <– このPrim上にリファレンス
            geom
            toy_shader
  • このscene.usdレイヤーにはネームスペース内に 自身の /Materials/plastic1 Primを持っていることに注目してください。

  • Toy/toy_shader内のリファレンスは、どっちの/Materials/plastic1 Primからデータを取得するでしょうか?

    その答えは、props.usd/Materials/plastic1からデータを取得します。 その理由は、シェーダ上のリファレンスが解決済みであり、scene.usdの方のPrimよりも Toy/toy_shader上で合成されたデータがscene.usdにリファレンスされているからです。

これがReferenceコンポジションアークの挙動だと考えると、おそらく何かしらの方法でscene.usd/Materials/plastic1Prim toy_shaderに影響を与えることができると便利ではないかと思うでしょう。 これによって、参照ファイルが入力データをカスタマイズすることができるはずです。

これを行なう方法は、単にReferenceコンポジションアークを使うのではなくて、"Inherits(継承)"または"Specializes(特別化)"というコンポジションアークを使用することです。

  • Primが他のPrimを"Inherits(継承)"または"Specializes(特別化)"すると、そのPrim自身のレイヤー内で(まさにReferenceと同じように)参照を解決しますが、レイヤーがレイヤー内で参照されると、USDは、指定されたプリミティブパスにあるPrimsに対して 追加チェック を行なってそれらが存在すれば、そのPrim上にそれらを合成します。

  • 上記の例だと、toy_shader/Materals/plastic1に関連した"Inherits"または"Specializes"リレーションシップがあれば、props.usd/Materials/plastic1 scene.usd/Materials/plastic1両方 からデータが取得されるようになります。

  • "Inherits(継承)"と"Specializes(特別化)"の違いは、"Inherits"の場合だと、"より高位の"レイヤーのオピニオンは、継承する側のPrim自身のレイヤーのオピニオンよりも 強い です。"Specializes"の場合だと、より高位のレイヤーのオピニオンは、特別化する側のPrim自身のレイヤーのオピニオンよりも 弱い です。

デフォルトプリミティブ

メタデータの一部として、各レイヤーには"デフォルトプリミティブ"を指定することができます。 これは、ファイルを参照した際に、そのファイルから追加したいルートレベルPrimを明示的に指定しなかった場合に参照されるプリミティブのことです。

  • Configure Layer LOPを使用することで、(他のレイヤーレベルメタデータと併せて)レイヤーのデフォルトプリミティブを指定することができます。

  • USDファイルを書き出す時、USDレンダーノードには、その"トップレベル"レイヤーファイルのデフォルトプリミティブを設定するためのパラメータがあります。

  • USDレンダーノードには、書き出されるすべてのレイヤーに対してデフォルトプリミティブが指定されていなかった場合にエラーを引き起こすためのオプション( Error Saving Layer With No Default Primitive )があります。

HoudiniでローレベルのUSDアクセス

  • usda形式のコードを書き出し、それを評価し、その結果のPrimsをInline USDノードを使ってIn-Memoryシーングラフツリー内に直接挿入することができます。

  • Python Script LOPを使ってPython Usd APIを利用することで、In-Memoryステージを直接制御することができます。

拡張性

ファイルフォーマットプラグイン

USDオープンソースライブラリには、異なるファイルフォーマットからデータを読み込むためのプラグインが備わっています。

Houdiniでは、一般的にはUSDのファイルインポートコードを使わずに、LOPノードを使って、他のネットワークまたはディスクから取り込んだUSDにジオメトリなどのデータを直接インポートします。 さらに、HoudiniにはHoudiniファイルフォーマット用USDプラグインも含まれているので、Houdini外のパイプライン内でこのプラグインを利用することができます(このプラグインはバッチライセンスを消費します)。

Asset Resolvers

明示的にファイルパスを指定する方法以外にも、USDは、暗号めいた アセットID (usdaフォーマットでは、このアセットIDは@my_asset_name@のようになっています)を使ってファイル参照をエンコードすることができます。 Asset Resolver と呼ばれるプラグインは、このアセットIDを、読み込み可能なオブジェクトに変換する役割を担っています。

例えば、スタジオがアセットを中央Gitサーバーで保管すると仮定すると、そのサーバーに関連した意味のあるパスと改訂情報を含んだアセットID(例えば、@/models/tintoy#bf452ac@)としてファイル参照を保管することができます。

Houdiniから生成されるUSDに関しては、出力プロセッサは、ファイル場所とファイルパス文字列にのみ何かしらのカスタマイズをすることができます。

カスタムスキーマ

以下のスキーマを参照してください。 スキーマは、独自のタイプ、アトリビュート、APIをUSDに追加して、その挙動を定義します。

レンダーデリゲート

以下のHydraとレンダリングを参照してください。 ソフトウェアは、 レンダーデリゲート を管理する(Hydraと呼ばれる)APIを使って、USDをレンダリングします。 レンダラーにレンダーデリゲートが存在していれば、どのソフトウェアでも、そのレンダラーを介してUSDをレンダリングすることができます。

シーンデリゲート

プログラムがレンダーデリゲートを介してレンダリングを管理するのと同様に、レンダラーでシーンからの情報が必要になった時には、レンダラーはAPIを使って、 シーンデリゲート からその情報を要求します。 これによって、間接的なレベルで、レンダリング時にシーンを変更することができます。

Prim Adapters

各タイプのUSDプリミティブは、Prim Adapterタイプによって、Hydra互換のレンダリング可能表現に変換されます。

以下の2通りの方法で、HoudiniでカスタムファイルフォーマットプラグインとAsset Resolverを使用することができます:

  • Houdiniに同梱されているUSDのヘッダとライブラリを使ってプラグインをビルドします(これらのヘッダとライブラリは、HDKの一部として含まれています)。

  • HoudiniバージョンのUSDライブラリをあなた自身のバージョンに置換します。これをするには、SideFX GitHubからHoudiniUsdBridgeリポジトリをダウンロードする必要があります。このリポジトリには、USDでコールするHoudiniソースコード一式が含まれています。あなた自身のUSDライブラリを使って、このコードをリビルドして、その結果を使って、Houdiniインストール先の該当するライブラリを置換します(まるまるコピーするかLD_LIBRARY_PATHを編集します)。

Solarisの出力プロセッサは、ファイルの保存方法をカスタマイズすることができます(例えば、$HIPを基準としたパスを受け取り、そのパスをルートUSDファイルのパス基準に変換し、ファイル拡張子を調整することができます)。

USDのジオメトリ

USDでは、ジオメトリを表現したPrimのことを Gprim と呼びます(これは、レンダリング時に実際に画像内で描画されるPrimであり、その対義語としてトランスフォームPrimがあります)。このようなPrimタイプ(UsdGeomGprim)のスキーマには、境界ボックス検索などの便利メソッドが用意されています。

USDは、数学的に定義された形状(カプセル、円錐、立方体、円柱)を表現したPrim一式を含んでいて、さらには、ポリゴンメッシュやサブディビジョンサーフェスなどのジオメトリを格納するもっと複雑なPrimも持つことができます。

シーングラフツリー内で、他のGprim下にGprimを置かないでください 。 アクティブ化、可視性、PurposeなどのUSDの多くの主な特徴は階層的に適用されます。 このようなオペレーションを個々のGprimに適用できなくするのは、無意味で生産性を下げてしまいます。

Kinds(種類)

Kindsは、理解するのが難しいUSDの概念の1つです。 ジオメトリを持ったどのPrimにも"Kind"を持たせてください。

"Model Kinds"は、modelとよばれる"抽象"Kindのサブクラスです。 modelをKindとして割り当てないでください。 "Model Kinds"は3種類あります:

assembly(groupのサブクラス)

"重要なグループモデルで、出荷したアセット、出荷したアセットの参照でこれを割り当てることが多いです"

group

他のモデル(コンポーネント、アセンブリ、グループ)のグループ。モデル階層でいう"ブランチ:分岐"に相当します。

component

モデル階層でいう"リーフ:末端"に相当します。このKindのPrimには他のモデル(グループ、アセンブリ、他のコンポーネント)を含めることができません。

代わりに、componentにはKindがsubcomponentに設定されたPrimを含めることができます。 このKindは、modelのサブクラスではありません。

Kindsは、シーン内のジオメトリを モデル階層 にまとめることができます。 これは、シーン内のモデルの"目次"です。 この階層を適切に維持することで、ソフトウェアパッケージは、例えば選択に対してもっと良いUIを提供することができ、シーン内のモデルをもっと効率的に走査することができます(例えば、ソフトウェア側でユーザーにシーン内のすべてのモデルを表示させたい場合、ルート内でKindを持ったツリーだけの検索を開始するだけで済んで、componentKindが見つかった時にそのツリー以降の検索を停止することができます)。

シーングラフツリー内のモデル階層は、以下のように見えます:

/Models ← group
    /Characters ← group
        /Lady ← assembly
            /Skin ← component
                /mesh
            /Purse ← component
                /mesh
        /Dog ← assembly
            /Skin ← component
                /mesh
            /Collar ← component
                /mesh
    /Props ← group
        ...

モデル階層を維持するには、シーンが以下のようになっている必要があります:

  • (下流まで見据えて)ジオメトリを扱うPrimすべてには、ルートPrim(例えば、/Models)含めて最初からKindを持たせてください。

  • group/assembly Kindsのみに他の"Model Kinds"を含めることができます。component Kindsには、groupassemblymodelを含めることができません(component KindにはsubcomponentKindだけ含める ことができます )。

Houdiniでは、プリミティブを作成/編集するノードに、そのPrimのKindを設定するパラメータが用意されています。Houdiniは、モデル階層ルールに準じてmodelをKindとして使用することを許可していません。 現在のところ、Houdiniは、モデル階層を維持するために何かユーザーに手助け、または、強制させるための処理をしていません。

インスタンス

USDでは"インスタンス化"(効率的なコピー)の方式が色々あります。

本来のUSDは、ツリー内のPrimが他のPrimを軽量で 参照 するという概念を持っています。 この概念は、ブランチ内のすべてのPrimsを実際にコピーするよりも非常に効率的であるものの、サブPrimsをオーバーライドする機能を維持するには、USDはその参照下のサブPrim毎に参照を作成しなければならないことも意味します。

膨大な数のコピー/バリエーションでシーンをもっと効率的に構築できるようにするために、USDには2つの方法の インスタンス化 があります。

"instanceable"プリミティブ("ネイティブ"インスタンスとも呼びます)

Primが instanceable (つまり、"インスタンス化に考慮することができる")であることを示したメタデータをPrimsに追加します。

USDがinstanceable = trueのメタデータを持ったPrimを読み込むと、USDはそのPrim(と子Prim)の仮想"マスター"コピーを作成し、そのinstanceable Primをこのマスタープリミティブの参照にします。 次に、USDがシーングラフ内で同じinstanceable Primに遭遇したら、自動的にそれらのPrimが同じマスタープリミティブを指すようにすることで、非常に小さな実容量で"複製"を作成することができます。

この代償として、instanceable Prim下のどのプリミティブも変更することができません。 とはいえども、instanceable Prim自体のアトリビュートは変更することができます。 これによって、例えば、各インスタンスのトランスフォームを固有にしたり、シェーディングで使用するPrimvarを設定することができます。

詳細は、PixarのUSD用語集のInstancing(インスタンス化)を参照してください。

ポイントインスタンサー

ポイントインスタンサーPrimでは、ビューまたはレンダリング時にジオメトリ内の各ポイントが、そのインスタンサーの"prototype"リレーションシップを持ったPrimsのどれかのジオメトリのインスタンスで置換されます。 ポイントインスタンサーPrimは、ポイント対プリミティブのマッピングを配列で非常に効率的に保存します。

Houdiniのインスタンスノードは、Houdini自身のポイントインスタンスと同様に、USDの基本的なポイントインスタンサー以上の機能に対応しています。それらの"追加"機能は、そのノード内で計算されて、USDに"ベイク"されます。

詳細は、USD APIドキュメントのUSDポイントインスタンサーを参照してください。

instanceable Primsは、ポイントインスタンサーを扱うよりも簡単です。 しかし、Primのコピーと比べて各"エイリアス"は非常に少ないメモリ使用量で済むものの、非常に膨大な数のインスタンスになると、その使用量が積み上がってしまいます。 ポイントインスタンスは、一定のメモリ使用量で 数十億 ものインスタンスの制御ができるように設計されています。

どちらのタイプのインスタンスも、各プリミティブのトップレベルのトランスフォームに対応しており、さらに、(ネイティブインスタンスまたはポイントインスタンサープリミティブに対して)Primvarを使ってインスタンス単位でマテリアルパラメータをオーバーライドすることができます。

Note

ジオメトリPrimsを直接インスタンス化しないでください。 代わりに、実ジオメトリより上位にあるPrim(例えば、親トランスフォーム)をインスタンス化してください。

Houdiniは、Instancerでインスタンス化する方法、色々なTabメニューツールでインスタンサーノードを調整してインスタンス化する方法のどちらの方法でもインスタンス化することができるので、 たいていは必要なインスタンスの数に基づいて、そのインスタンス化の方法を選択します。 さらに、Houdiniは、 入れ子化されたポイントインスタンス (1レベル以上に入れ子化されたインスタンサーは、インスタンスサーを含んだプロトタイプをインスタンス化します)、"実"モデルにするインスタンスの ヒーロー化 にも対応しています。

バリアント

USDは、複数の名前が付いたプリミティブの バリアント をプリミティブ上に保存することができます。 バリアント毎に異なるアトリビュート、リレーションシップ、子Primsを持たせることができます。 レイヤー別に異なるバリアントでプリミティブを切り替えることができます。 各プリミティブは、名前の付いた バリアントセット 内に複数のバリアントのグループを保存することができます。

以下にバリアントの一般的な使用方法を載せています:

  • 異なるジオメトリのファミリー(例えば、異なるタイプのツリーを切り替えることができるプリミティブ)。

  • Levels of Detail(同じジオメトリを段階的に解像度を下げたバージョンをバリアントとして保存し、カメラまでの距離に基づいて切り替える)。

    Houdiniには、このワークフローを自動化するためのCreate LODノードとAuto Select LODノードが入っています。

  • マテリアルの割り当て(異なるマテリアルを割り当てたバリアントを保存することで、プリミティブのルックを簡単に切り替えることができます)。

以下に、ownerという名前のバリアントセットを含んだレイヤーのサンプルを載せています。 各バリアントのカラーは、そのボールを所有するストーリー内のキャラクタに呼応しています:

#usda 1.0
()

def Sphere "toyBall" (
    variants = {
        string owner = "blake"
    }
    append variantSets = "owner"
)
{
    double radius = 1
    variantSet "owner" = {
        "andy" (
        ) {
            color3f[] primvars:displayColor = [(1, 0, 0)] (
                interpolation = "constant"
            )


        }
        "blake" (
        ) {
            color3f[] primvars:displayColor = [(0, 0, 1)] (
                interpolation = "constant"
            )


        }
        "sally" (
        ) {
            color3f[] primvars:displayColor = [(0, 1, 0)] (
                interpolation = "constant"
            )


        }
    }
}

PixarのUSD用語集のバリアントセットを参照してください。

アニメーション

Houdiniのアニメーションが時間可変のノードパラメータで制御するのと同様に、USDのアニメーションは、時間可変のアトリビュート値で制御します。

  • 各アトリビュートは、単一の"デフォルト"値を持てるだけでなく、"タイムサンプル"のコレクションを持つこともできます。これは、時間をインデックスとした値のリストです。そのアトリビュート上のメタデータで、それらのサンプルの補間方法を指定します。

  • USDに"デフォルト"時間における値を照会すると、デフォルト値が設定されていれば、その値が返され、設定されていなければ空っぽの値が返されます。すべてのタイムサンプル値は無視されます。他の時間における値を照会し、デフォルト値またはタイムサンプル値が設定されていれば、その値が返されます。 この値は、デフォルト値(最初のタイムサンプルより前の時間で値を照会した場合)、最近接タイムサンプル値、2つの周辺タイムサンプル間の線形補間値のどれかです。

LOPノードのHoudiniパラメータはアニメーションさせることができ、USDをディスクに書き出す時、Houdiniは、そのアニメーション値をタイムサンプルとして書き出します。

スキーマ

  • スキーマは、新しいPrimタイプを定義したり、新しいアトリビュートの意味/挙動を定義することができるカスタムエクステンションです。

    "ツリー"レベルでは、USDは非常に汎用的で"自由形式"です。 アトリビュートに任意のタイプの値を直接設定することができます。 スキーマは、制約と不変条件を強制することができます。 よりハイレベルなAPIを使用することで、アトリビュートを"正しく"編集することができます(値を特定の範囲内に抑えたり、ありえない設定ができないようにすることができます)。

  • USDでは、独自のPrimタイプを定義したスキーマのことを Typed Schema(型決めスキーマ) と呼びます。

  • USDでは、異なるタイプのPrim上に適用可能なAPIだけを定義したスキーマのことを APIスキーマ と呼びます。このタイプのスキーマは、通常では名前がAPIで終わります。

    APIスキーマは、さらに 単体適用複数適用 に分かれます。

    • 単体適用スキーマの例がUsdLuxShapingAPIです。ライトPrimにシェイピングコントロールを追加するかしないか制御することができます。 適用済みのすべての単体適用APIスキーマのリストを記録したメタデータがPrimsにあります。

    • 複数適用スキーマの例がCollectionAPIです。指定したPrim上に必要なだけcollection:が付いた固有な名前のアトリビュートを定義することができます。

一般的にHoudiniはカスタムアトリビュートを使用しません。代わりに、Primのメタデータで"カスタムデータ"辞書に情報を保存することが多いです。この空間は、ソフトウェアパッケージでの利用やスタジオ独自の利用向けにあります。 Houdiniは、ここではUSDをディスクに保存したりシーンをナビゲーションするのに役立つを簿記情報を保存しますが、このカスタムデータは通常ではディスクに書き出されたUSDから抜き出します。

Hydraとレンダリング

USDは、特定の時点でUSDシーンから画像を生成するためのAPI(Hydraと呼びます)を定義します。 (Houdini、usdview、レンダラーなどの)プログラムがこのAPIを実装している限り、このAPIを使用してUSDシーンを表示/レンダリングすることができます。

このAPIを実装したソフトウェアのことを、 レンダーデリゲート と呼びます。

Houdiniでは、このAPIの素晴らしい恩恵のおかげで、HoudiniのOpenGLビューア、PixarのStrom OpenGLビューア、Karma IPRなどの利用可能なレンダーデリゲート間でシーンビューを切り替えることができます。 さらに、Hydra APIを実装した サードパーティ製レンダラー を使ってシーンをビューイングすることできます。 特にHoudiniと統合するための余計な作業は必要ありません。

USDの整理

  • 一般的には、"トップレベル"のUSDファイルは ショット を表現します。 スタジオでは、このトップレベルのファイルは通常では 部署 別に分けられたファイル(キャラクタ、セット/プロップ、ライト)を参照します。各ファイル自身も個々のアセットを参照します。 小規模または個人の環境では、このトップレベルのファイルは、直接アセットを参照する場合もあるでしょう。

  • USD参照の深度を"遅さ"(複雑さ)の尺度として見るのは役に立ちません。USDのコンポジションエンジンは非常に高速です。 (ロード時間などの)パフォーマンスは、参照のレベルの深さよりも、レイヤーの内容や(ファイルをローカルまたはネットワークで読み込む)インフラに左右されます。

Tips

  • USDドキュメントでは、シーンツリーのことを ネームスペース と呼んでいます。

  • 絶対ファイルパスの使用を避け、Windowsでもスラッシュを使用してください。

    相対パス参照(例えば、./chair.usd)は、参照を含んだレイヤーファイル(例えば、/Users/Aisha/Work/kitchen.usd)が基準です。

    大規模スタジオまたは複雑なファイル管理のケースでは、おそらく複雑なファイルパスを使用するよりも、アセットIDとカスタムリゾルバを使用した方が良いです。

    Houdiniでは、通常だと$HIP(シーンファイルを含んだディレクトリ)や$JOB(プロジェクトディレクトリ)やスタジオ固有変数などの環境変数で指定されたディレクトリを基準にファイルを保存します。HoudiniがUSDを書き出す時は、出力プロセッサをコールしてファイルパスを変換します。 デフォルトの出力プロセッサは、$HIPを基準としたファイルパスを、USDの慣例に従ってトップレベルのUSDファイルを基準としたパスに変換します。

関連記事

Solaris

USD

チュートリアル

Karmaレンダラー