TouchDesigner: Instance Textures ちょっと応用編

# はじめに
この記事は「[TouchDesigner: Instance Textures 基本編](https://qiita.com/aokey/items/8b498021769a1ebe7e0f)」の続編です。
また、TouchDesigner アドベントカレンダー 2020 「[嵐のバーチャル大合唱っぽいものをやってみたい！](https://qiita.com/aokey/items/12fc1809f0c788c0f842)」の補足記事となっていますので、併せて読んでいただけると嬉しいです。

# 今回作るもの
基本編では、1つのインスタンスに1枚のテクスチャを丸ごと貼り付けることができましたが、今回はインスタンシングしたオブジェクトに対して、テクスチャの一部を切り取って貼り付けるというのをやってみます。  

Zoom の 7x7 の画像を4枚使って、インスタンシングしたオブジェクトに1面ずつ貼りつけていきます。
最終的にできるイメージはこんな感じです。
![InstanceResult.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/9eb264ab-06f5-8aac-a210-ca05204d0b63.png)

# サンプル
基本編、ちょっと応用編のサンプルはこちらです
https://drive.google.com/file/d/1ecURR_sNvjqYKtqM_SMKNtCuEU2bIJob/view?usp=sharing

# GPU インスタンスを使ったマルチスクリーンの作成
さっそく作っていきましょう。
7x7の画像はネットで適当に拾ってくるか、「[嵐のバーチャル大合唱っぽいものをやってみたい！](https://qiita.com/aokey/items/8b498021769a1ebe7e0f)」のところから適当に使ってください。

## SOP のインスタンシング（コピー）
では早速インスタンシングの元となる 7x7 = 49 Points の SOP を4面作ってみたいと思います。

1. まずは、1画面用の Grid SOP (grid1) を作ります。画面アスペクトに比例した7x7のポイントを作りたいので **Size: (1.6, 0.9), Rows:7, Columns: 7** と設定します
2. もう１つ、複製用の Grid SOP (grid2) を作り、とりあえず **Size: (1.6, 0.9)** にしておきます
3. Copy SOP を作り Input 0 にコピーされるオブジェクト grid1 を入れ、Input 1 コピー先に grid2 を入れます
![CopySOP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/bc9f84bc-fba0-e4e7-2d55-2aead9f0795a.png)
4. 複製された SOP のポイント番号を確認すると、座標軸のところでポイントが重なっているところがありますね。
これは、コピーされた SOP のエッジが重なっているからです。
![CopySOP2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/49e6a248-3ab7-30d6-ee7a-7e636136ec51.png)
エッジとエッジの間に小窓１つ分(横: 1.6/6、縦: 0.9/6)のスペースを空ける必要があるので、grid2 の Size を **sizex: 1.6*(1+1/6), sizey: 0.9*(1+1/6)** にするときれいにピッチがそろったグリッドができあがります。
![CopySOP3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/e545abe2-e4a8-49f3-3041-83fefda9e97f.png)
目の錯覚で少し間隔が狭いように感じますね。  
ここで、注目して欲しいのが、上下左右4つに配置された SOP 単位で連続した 49 個の Point ID がまとまっているという事です。左下の SOP で 0 ～ 48 までのIDがまとまっていますが、これが Zoom 1画面と同じ配置になります。
5. 最後にインスタンスの間の調整用として copy1 の後ろに Transform SOP (transform1) を繋ぎ、終端として Null SOP (null1) を繋いでおきます。
![SOP_for_Instance.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/a73796c3-ce46-5667-d3d0-0798c65d637b.png)
ここで作った SOP の Point に対して Zoom 画面用の SOP をインスタンスしていきます。

## SOP のインスタンス （GPUインスタンス）
今度は Zoom 画面用の板を作っていきます。
1. Grid SOP (grid3) を作り、画面アスペクトに合わせて **Size: (1.6, 0.9)** にします
2. このあと Texture の UV を操作するので、grid3 の後ろに Texture SOP (texture1) を繋ぎます。
3. Geometry COMP (geo1) につないでいきます
![GridSOP3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/1903fed0-07e0-ad57-a47a-3f7b0d8aafad.png)
4. geo1 を **Instance: ON** にし、**Translate OP: null1, Translate X,Y,Z: P(0), P(1), P(2)** にします
![SOP_Instance.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/d7500d7a-288f-6866-cbd4-2f854b0e9750.png)
この時点では geo1 にインスタンスされた SOP が重なっている状態になっていて1つの板のように見えています。
5. Transform SOP (transform1) の Scale を上げていくと徐々にインスタンスされた SOP が広がっていきます。**Scale: 7** 程度にすると、1つ1つが離れてみえるようになるでしょう
![TransformSOP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/7c20d072-dcfa-87c6-4db6-d2344cf24941.png)

## テクスチャの貼り付け
Zoom の 7x7 の画像を4枚を使って作業します。

1. Movie File TOP を4つ作ってそれぞれに Zoom 7 x 7 の画像を読み込みます
2. 不要な部分を Crop TOP でカットし、終端に Null TOP つないで名前を tex1, tex2, tex3, tex4 のように変えておきます
3. Constant MAT を作り **Color Map: tex1** にし、geo1 の **Render -> Material: constant1** にします
4. このままでは各SOPに49人分の画像が貼り付けられてしまいます。そこで、Texture SOP (texture1) の **Scale: (1/7, 1/7, 1)** にし一人分のテクスチャスケールに変更します
![TextureSOP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/ac564f09-3703-e8bc-bd46-104e27d359e5.png)

## Instance Textures
これで、テクスチャをバラバラにする準備は整いました。
あとは基本編でやったテクスチャの入れ替えと、UV のオフセットを各インスタンスオブジェクトに割り当てていくだけです。
1. Pattern CHOP (patern1) を作り、チャンネル名をわかりやすく **Channel -> Channel Name: index** に変更しておきましょう
2. **Type, Length, Number of Cycles, Step per Cycle** の4項目を以下のように設定します

![PatternStepForInstance.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/4b016499-c76b-e2f1-7072-132b0c72d2f4.png)
3. geo1 の Instance 2 のところで、**Instance Textures: tex1, tex2, tex3, tex4** と指定し、**Texture Index OP: pattern1, Texture Index: index** を指定すると、左下、右下、左上、右上にインスタンシングされた SOP のテクスチャを切り替えることができました。
![InstanceTexture2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/a84b66f1-c571-de41-e66d-85562cf70452.png)
現状こんな感じです。次に Offset を割り当てていきます。
4. Pattern CHOP (pattern2) を作り、**Type: Ramp, Length: 7*7*4, Number of Cycles: 7*4, To Rang: (0, 1-1/7)** にします。
5. **Channel -> Channel Name: offsetu**
![OffsetU.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/96663e07-9770-0bee-a5d5-cbd8d2b29814.png)
これで、左端から右端へ反復移動する U のオフセットパターンが作れました。
6. もう１つ Patter CHOP (pattern3) を作り、**Type: Ramp, Number of Cycles: 4, To Range: (0, 7), Integer: Floor** にします。
7. **Channel -> Channel Name: offsetv** に変更しておきます。
8. pattern3 では Integer パラメータを使うことで、0～6の値を取るステップを繰り返し作りました。だけど、V のスケールは 0-1 の範囲なので、pattern3 の後ろに Math CHOP を繋ぎ、**Multi-Add -> Multiply: 1/7** にして 0-1 の範囲に収めるようにします。
9. 先ほどの pattern2, pattern3 をマージして、終端に Null CHOP (null2) をつなげておきます
![MergeCHOP.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/7074255f-cff6-0a27-2b09-575840a94187.png)
10. 最後の仕上げです。
null2 を geo1 の Instance パラメータに設定していきます。**Instance 2 -> Texture Mode: Offset, Texture Coord OP: null2, U: offsetu, V: offsetv**
![TextureCoord.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308950/4844c455-b593-36e8-f64b-d9ba6d5a608e.png)
これで、7 x 7 x 4 = 196 面の SOP にそれぞれ別々の Zoom 画面を GPU Instance を使って割り当てることができました。
TOP を使ってやろうとすると処理負荷が重いのですが、この方法を使えば GPU を効率よく扱えかなりサクサク動くようになることでしょう。

あとは、今まで通り GPU インスタンを使って煮るなり焼くなり自由動かして遊んでみてください。

# まとめ
GPU Instance は SOP オブジェクトを大量に扱うのに適した手法です。TOP では負荷がかかりすぎて処理しきれないものも、GPU Instance を使うことで効率的に扱えるようなこともありますので、色々試して新しい使い方を発見してみてください。
今回は U, V までしかやりませんでしたが、3D Texture と UVW を使って作る表現も面白そうですね。
この記事を書きながらアイデアが浮かんできたのでテストしてみてまたいつか記事にできればと思っています。