GLSL
TouchDesigner

TouchDesignerでGLSLを扱うには

TouchDesignerでGLSLを扱っている人は多いと思いますが、GLSL事始め的なエントリが少ないことに気づいたのでメモ代わりに書いておきます。
サンプルコードはこちら

TouchDesignerでGLSLを扱うためのノード

TouchDesignerではGLSLを扱うためのノードが3つ用意されています。
GLSL MATとGLSL TOP、GLSLMulti TOPの3つです。

GLSL MAT

GLSL MATは最も一般的なシェーダになります。
TouchDesignerにおけるGeometry COMP、Camera COMP、Light COMPと連携し、Renderへの描画処理を記述していくものです。
記述が簡単になる独自関数やCamera COMPやLight COMPと簡単に連携できる変数がすでに準備されているため、詳細はwikiのWrite a GLSL Materialを読みましょう。

GLSL TOP、GLSLMulti TOP

GLSL TOPはGLSL SandboxやShaderToyのように、普通の板ポリとFragment Shaderだけあれば良い(Camera COMP、Light COMPが要らない)場合に、GLSLをより簡単に記述できるよう作られたノードです。
GLSLMulti TOPはGLSL TOPとほぼ同じなのですが、パッチングできる入力ノードの数が3つ以上になっています(中で書くGLSLは全く同じものになります)。
こちらも便利な独自関数や変数があるので、Write a GLSL TOPを読みましょう。

使い分け

『どう使い分ければいいの?』という話になるでしょうが、以下のポイントをチェックすれば良いかと思います。

GLSL MATが向いている場合

  • Geometry COMP、SOPをGLSLで利用する
  • Light COMP、Camera COMPをGLSLで利用する
  • GPU Instancingやりたい
  • Unityでやってたシェーダ処理を移植したい

GLSL TOP、 GLSLMulti TOPが向いている場合

  • Light COMPやCamera COMPを利用しない
  • Geometryも板ポリで十分
  • ShaderToy、GLSL SandboxのGLSLを移植したい
  • フィルタ処理を移植したい
  • ノイズ画像、グリッチ画像など、平面的な画像を生成したい

TouchDesignerでのGeometry Shaderの追加方法

TouchDesignerではGeometry Shaderも利用可能です。
Geometry Shaderを記述するためのText DATを用意し、それをGLSL MATのGeometry Shaderのボックスにドラッグ&ドロップすることでGeometry Shaderがパイプラインに挿入されます。
Vertex ShaderとPixel ShaderだけだとVertex Shaderから直接Pixel Shaderに値を渡せますが、Geometry Shaderを挟む場合、Geometry ShaderにVertex Shaderから受け取った値をPixel Shaderに渡せるようにちゃんとGLSLに追記を行う必要があります。

TouchDesignerでのGLSLへの変数(Uniform)の扱い方

TouchDesignerではGLSLへの各種変数の受け渡しも可能です。

Vector

GLSLノードのTabを見てもらうとVectorsのタブがあり、そこに値を挿入できます。
exportもしくはpythonで値を書き込み、GLSL上で参照するためのUniform変数名を記述します。
型はfloat、vec2、vec3、vec4の中から選べます。
floatを選ぶと、左端以外の値はGLSLには渡りません。
その後、GLSLで uniform <型名> <変数名>; と呼ぶことでGLSL内で値を参照可能になります。

Array

ArrayはCHOPノードを参照することで利用可能です。
参照したい値をCHOPの配列として作成し、GLSLノードのTabのArraysタブのCHOPのところにドラッグ&ドロップします。
GLSLでの変数名と型(こちらもVectorと同様、float、vec2、vec3、vec4から選べます)を定義します。
それ以外にもArray Typeを設定できるのですが、基本的にはTexture Bufferを利用したほうが良いようです。
違いとしては配列をTextureとして読み込むかUniform変数の文字通り配列として読み込むかの違いなのですが、Uniform変数の配列は遅い、とのこと。
Texture Bufferを利用する場合、texture関数ではなくtexelFetech関数を使って値を抜き出します(texelFetchBuffer関数でも同様のことが可能ですが、こちらはMacOSでは動作しないため、利用しないことをお勧めします)

out vec4 fragColor;

uniform samplerBuffer uMultiplyArray;

void main()
{
    vec4 color = texture(sTD2DInputs[0], vUV.st);
    vec4 multi = texelFetch(uMultiplyArray, int(vUV.x * uTD2DInfos[0].res.z)); 
    // vec4 res = vec4(1/width, 1/height, width, height ) 
    fragColor = TDOutputSwizzle(multi);
}


Sampler2D

ArrayとVectorはGLSL MATとGLSL TOPで扱い方が同じでしたが、Sampler 2Dは2つで扱い方が大きく異なります。
GLSL TOPおよびGLSLMulti TOPではノードをつなぎ、すでにTouchDesignerに組み込まれているオリジナル変数sTD2DInputs[]を使うことで参照可能です。
また、Sampler2DのサイズもuTD2DInfos[]という変数名で取得可能になっています。
非常に便利ですね。
一方、GLSL MATで利用する場合はSamplersタブのTOPの項目に利用したいImage DataのTOPをドラッグ&ドロップし、GLSL上で扱う変数名を指定し、 uniform Sampler2D <変数名>; とすることで利用可能となります。
画像サイズなども合わせて利用する場合はVectorsに画像サイズ情報を設定するなどして別途参照可能にする必要があります。

Multi Buffer

GLSLでの処理結果を複数出力したい場合、Multi Bufferを利用することで出力可能です。
of Color Buffersを1以上に設定することで利用可能になるのですが、GLSL TOPの場合はGLSL TOP内のTabに、GLSL MATの場合はRender TOPのほうに設定項目があります。
また、最後のPixel Shaderにlocationの記述が必要になります。
例えば元絵のRGBを分けて3枚のバッファに出力したい場合、以下のように記述します。

layout (location = 0) out vec4 redColor;
layout (location = 1) out vec4 greenColor;
layout (location = 2) out vec4 blueColor;

void main()
{
    vec4 color = texture(sTD2DInputs[0], vUV.st);
    redColor = TDOutputSwizzle(vec4(color.r, 0, 0, 1));
    greenColor = TDOutputSwizzle(vec4(0, color.g, 0, 1));
    blueColor = TDOutputSwizzle(vec4(0, 0, color.b, 1));
}

最初の結果以外を取得するにはRender Select TOPを利用します。
Multi Bufferの処理をしているTOP(GLSL TOPもしくはGLSL MATを使っているRender TOP)をRender Select TOPで参照させ、出力したいBufferの番号を指定します。

その他、TouchDesignerでGLSLを扱う際に便利な機能

Output Shader機能

TouchDesignerではPhong ShadingとPBR Shadingのテンプレート機能が用意されています。
Phong MATおよびPBR MATのパラメータのOutput Shaderを押すことでPhong ShadingおよびPBR ShadingのGLSLが書き込まれたGLSLを書き出すことができ、編集/追記が可能になります。Geometry Shaderも含めた出力も可能なため、Vertex ShaderおよびGeometry Shaderを使った表現にPBRなどのリッチな質感表現を混ぜることも容易に行えます。

Computer Shader

今回紹介していませんが、GLSL TOPおよびGLSLMulti TOPにはCompute Shaderも利用可能です。
あまり詳細な記述がofficialには無いので、satoruhigaさんのエントリなどが参考になるかと思います。