Houdini Solaris応用|アセット化・バリアント・レイヤー設計

Houdini Solaris応用|アセット化・バリアント・レイヤー設計

Houdini Solaris入門|LOPでUSDを組みKarmaでレンダー では、小物を1つ取り込んで Karma で描き、USD に書き出すまでを扱いました。本記事はその続きとして、Solaris を実制作のパイプラインに組み込むための5つのテーマ(アセット化、バリアント、レイヤー構成、インスタンシング、パイプラインへの組み込み)を、Houdini での手順に絞って解説します。

合成(Composition Arcs)の理論は、理論編の USD入門|ゲームスタジオで何が変わるか・段階移行と現状 にまとめています。対象は執筆時点の最新版 Houdini 22.0(2026年7月リリース)で、22.0 では Instancer LOP が Copy to Points LOP に改名されるなど、バージョンによって表記が異なります(変更点は SideFX 公式の What’s new in Houdini 22(Solaris) を参照)。

夕宮たいだ

ふぁ……みんな〜、今日は Solaris の応用編だよぉ。アセットを USD にして、並べて、チームに流すところまで。ゆっくりいこ〜。

目次

全体像:アセット・配置・出力の3段で考える

ひとことで:Solaris の発展的な機能は「アセットを作る」「シーンに並べる」「書き出して流す」の3段に分けると整理できます。

段階やること主なノード・機能
アセットを作る小物を再利用できる USD にまとめるComponent Builder
シーンに並べる参照・大量配置し、担当ごとにレイヤーを分けるReference、Copy to Points、Sublayer、Layer Break
書き出して流すファイル構成とパスを固定し、処理を自動化するUSD ROP、出力プロセッサー、husk、TOPs
Solaris 応用の全体像。アセットを作る、シーンに並べる、書き出して流すの3段階
図1:応用機能は「作る・並べる・流す」の3段で整理すると迷わない

Component Builder でアセットを USD にする

ひとことで:Component Builder は、ペイロード・ファイル分割・クラス継承といった USD の定石を自動で整えるアセット化の型です。

USD では、木箱のように単体で参照される「もの」を component という kind で表します。Component Builder はこれを作るノード一式で、ジオメトリのペイロード化(読み込みを外してもバウンディングボックスは表示される)、ジオメトリとマテリアルのファイル分割、クラスプリムからの継承の設定を自動で行います。

LOP ネットワークで Tab キーを押し、Component Builder と入力して Enter を押すと、次の4ノードが置かれます。ディスプレイフラグは一番下の Component Output に立てます。

ノード役割
Component Geometry中の SOP ネットワークでジオメトリを作る
Material Libraryアセット専用のマテリアルを作る
Component Materialマテリアルを割り当てる
Component Outputcomponent プリムにまとめ、ディスクに書き出す

Component Geometry の中の出力ノードには、default(緑)に本番用、proxy(黄)に表示用の軽いジオメトリ、simproxy(ピンク)に衝突計算用の簡易ジオメトリをつなぎます。proxy には PolyReduce SOP、simproxy には Convex Decomposition SOP が便利です。22.0 では4つ目の出力が加わっており、既存のノードは Fix SOP Outputs で出力ノードを作り直せます。

書き出しとファイル構成

Component Output の Root Prim は既定で /$OS(ノード名)なので、/crate のような名前にします。Caching セクションの Location が保存先で、既定は .hip ファイルからの相対パスです。Save to Disk を押すと、次のディレクトリができます。

crate/
  crate.usd      メインファイル(payload を参照)
  payload.usdc   geo と mtl を参照
  geo.usdc       ジオメトリ
  mtl.usdc       マテリアル
  thumbnail.jpg  サムネイル

チームで使うアセットは、Location を共有のアセットディレクトリに向けてから書き出します。書き出し後に Add to Asset Catalog を押すとアセットカタログに登録され、Paint Instances LOP のブラシで配置できます。

/crate は /__class__/crate を継承するので、シーン側でこのパスに値を書くと、配置したすべての木箱にまとめて上書きが効きます。Scene Graph Tree でプリムを右クリックし、Edit Primitive > New Node to Edit Inherit を選ぶと、そのための Edit Properties LOP が作られます。一連の手順は公式の Component Builder のページにまとまっています。

Component Builder が書き出すファイルの参照関係とクラス継承
図2:メインファイルからペイロード、ジオメトリとマテリアルへと参照がつながる
夕宮たいだ

ほえ〜、ペイロードもファイル分けもクラス継承も、ノードを置くだけでそろうんだぁ。一から組むより事故が少ないねぇ。

バリアントを作って切り替える

ひとことで:アセット内のバリアントは Component Builder で作り、既存のプリムへの後付けは Add Variant LOP、選択は Set Variant LOP で行います。

Component Builder で作る

  • ジオメトリ:Component Geometry と Component Material の間に Component Geometry Variants を挟み、マルチ入力に Component Geometry を複数つなぎます。1ノードが1バリアントになり、セット名の既定は geo です
  • マテリアル:Component Material を追加し、それぞれの Variant Name を設定します。セット名の既定は mtl です
  • 既定の選択:Component Output の Set Default Variants で決めます

