Geometry instancingとGLSLを使った滑らかな絵作り

[TouchDesigner Advent Calendar 2018](https://qiita.com/advent-calendar/2018/touchdesigner) 21日目の記事です。

# はじめに
安定性に定評のあるTouchDesignerですが、沢山の3Dモデルを配置したり、一度に大量のオブジェクトを動かそうとするとFPSの低下が起きてしまいます。
リアルタイムレンダリング命のTouchDesignerが、カクカクしているとなんとも切ない気持ちになるのは避けられません。

そこで今回は**Geometry instancing**というテクニックと、**GLSL(シェーダー言語)**を使って大量のオブジェクトを描画しつつ、それらを滑らかに動かそうというのが目的です。

# サンプルプロジェクト
こちらに今回のサンプルプロジェクトをアップしましたので参考にしてみてください。
[GoSato/GeometryInstancing_GLSL](https://github.com/GoSato/GeometryInstancing_GLSL) (GitHub)

# ゴール

![WaveFloor2.gif](https://qiita-image-store.s3.amazonaws.com/0/82250/ae2c5a2a-e48d-32f8-393d-acd4a7e6e753.gif)

こんな感じに、**スッ**っと出てきて**ワサワサー**と動くものを作ります。
gifだと画質とFPSに限界があるので、画質の良いものはvimeoに貼っておきます。

[Geometry instancing x GLSL TouchDesigner](https://vimeo.com/307526681) (vimeo)

![geo05.JPG](https://qiita-image-store.s3.amazonaws.com/0/82250/f129a454-bf7b-b403-bc7b-a4c946192177.jpeg)

# Geometry instancingとは

簡単に説明すると画面に大量にオブジェクトを描画したい時に、CPU側からの描画呼び出しは1回で同じメッシュのコピーを大量に描画するテクニックです。  
これによりドローコールを大幅に抑えることができます。  
ゲームではパーティクルや草、木など画面上に大量に必要なものに使われます。

# Geometry instancingの準備

Geometry instancingを使うためには「**大量に生成する元となるメッシュデータ**」と「**生成させる位置となる座標データ**」の2つが必要になります。  

<img src="http://haquxx.com/wp-content/uploads/2018/11/example_instancing-1024x576.jpg" alt="" width="1024" height="576" class="alignnone size-large wp-image-1787" />

この画像では「**teapotオブジェクト**」を「**boxの頂点座標の位置**」にinstancingしています。  
これを「**草**」に変えて、生成位置を「**平面上のランダムな位置**」にすればフィールドに大量の草を生やすことができると言うわけです。

# TouchDesingerでの使い方

### 3Dオブジェクトの描画準備

<img src="http://haquxx.com/wp-content/uploads/2018/11/01-1024x555.jpg" alt="" width="1024" height="555" class="alignnone size-large wp-image-1788" />

まず始めに```Geometry OP```、```Camera OP```、```Light OP```と```Render TOP```を配置し、3Dオブジェクト(torus)を描画します。   
これは「**大量生成する元となるメッシュデータ**」になります。 

ここまででTouchDesignerの基本である**3Dオブジェクトの描画**ができたと思います。

### Geometry OPの設定

<img src="http://haquxx.com/wp-content/uploads/2018/11/02-1024x555.jpg" alt="" width="1024" height="555" class="alignnone size-large wp-image-1789" />

次に```Geometry OP```を選択し、**Instanceタブ**の**Instancing**をオンにします。  
画面上に何も表示されなくなりますが、これは「**生成する位置となる座標データ**」を指定していないからです。

### 生成する位置となる座標データの用意

<img src="http://haquxx.com/wp-content/uploads/2018/11/03-1024x555.jpg" alt="" width="1024" height="555" class="alignnone size-large wp-image-1790" />

今回は```Shere SOP```を使用します。  
頂点座標を取得するために```SOP to CHOP```に```Sphere SOP```を設定。  
これで下準備終了です。

### Geometry OPにSOP to CHOPで取得した値を反映

<img src="http://haquxx.com/wp-content/uploads/2018/11/04-1024x555.jpg" alt="" width="1024" height="555" class="alignnone size-large wp-image-1791" />

あとは```Geometry TOP```のInstanceタブを画像のように設定。  
```Render TOP```上で描画結果を確認するとtorusが大きいために重なって表示されているので、大きさを適度なサイズに変更します。

# 完成

<img src="http://haquxx.com/wp-content/uploads/2018/11/05-1024x555.jpg" alt="" width="1024" height="555" class="alignnone size-large wp-image-1792" />

このように***Geometry instancing***を使って「**ドーナッツ(torus)**」を「**球面上の位置**」に大量生成することができました。

# 動きをつける

これでは地味なので動きをつけてあげます。
![GeometryInstancingSample.gif](https://qiita-image-store.s3.amazonaws.com/0/82250/849072da-350f-00b0-0652-8e45df2f5cdc.gif)
[Geometry Instancing Demo](https://vimeo.com/298798859) (vimeo)
Transformの値を変えることによって動きをつけています。

# 本題
ここまでは入門編みたいな感じで、ここからが本編です。

このように**Geometry instancing**で生成した大量のオブジェクトを**GLSL**で動かしたいと思います。

# GLSLとは
GLSLとは**OpenGL Shading Language**の略称で、いわゆるシェーダー言語と呼ばれるものです。

GPUを使用した並列処理のため、高速に描画を行うことができるのが特徴です。
 
# 下準備

「**大量に生成する元となるメッシュデータ**」と「**生成させる位置となる座標データ**」の2つを用意します。
一番最初に載せたものでは四角形の平面に、タイル状に並べられた六角柱が上下にワサワサしていました。

![geo05.JPG](https://qiita-image-store.s3.amazonaws.com/0/82250/a5e9ae6a-fed7-e8b8-4ef0-8f767a077d9c.jpeg)

「**大量に生成する元となるメッシュデータ**」が「**六角柱**」で、「**生成させる位置となる座標データ**」は「**四角形の平面**」になります。
そして、**ワサワサ**の部分がGLSLで記述した動きです。

# ハチの巣模様の平面を作る
これは```Grid SOP```を使用しているのですが、そのまま使おうと思うと六角形が隙間なく並ばずに、頂点同士が当たってしまいます。

![geo01.JPG](https://qiita-image-store.s3.amazonaws.com/0/82250/904c2b17-9caf-1021-c6e9-4f5d65f83e47.jpeg)

頂点同士がぶつからずに、六角形の形を生かしてハチの巣状に並べるには一工夫必要です。

今回説明は割愛しますが、簡単に説明すると```Select CHOP```を使用して各頂点のx座標を抜き出し、```Script CHOP```を使って奇数行目のx座標を六角形の横幅分ずらしてあげることで前後の頂点がぶつかることなく並べることができます。

具体的にどういった処理を行っているかは```Script CHOP```の中身を見てみてください。

# 六角柱の3Dモデル

こちらはTouchDesigner標準のSOPにはないのですが、サンプルプロジェクトのGeoフォルダの中に入れてあるのでそちらを使ってください。

Blenderなどを使えば複雑な操作なしに簡単に作れるので、欲しい3Dモデルがあれば自分で作ってしまうのも手です。
頂点を指定すればTouchDesigner上で同じものを作るのも可能です・・・若干面倒ですが・・・

今回はタイルのように使いたいのでx軸で90度回転して寝かせてあげます。

![geo02.JPG](https://qiita-image-store.s3.amazonaws.com/0/82250/db9b24f0-136b-ded7-0736-738e744d82b5.jpeg)

# Let`s Geometory instancing
これで下準備が終わったので、あとは先ほどの説明の通りに**Geometry instancing**の設定をしてあげれば
「**四角形の平面に、タイル状に並べられた六角柱**」の完成です。

残すはシェーダーで動きをつけてあげるだけです。

# GLSLで動きをつける
今回はPhongシェーダをベースに、主にVertexシェーダーをいじってあげることで波打つような動きにします。

```Phong MAT```を選択し、OutputシェーダーボタンからPhongシェーダーベースのVertexシェーダーとPixelシェーダーを作成します。

![geo03.JPG](https://qiita-image-store.s3.amazonaws.com/0/82250/3baff91c-bb05-3aed-bd76-10db300efd35.jpeg)

こちらの左側が標準のPhongシェーダー、右側が今回用に実装したy軸縦方向に**ニュッ**と伸びる処理を描いたシェーダーです。

![geo04.JPG](https://qiita-image-store.s3.amazonaws.com/0/82250/c43f99b3-b6b3-9d27-263e-306567783da5.jpeg)

コメントアウト分があるのでわかりづらいですが、実際に書き換えているのはほんの数行程です。

# Vertexシェーダー

やっていることを簡単に説明すると、**Geometry instancing**で生成したオブジェクトごと(六角柱1つ)にy軸正方向にスケールをかけて、縦に伸ばしてあげます。

```vertex.glsl
newP.y *= max(0, TDPerlinNoise(TDDeform(vec3(0)).xyz + vec3(0, uTime, 0))) * 200;
```

この時、**生成したオブジェクトごとにランダムな値でスケールをかける**のがポイントです。

```vertext.glsl
TDPerlinNoise(TDDeform(vec3(0)).xyz
```

頂点ごとにスケールをかけるとそれはそれで違った動きをして面白いです。

Vertexシェーダー内で```Patter A,B,C```という形でそれぞれ異なる処理が記述してあるのでコメントアウトを外してそれぞれの挙動を確認してみてください。

**ぬるっ**とした動きは**パーリンノイズ**がポイントです。

# Pixelシェーダー

こちらはworld座標系のy軸の値に基づいて色を変化させています。

```pixel.glsl
diffuseSum *= vec3(0.5, 0.0, iVert.worldSpacePos.y*4.5) * iVert.color.rgb * uDiffuseColor.rgb;
```

この辺は個人の好みの問題だったりするので良い感じに調整してみてください。

# 仕上げ
仕上げとして```SSAO TOP```や```Level TOP```、```Ramp TOP```を使って色味を調整してあげれば完成です。

# 最後に

こんな感じでGLSLを使うことでパフォーマンスを維持しつつ、大量のオブジェクトに対して複雑な動きをつけてあげることが可能です。

TouchDesignerやProcessingを使って作った作品をtwitterにぱらぱらと挙げているのでよろしくお願いします。

[go](https://twitter.com/0505_lab) (twitter)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="und" dir="ltr"><a href="https://t.co/W5ZUnFJPbK">pic.twitter.com/W5ZUnFJPbK</a></p>&mdash; 5O | go (@0505_lab) <a href="https://twitter.com/0505_lab/status/1074222587478077440?ref_src=twsrc%5Etfw">2018年12月16日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">What is Mixed Reality?<br>Made with STYLY<a href="https://twitter.com/hashtag/MR?src=hash&amp;ref_src=twsrc%5Etfw">#MR</a> <a href="https://twitter.com/hashtag/STYLY?src=hash&amp;ref_src=twsrc%5Etfw">#STYLY</a> <a href="https://t.co/P7YWCXzakT">pic.twitter.com/P7YWCXzakT</a></p>&mdash; 5O | go (@0505_lab) <a href="https://twitter.com/0505_lab/status/1073053281608318976?ref_src=twsrc%5Etfw">2018年12月13日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">「Color Of The Year 2019」の「Living Coral」が個人的に超好きなカラーリングだったのでシェーダーで一発<a href="https://twitter.com/hashtag/COY2019?src=hash&amp;ref_src=twsrc%5Etfw">#COY2019</a> <a href="https://twitter.com/hashtag/GLSL?src=hash&amp;ref_src=twsrc%5Etfw">#GLSL</a> <a href="https://t.co/kZiC0iqj7j">pic.twitter.com/kZiC0iqj7j</a></p>&mdash; 5O | go (@0505_lab) <a href="https://twitter.com/0505_lab/status/1071244821648486400?ref_src=twsrc%5Etfw">2018年12月8日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">グラフィックからパターンを見出して数式に落とし込み、それを自分なりに味付けしてグラフィックに落とし込む作業 <a href="https://t.co/UJWNAtp3IL">pic.twitter.com/UJWNAtp3IL</a></p>&mdash; 5O | go (@0505_lab) <a href="https://twitter.com/0505_lab/status/1070327237189107715?ref_src=twsrc%5Etfw">2018年12月5日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/glsl?src=hash&amp;ref_src=twsrc%5Etfw">#glsl</a> <a href="https://t.co/UqbsVUu53O">pic.twitter.com/UqbsVUu53O</a></p>&mdash; 5O | go (@0505_lab) <a href="https://twitter.com/0505_lab/status/1069127082796474368?ref_src=twsrc%5Etfw">2018年12月2日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>