Instancer (Copy to Points) を使いつつテクスチャをランダムに！

**Houdini Apprentice Advent Calender 2024** の6日目の記事になります。
今回の記事で作成したhipファイルは[こちら](https://drive.google.com/drive/folders/1cszY9qIyOocbfrdfFhQ8HW2MYWDq8eL7?usp=sharing)になります。テクスチャはお好みのものを適用してください。

# はじめに
最近参加しているプロジェクトで本格的にHoudiniのSolarisとUSDを使い始めました。HoudiniはUSDに関する機能のサポートが充実しており毎日楽しく勉強しております。
今回の記事ではSolarisでInstancer(Copy to Points)を使う際の基本的なテクニックを解説しようと思います。幅広く応用が利くと思うのでぜひ最後まで読んで頂けると嬉しいです！

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [背景](#背景)
1. [準備](#準備)
1. [uv_offsetアトリビュートの作成と活用](#uv_offsetアトリビュートの作成と活用)
1. [まとめ](#まとめ)
1. [参考文献](#参考文献)

# 背景
<a id="#背景"></a>
Houdiniではよく大量のオブジェクトの配置にCopy to Points を使います。 objコンテキストではCopy to Pointsノード、stageではInstancerノードがこれに該当します。Instancer(インスタンス化)は通常のCopy to Pointsと比べてメモリ使用量の削減ができ、軽い状態で作業できるというメリットがあります。
自分が初めてstageでInstancerノードを使ってオブジェクトの配置とテクスチャ割り当てを行ったとき次のような問題に直面しました。それは…
**コピーされたオブジェクトのテクスチャのオフセットが全部同じになっていてランダム感がない！！！**
という問題です。
Instancerでコピーされた板のようなオブジェクト(以下板オブジェクト)にテクスチャを貼った様子です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/f48a0fb3-883b-c47c-4c7b-33b92750fb0c.png)

大量にオブジェクトを配置した際にテクスチャのオフセットが全て同じだとランダム感がなくて困りますよね。

objコンテキストならCopy to PointsとUV系のノードを組み合わせることで、それぞれの板オブジェクトのテクスチャをランダムにできます。しかしstageで普通にInstancerを使うとこのような問題が起きてしまいます。
そこでこの問題を解決するちょっとした工夫を解説しようと思います。

# 準備
<a id="#準備"></a>
まず、コピー元となる板オブジェクトとコピー先のポイントを用意します。
Stageで扱いやすいようにNULLに繋いでおきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/8568b312-c6ef-cbc2-15cc-1ea60e7979a8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/f07cb5d3-df3f-aa8b-1b0f-4880af7236d6.png)

stageに行ってInstancerで配置してみましょう。
今回は板オブジェクトはSop Import ノード、ポイントはInstancerのTargets PointsをExtarnal SOPに設定して読み込みます。
板オブジェクトに関してはobjでUSDに書き出してstageでreferenceノードなとで読み込んであげても大丈夫です。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/8e59a7ec-0c1f-025b-027d-41bb38d636ce.png)

次にMaterial liblaryでMtlx standard surfaceノードーのbase colorにテクスチャを割り当ててみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/8abc272c-6115-5afb-c75b-469a572c1994.png)

冒頭で見たようにテクスチャのオフセットが全ての板で同じになっておりランダム感がないです。

# Instancerについて
<a id = "#Instancerについて"></a>
Houdini公式のInstancerの説明
(https://www.sidefx.com/ja/docs/houdini/nodes/lop/instancer.html)
USDのインスタンス化の説明
(https://www.sidefx.com/ja/docs/houdini/solaris/usd.html#instancing)
を見るとインスタンス化とは

**・ブランチ内のすべてのPrimsを実際にコピーするわけではなく、ツリー内のPrimが他のPrimを軽量で 参照するため非常に効率的**

**・インスタンス化の方式には、実用量が非常に小さい仮想マスターコピーを行うinstanceable”プリミティブ方式とポイントをprototypeリレーションシップを持ったPrimsで置換するポイントインスタンサー方式がある**

**・どちらの方式もPrimvarなどを使ってインスタンス単位でマテリアルパラメータをオーバーライドすることができる。**

となっていることが分かります。
つまりInstancerノードに特に何も設定しないとコピー元を参照しただけで実体がないということです。そのため今回の場合は

**4つのポイントに板オブジェクトが読み込まれただけで4つの板が実体として存在していない**
**→ 4つの板が1つのオブジェクトとしてUV展開が行われるわけではなく、4つの板は全てコピー元のUVを読み込んだだけ**
**→ 4つの板全てで同じUVなのでテクスチャが同じになる。**
というわけです。
これを解決するために各ポイントにランダムなUVのオフセットを持たせましょう！

# uv_offsetアトリビュートの作成と活用
<a id="#uv_offsetアトリビュートの作成と活用"></a>
各板オブジェクトのテクスチャをランダムにするため、ポイントごと値がランダムな「uv_offset」アトリビュートを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/f9928312-86a6-8020-2383-a4ba76f564fc.png)

この際大切になるのが
**コピー先となる「ポイント」にアトリビュートを作成する**
ことです。
コピー元のポイントのそれぞれにランダムなuv_offsetを作成し、テクスチャの割り当ての時にその情報を使います。

stageのMaterial Libraryノードに移動してUV情報とuv_offsetアトリビュートを読み込みます。今回は
MtlX Geometory Property Valueを使いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/ef1b6b4c-4ded-de8f-4386-36af3821f19c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/2c012d11-0a09-1354-5a64-a62d10fbeb6c.png)
これらをmtlxaddノードで足してテクスチャを読み込むMtlX Tiled Imageノードの「texcord」に繋ぎます。uv_offsetに繋いでも大丈夫です。

ビューを見てみるとこのように各板オブジェクトでテクスチャが別になりランダム感が出ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/d3d32efc-aaa1-755d-926a-bed637f9d986.png)

stage(USD)でも「uv_offset」アトリビュートを確認しておきましょう。InstancerノードのMethodは Instanceable Referenceです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3788547/2d83a914-f52c-d8c6-ba9e-11fc51d45973.png)
各インスタンスのレイヤーで 「primvars:uv_offset」を持っているのでUSDにも「uv_offset」アトリビュートを持ってこれているのが分かります。

# まとめ
<a id = "#まとめ"></a>
ポイントごとに「uv_offset」アトリビュートを持たせることで、stageのInstancer(Copy to Points)ノードでコピーした場合でもテクスチャをランダムにすることができました。
この
**コピー先のポイントにアトリビュートを持たせる**
というのが
**Instancerでコピーした各オブジェクトのアトリビュートを別々にしたい！**
という時の基本のテクニックになります。
例えばポイントの「orient」アトリビュートをランダムにしておけばコピーされたオブジェクトの回転をランダムにできます。
このようにInstancerノードを使う場合は幅広く応用できるのでぜひ活用してみてください！

# 参考文献
<a id = "#参考文献"></a>
Super Photo Real様の解説動画 Unique UVs for Each Geometry Instance | Instance To Texture HDA + Scene | Houdini
https://www.youtube.com/watch?v=IQpbvCuCxTk&list=PLsuDLbyvLYgq3vxV3afbdCUHWJKZ6_IXz&index=3
Houdini公式のInstancerの説明
https://www.sidefx.com/ja/docs/houdini/nodes/lop/instancer.html
USDのインスタンス化の説明
https://www.sidefx.com/ja/docs/houdini/solaris/usd.html#instancing