Component Geometry Variants の Source Mode を Number にすると、コンテキストオプション @GEOVARIANTINDEX を使って形違いを手続き的に作れます。ただし、Component Builder のネットワークは複数や入れ子のジオメトリバリアントセットには対応していません。

Add Variant LOP で後付けする

既存のプリムには Add Variant LOP でバリアントを足します。1つ目の入力にメインのステージ、2つ目のマルチ入力にバリアントの中身をつなぎ、Primitive Path(追加先)、Variant Set(セット名)、Variant Source Primitive(中身のルート)を指定します。

注意したいのは合成の強さです。バリアント内の値は、手元のレイヤーに書かれた値より弱く扱われます。Add Variant には、これを検出する Warn of Weak Variant Opinions と、元のプリムを参照経由にしてバリアント側を強くする Source Primitive が用意されています。

選んで見比べる

  • Set Variant LOP:Variant Set と Variant Name で選択を切り替えます。Choose Variant Name by Index を使えばランダムな選択もできます
  • Explore Variants LOP:全バリアントをビューポートに並べて見比べられます
  • Asset Reference LOP:参照・トランスフォーム・バリアント選択を1ノードで行えます
バリアントセットの構造と、手元のレイヤーがバリアントより強いことを示す図
図3:同じ属性に手元のレイヤーの値があると、バリアントの切り替えより優先される
夕宮たいだ

むずかしいねぇ……バリアントより、手元のレイヤーの値のほうが強いんだって。切り替えても変わらないときは、まずそこを疑ってねぇ。

レイヤー構成を設計する

ひとことで:「誰がどのファイルに書くか」を先に決め、Sublayer・Layer Break・Save Path で LOP ネットワークに写し取ります。

LOP はディスクから読んだレイヤーを書き換えず、上に重ねたレイヤーに編集を書き込みます。この性質を使えば、担当ごとに自分の分だけを別ファイルへ書き出せます。ゲームのレベルなら、たとえば次の分け方です。

ファイル担当中身
level.usdaリード・TA各レイヤーを Sublayer でまとめるルート
lighting.usdライティングライトとレイアウトへの上書き
layout.usdレイアウトアセットの参照と配置

担当レイヤーだけを書き出す

ライティング担当の例で、公式の Layer Break LOP のヘルプにある手順を追います。

  • ① Sublayer LOP で layout.usd を読み込みます
  • ② Layer Break LOP をつなぎます。これより上のレイヤーは書き出されません
  • ③ Prune LOP で仮のライトを非アクティブにし、本番のライトを足します
  • ④ USD ROP で lighting.usd に書き出します

出力されるのは、Layer Break より下で書いた上書きとライトだけです。USD ROP の Strip Layers Above Layer Breaks がオンになっていることも確認します。

保存先と書き出し方を決める

別ファイルにしたいレイヤーは、Configure Layer LOP の Save Path に保存先を指定します。同じノードの Default Primitive は、参照側がプリムを指定しなかったときに使われます。Up Axis や Meters per Unit は変換を伴わない目印なので、単位の違うデータは実データのスケールを事前にそろえます。USD ROP の Save Style は、書き出すファイルの分け方を決めます。

Save Style動作
Flatten Implicit Layers(既定)匿名レイヤーを保存先のあるレイヤーにまとめる
Flatten All Layersすべてのレイヤーを1つにまとめる(参照先は残る)
Separate Layersレイヤーごとに書き出す(主にデバッグ用)
Flatten Stage合成まで焼き込んで1ファイルにする

Merge LOP で枝を合流させる場合は、後ろの入力ほど強くなります。同じ上流から分岐して合流するひし形の構成では、1本目以外の枝の先頭に Layer Break を入れることが公式に推奨されています。

担当別のレイヤー構成と、Layer Break より下だけが書き出される仕組み
図4:Layer Break より上は読み込んだ文脈、下が自分の担当として書き出される
夕宮たいだ

Layer Break を入れ忘れて、読み込んだ元データまで書き出しちゃうの、ほんとダメだよ。書き出す前に Scene Graph Layers を見てねぇ。

インスタンシングで大量に配置する

ひとことで:USD のインスタンスは「インスタンス可能なプリム」と「PointInstancer」の2種類で、配置の数と編集の必要性でノードを選びます。

インスタンス可能なプリム(ネイティブインスタンス)は、配置ごとにプリムがあり位置などを個別に持てますが、子孫は共有されるため個別に編集できません。PointInstancer は1つのプリムにポイントとプロトタイプを持ち、描画時に展開するので、大量の配置に向きます。

ノード使いどころ
Copy to Pointsポイントやプリムの位置にプロトタイプを配置する(22.0 で Instancer から改名)
PointInstancerPointInstancer の作成と編集に特化(22.0 で追加)
Paint Instancesアセットをブラシで置く・散らす(22.0 で Layout から改名)
Scatter Instancesメッシュへの散布をレンダー時に計算する(22.0 で追加)

