第17回: GLSL TOPでシェーダープログラミング
本日のtoeファイル ← ダウンロードしておいてください
GLSLについて
TouchDesignerでGLSLを扱うには大きくわけて2つの方法があります。GLSL TOP と GLSL MATです。今回はGLSL TOP を中心に説明します。

GLSL TOP と GLSL Multi TOP の違い
GLSL TOP を作ろうとすると GLSL Multi TOP というものもあって、どっちを使ったらいいのかと思われるかもしれませんが、両者にものすごく大きな違いはありません。違いは入力の数で、GLSL TOP が最大3つの入力に対応しているのに対して GLSL Multi TOP は入力の数に制限はありません。
この回の中では GLSL TOP のほうを使っていましたが、どちらかというと GLSL Multi TOP のほうがツブシがきくのかな?という印象です。
デフォルトの状態
/project1/GLSL_TOP_BASIC
GLSL TOP を作るとオペレーターが3つ開いた状態で作成されます。glsl1_pixel みたいな名前の Text DAT がGLSL言語を書くためのもので、glsl1_info みたいなのがエラーログなどが出力される Info DAT です。これらの Text DAT を編集しながら Info DAT を見てエラーなどが出ていないかを確認しながら使っていくことになります。

パラメーターウィンドウには、GLSLのバージョンやモード、その他の状態を変更するパラメーターがありますが、基本的にはデフォルトのままで問題ありません。これは、GLSL TOP ではGLSL言語を使って画像を生成したりエフェクトをかけたりといった操作を基本としますが、そこで使われるシェーダーのタイプを Vertex/Pixel Shader で計算するか、Compute Shader で計算するかを選択する項目です。
Compute Shader を使ったほうが新しい機能だし高速に処理できるかと考えがちですが、個人的な感想では Vertex/Pixel Shader を使ったほうがシンプルで高速な場面が多いです。まずは Vertex/Pixel Shader を使ってみて、どうしてもそれで実現できない複雑な計算をしなければいけない時のみ Compute Shdaer を使うといいでしょう。
作成時のデフォルトの状態だと Text DAT の中身は以下のようになっています。
// Example Pixel Shader
// uniform float exampleUniform;
out vec4 fragColor;
void main()
{
// vec4 color = texture(sTD2DInputs[0], vUV.st);
vec4 color = vec4(1.0);
fragColor = TDOutputSwizzle(color);
}
ここで扱われているのは、GLSL言語の中でも ピクセルシェーダー、フラグメントシェーダー と呼ばれるもので、OpenGLでのレンダリングの一番最後のステージの、**色々あったんだけどどういう色で出力する?**という計算をする部分です。
ピクセルシェーダーは出力の1ピクセルごとに計算が走って、最終的に out vec4 fragColor; のように out 修飾子をつけた変数に値を書きこむことでそのピクセルの色が決まります。上記のコードの場合は vec4 color = vec4(1.0); で指定したようにRGBA全ての要素が1、つまり白が出力されます。
では、GLSLコードを以下のように書き換えてみましょう。
// Example Pixel Shader
// uniform float exampleUniform;
out vec4 fragColor;
void main()
{
// vec4 color = texture(sTD2DInputs[0], vUV.st);
vec4 color = vec4(1.0);
color.r = 1.0;
color.g = 0.0;
color.b = 0.0;
fragColor = TDOutputSwizzle(color);
}
この例だとRGBの色情報を手動で書き換えているので、真っ赤な平面が出力されるはずです。
ユニフォーム変数を使う
/project1/UNIFORM_PARAM
GLSLにはユニフォーム変数という概念があります。通常GLSLコードの中で定義された変数を変更するとGLSLプログラムは再度コンパイルしないと有効になりませんが、uniform 指定子をつけた変数に関してはGLSLプログラム外部から動的にパラメーターを変更できるようにする、という仕組みです。TouchDesignerでももちろんこの機能が使えます。
// Example Pixel Shader
// uniform float exampleUniform;
uniform float uColor;
out vec4 fragColor;
void main()
{
// vec4 color = texture(sTD2DInputs[0], vUV.st);
vec4 color = vec4(1.0);
color.rgb = vec3(uColor);
fragColor = TDOutputSwizzle(color);
}
uniform float uColor; という行が追加されています、行頭に uniform 指定子がついているので、これがユニフォーム変数です。プログラムの中では、color.rgb = vec3(uColor); の指定で uColor の色をそのまま出力ピクセルのRGBに指定しています。

