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


TouchDesigner Advent Calendar 2018 21日目の記事です。

はじめに

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

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

サンプルプロジェクト

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

ゴール

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

Geometry instancing x GLSL TouchDesigner (vimeo)

Geometry instancingとは

簡単に説明すると画面に大量にオブジェクトを描画したい時に、CPU側からの描画呼び出しは1回で同じメッシュのコピーを大量に描画するテクニックです。

これによりドローコールを大幅に抑えることができます。

ゲームではパーティクルや草、木など画面上に大量に必要なものに使われます。

Geometry instancingの準備

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

この画像では「teapotオブジェクト」を「boxの頂点座標の位置」にinstancingしています。

これを「」に変えて、生成位置を「平面上のランダムな位置」にすればフィールドに大量の草を生やすことができると言うわけです。

TouchDesingerでの使い方

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

まず始めにGeometry OPCamera OPLight OPRender TOPを配置し、3Dオブジェクト(torus)を描画します。

これは「大量生成する元となるメッシュデータ」になります。

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

Geometry OPの設定

次にGeometry OPを選択し、InstanceタブInstancingをオンにします。

画面上に何も表示されなくなりますが、これは「生成する位置となる座標データ」を指定していないからです。

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

今回はShere SOPを使用します。

頂点座標を取得するためにSOP to CHOPSphere SOPを設定。

これで下準備終了です。

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

あとはGeometry TOPのInstanceタブを画像のように設定。

Render TOP上で描画結果を確認するとtorusが大きいために重なって表示されているので、大きさを適度なサイズに変更します。

完成

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

動きをつける

これでは地味なので動きをつけてあげます。

Geometry Instancing Demo (vimeo)
Transformの値を変えることによって動きをつけています。

本題

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

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

GLSLとは

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

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

下準備

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

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

ハチの巣模様の平面を作る

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

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

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

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

六角柱の3Dモデル

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

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

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

Let`s Geometory instancing

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

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

GLSLで動きをつける

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

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

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

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

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 TOPLevel TOPRamp TOPを使って色味を調整してあげれば完成です。

最後に

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

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

go (twitter)