Copy to Points LOP では、Method で PointInstancer・Instanceable Reference・Reference などの方式を、Location Source で配置先を選びます。Prototype Index を Random にすると、複数のプロトタイプがランダムに使われます。参照するだけなら、Reference LOP の Make Instanceable や、Component Output の Instanceable by Default でもネイティブインスタンスにできます。

Scatter Instances LOP の Execution Mode が Deferred の場合、散布はレンダー時に計算され、ステージには書き込まれません。他のツールへ渡すなら Immediate を選びます。エンジン側がどのインスタンス方式に対応しているかも、渡す前に確認します。

インスタンス可能なプリムと PointInstancer の違い
図5:1個ずつ扱うならインスタンス可能なプリム、大量に置くなら PointInstancer
夕宮たいだ

何千個も置くなら PointInstancer、1個ずつ少しいじりたいならインスタンス可能なプリム。分かれば便利だねぇ。

パイプラインに組み込む

ひとことで:書き出しのルール・パスの解決・自動化・検証を決めておくと、Solaris の成果物をチームやエンジンへ安定して流せます。

書き出しのルールとパス

USD ROP には既定で出力プロセッサー Use Relative Paths が入っており、$HIP などの絶対パスをファイル同士の相対参照に変換して書き出します。スタジオ独自のパス規則は、Python で書くカスタムの出力プロセッサー($HOUDINI_USER_PREFS_DIR/husdplugins/outputprocessors/ に置くプラグイン)で強制できます。独自のアセットリゾルバーを使う場合は、Configure Stage LOP の Resolver Context Asset Path などで設定を渡します。

レンダーと処理の自動化

USD Render ROP は内部でコマンドラインレンダラーの husk を起動して描画しています。husk は husk [options] usd_file [-o image_file] の形で、バッチやファームから直接呼べます(オプションは公式の husk を参照)。22.0 からは USD に定義されたフレーム範囲をまとめてレンダーするので、1フレームだけなら -n 1 などを指定します。TOPs では、husk でレンダーする USD Render Scene TOP や、依存ファイルや未解決のパスを一覧にする USD Analyze TOP が使えます。

LOP で表現しにくい処理は、Python Script LOP で USD の Python API を使って書けます。

stage = hou.pwd().editableStage()
for prim in stage.Traverse():
    # プリムを調べたり書き換えたりする
    pass

editableStage() を呼ぶ前に、入力ステージの読み取りやパラメータの評価を済ませておくのが公式の推奨です。hou モジュールの基本は Python in Houdini入門 で扱います。構造の検証には、kind の階層や primvar の配列長などを調べる Scene Doctor LOP も使えます。

エンジンや他ツールへ渡すとき

  • マテリアル:Karma Material Builder は Karma 向け、USD MaterialX Builder は純粋な MaterialX を書きます。Material Library が自動生成する USD Preview Surface は、多くのレンダラーで表示できる代替になります
  • 単位と軸:Up Axis・Meters per Unit は目印にすぎないので、実データをそろえてから渡します
  • レンダー時の処理:Deferred の散布のような仕組みは、結果をステージに書き込む形にしてから渡します

エンジン側の USD 対応は、USD入門|ゲームスタジオで何が変わるか・段階移行と現状 の「UE5 / Unity / Houdini での対応状況」と、HoudiniでFBX/USDを書き出す で確認できます。

夕宮たいだ

書き出しのルールをツールに閉じこめちゃえば、みんな同じ形で USD を出せるんだぁ。ほら、らくちんでしょ?

よくある失敗と対処

  • バリアントを切り替えても変わらない:手元のレイヤーに同じ属性の値が書かれていると、そちらが優先されます。Scene Graph Details の Layer Stack や Composition タブで、値の出どころを確かめます
  • 書き出した USD に元データまで入っている:Layer Break がないか、Strip Layers Above Layer Breaks がオフです
  • Error Saving Layers with Implicit Paths で止まる:保存先のない匿名レイヤーを個別に書こうとしています。Save Path を与えるか、既定の Save Style に戻します
  • 別の PC でアセットが見つからない:アセットを .hip の隣(Location の既定の場所)に書き出したままになっていないか、Use Relative Paths を外していないかを確かめます
  • インスタンスの一部だけ編集できない:子孫は共有されています。Method を Reference にするか、プロトタイプやクラスプリムを編集します
  • 散布の結果が他のツールで出ない:Scatter Instances が Deferred のままです。Immediate にして書き出します

チェックリスト

  • [ ] アセットは Component Builder で作り、Root Prim と Location を決めた
  • [ ] proxy と simproxy を用意した
  • [ ] バリアントの命名規則を決め、既定のバリアントを設定した
  • [ ] 担当ごとのレイヤーを決め、Layer Break で上流のデータを書き出さない構成にした
  • [ ] Configure Layer の Save Path・単位・軸を確認した
  • [ ] インスタンスの方式を、数と編集の必要性で選んだ
  • [ ] 渡し先で使うマテリアルの形式を決めた
  • [ ] Scene Doctor LOP や USD Analyze TOP で検証した

次に読む記事

夕宮たいだ

ふぁ……これで Solaris をチームの道具として回す準備はばっちりだよぉ。小さなアセット1個から試してみてねぇ。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次