ユニフォーム変数は GLSL TOP の Verctors のタブで指定します。Uniform Name の項目に値を渡したいユニフォーム変数名を指定して、Value の項目に値を指定します。今回の場合は Uniform Name には uColor を指定することになります。指定されると、uColorには 0.5 が渡されてグレーの絵が出力されるはずです。
グレーの絵が出力できたら、ユニフォーム変数の指定を変更してRGBの値を自由に設定できるようにしてみてください。
ユニフォーム変数には最大で4要素(vec4) までのパラメーターが一度に渡せます。それ以上の要素を渡したい時には複数のユニフォーム変数を使うか、Uniform Array、Texture Buffer など他の機能を使うことになります。
UVを使う
/project1/UV1_tow_tone

GLSL TOP を使う時には uv の情報が非常に重要になってきます。uvは、画像の左下が (0, 0)、右上が (1, 1) になるような2次元の数字で、今どのピクセルを処理しているのかをシェーダー側で知ることのできる情報です。uvをRGチャンネルに書き込んで確認してみましょう。
// Example Pixel Shader
// uniform float exampleUniform;
out vec4 fragColor;
void main()
{
// vec4 color = texture(sTD2DInputs[0], vUV.st);
vec4 color = vec4(1.0);
color.rgb = vec3(vUV.st, 0.0);
fragColor = TDOutputSwizzle(color);
}
color.rgb = vec3(vUV.st, 0.0); のあたりがuvをrgbに書き込んでいる部分です。

次の例ではuvの値をif文で分岐させて領域を分けています。たとえば u要素が0.5以上か以下か、で出す色を変えることで左半分は白、右半分は黒、といった画像を生成できます。
丸を書いてみる
/project1/UV2_threshold

もうすこし発展させて2D的な距離を扱ってみましょう。
// Example Pixel Shader
// uniform float exampleUniform;
out vec4 fragColor;
void main()
{
// vec4 color = texture(sTD2DInputs[0], vUV.st);
vec4 color = vec4(1.0);
vec2 uv = vUV.st;
vec2 center = vec2(0.5, 0.5);
float v = length(uv - center);
color.rgb = vec3(v);
fragColor = TDOutputSwizzle(color);
}
中心をuv座標の (0.5, 0.5) の点として、その点と現在処置流のuv座標の点との距離を float v = length(uv - center); のような形で取り出します。その値を色情報として出力すると、中心が黒で離れるほどに白くなるグラデーションの円形の画像が得られます。
色の濃さは計算した距離に適当な係数をかけると変えることができます。また、さきほどのuv座標のようにif文で分岐させるとソリッドに塗りつぶすこともできます。
タイリング
/project1/UV3_fract

これまでuvの座標を使って色々してきましたが、uvの座標そのものを変更してエフェクトをかけるアプローチも存在します。たとえばGLSLの fract 関数を使ったテクニックがあります。
fract 関数は、0から1の範囲のみを出力するという関数で、1以上の値が入力されると整数部分を切り捨てて小数部分のみを残すという働きをします。(動作的には mod(x, 1) と同じです)
// Example Pixel Shader
// uniform float exampleUniform;
out vec4 fragColor;
void main()
{
// vec4 color = texture(sTD2DInputs[0], vUV.st);
vec4 color = vec4(1.0);
vec2 uv = vUV.st;
uv = fract(uv * 5);
color.rgb = vec3(uv, 0);
fragColor = TDOutputSwizzle(color);
}
たとえば上記のコードの場合、 uv = fract(uv * 5); の部分でuv座標を5倍するので本来であれば 0 から 5 の範囲にスケーリングされるはずですが、それを fract 関数に通すことで 0から1のループが5回、という変換を行うことができます。
同様にしてさきほど白い丸を出力したコードに対して fract 関数でuvを操作すると、簡単にタイリングされた丸の画像を得ることができます。
テクスチャを使う
/project1/USE_TEXTURE

GLSL TOP では、外部からの入力のテクスチャも簡単に扱えるようになっています。デフォルトで生成されるコードの vec4 color = texture(sTD2DInputs[0], vUV.st); の部分のコメントアウトを外せば1番目に接続されたテクスチャをそのまま表示する動作になります。
// Example Pixel Shader
// uniform float exampleUniform;
out vec4 fragColor;
void main()
{
vec4 color = texture(sTD2DInputs[0], vUV.st);
// vec4 color = vec4(1.0);
fragColor = TDOutputSwizzle(color);
}
テクスチャは texture 関数を使って サンプリング と呼ばれるプロセスを踏むことで色情報が取り出せます。サンプリングをするには、テクスチャのオブジェクトと、テクスチャのどの座標の色を取り出すかの情報が必要になり、それがそれぞれ sTD2DInputs[0] と vUV.st で指定されています。
GLSL TOP の1番目に接続されたテクスチャは sTD2DInputs[0] で取得することができ、2番目だと sTD2DInputs[1]、3番目だと sTD2DInputs[2] といった具合で使用できます。
複数のテクスチャを取得すると画像を合成したり、さまざまな効果を作ることができます。
テクスチャをゆがめる
/project1/DISPLACE
では、複数のテクスチャを使って Displace TOP を再現してみましょう。
Displace TOP は、ソースとなる画像のサンプリングする座標を別のテクスチャ画像の値を使ってオフセットすることで実現されています。一見複雑な処理のようですが、非常にシンプルに実装できます。
// Example Pixel Shader
// uniform float exampleUniform;
out vec4 fragColor;
void main()
{
vec4 disp = texture(sTD2DInputs[1], vUV.st);
vec4 color = texture(sTD2DInputs[0], vUV.st + disp.xy);
fragColor = TDOutputSwizzle(color);
}
まず、 vec4 disp = texture(sTD2DInputs[1], vUV.st); の部分でオフセット用のテクスチャのRGBA値を取り出しています。それを、ゆがめたい画像のuv座標に足すとゆがんだ画像が得られます。vUV.st + disp.xy の部分です。
ゆがみの大きさや粗さなどは2番目に接続された Noise TOP でコントロールされています。
Displace TOP にはもうすこし色々な機能があるのですが、自分でGLSLを使って再現することで内部的にどういう処理が走っているかをイメージできると思います。そうやって内部で起っていることをイメージできると、実際に使用する時にもトラブルシュートが自力ででき、かつ全然違った表現への応用もできるので他のTOPオペレーターの挙動をGLSL TOP を使って再現してみるのはいいトレーニングになるかもしれません。
リファレンス
GLSL TOP を使うにあたって役に立ちそうなリンクをまとめておきます。
Write a GLSL TOP - Derivative
GLSL TOP を使うにあたっての基本的な情報はこちらにまとまっています。
The Book of Shaders - 日本語訳 ピクセルシェーダーを使った表現についてかなり基本的な部分から解説されています。だいぶイカス。日本語訳は kynd さんがやっています。だいぶ異次元な人です
keijiro/ShaderSketches アーティストのKeijiro Takahashiさんの KodeLife という環境用のスケッチ集です。GLSLを使って色々な形状を書くノウハウが滅茶苦茶勉強になります
Shadertoy BETA かなり昔からあるGLSLのスケッチを公開できるサイトです。眺めていておもしろいですが、ゴミっぽいのや異次元なやつがまじっていて玉石混交状態なのでそんなに参考にはならないです。