TouchDesignerでGLSL入門

#この記事
VJでTouchDesigner初心者のわたくし、さのかずや（Twitter:[@sanokazuya0306](https://twitter.com/sanokazuya0306)）が、TouchDesignerでGLSLを書く場合どんな感じになるんや、ということを書き記します。
日本語リファレンスがなかったので。ほぼ公式の動画やリファレンスの翻訳＋要約です。

GLSLはほとんど書いたことありません。uniformとかvec4とかをかろうじて知ってるくらい。
シェーダープログラミングについて詳しく知りたい方はAyumu Nagamatsu兄さんの記事がとっても勉強になりますので一読をオススメ。
[シェーダープログラミングの意義とその実装 | 永松 歩 | Ayumu Nagamatsu](http://ayumu-nagamatsu.com/archives/587/)


環境はTouchDesigner 099、MacBook Pro Mid 2014を使用。

#まずいじる
[GLSL in Touch Designer - Lesson 1](https://www.youtube.com/watch?v=Q_SMhXipqZY)の内容。
GLSL TOPを立てて、色が変わるだけのFragment Shaderのパラメータをいじれるようにする。

## GLSL TOPをつくる
まず適当に、GLSL TOPをつくる。
<img width="1433" alt="スクリーンショット 2017-08-26 22.57.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/77925/8bbf1b0a-324b-360e-4b2a-f909fe91bdf7.png">
初期設定ではGLSL TOPの中身は真っ白になっている。GLSL TOPに紐付いているText DAT（glsl1_pixel）の中身は下記のようになっているはず。

```GLSL:glsl1_pixel
// 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);
}
```

これは色の設定が`vec4 color = vec4(1.0);`によって、16進数RGBAの(255,255,255,255)の状態になっているため。`vec4 color = vec4(0.5);`とか`vec4 color = vec4(1.0, 0.0, 0.0, 1.0);`とかにすれば色が変わる。


##パラメータをアサインしていじれるようにする

myColorという変数を置き、その変数をParametersウィンドウからいじれるようにする。
まずコードを下記のように書き換える。

```GLSL:glsl1_pixel
// Example Pixel Shader

// uniform float exampleUniform;
uniform vec4 myColor;

out vec4 fragColor;
void main()
{
    // vec4 color = texture(sTD2DInputs[0], vUV.st);
    // vec4 color = vec4(1.0);
    vec4 color = myColor;	
    fragColor = TDOutputSwizzle(color);
}
```

次にParametersのVectors 1のUniform NameにmyColorと記入。
<img width="501" alt="スクリーンショット 2017-08-27 2.13.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/77925/66842f3e-fcb3-2373-bd35-f362e7d248fc.png">

そうするとParametersウィンドウからいじれるようになります。

![tdtest1.gif](https://qiita-image-store.s3.amazonaws.com/0/77925/2fef7704-54ba-b3b3-f266-bb7f9679ee76.gif)

absTime.frameとMultiply TOPを使えば[綿貫くん](https://twitter.com/watakemi725)もギラギラします。チャラい！

![ダウンロード.gif](https://qiita-image-store.s3.amazonaws.com/0/77925/ba19086b-075d-3c02-b18f-938bbdcef2bb.gif)

###小ネタ
<img width="448" alt="スクリーンショット 2017-08-27 3.13.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/77925/c2e77ac7-ccd0-b8a3-d092-27a7f4ebd393.png">
info DATを使うとShaderのコンパイル状況を確認できます。

<img width="566" alt="スクリーンショット 2017-08-27 3.02.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/77925/dbe8982a-9efe-0346-4ca3-8be52d87e0f6.png">
TOP to CHOPを使うとパラメータが可視化できます。CropをPixel(U,V)にすると便利。


#inputを使ってあれこれ
[GLSL in Touch Designer - Lesson 2](https://www.youtube.com/watch?v=863TeJRaxbc)の内容。



##GLSL TOPのinputを使う
最初から入っている`vec4 color = texture(sTD2DInputs[0], vUV.st);`のコメントアウトを外し、`vec4 color = myColor;`をコメントアウトする。

```GLSL:glsl1_pixel
// Example Pixel Shader

// uniform float exampleUniform;
// uniform vec4 myColor;

out vec4 fragColor;
void main()
{
    vec4 color = texture(sTD2DInputs[0], vUV.st);
    // vec4 color = vec4(1.0);
    // vec4 color = myColor;	
    fragColor = TDOutputSwizzle(color);
}
```
そして何でもいいのでGLSL TOPにinputをつなぐと読み込める。
<img width="782" alt="スクリーンショット 2017-08-27 3.06.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/77925/56696635-0085-b9f7-ceaa-27495e4a3d95.png">

`sTD2DInputs[0]`は一番上のinputに繋いだものが出る。`sTD2DInputs[1]`は上から２番め。
下記のようにして2つの入力をいじくることもできる。

```GLSL:glsl1_pixel
uniform float blend;

out vec4 fragColor;
void main()
{
    vec4 input1 = texture(sTD2DInputs[0], vUV.st);
    vec4 input2 = texture(sTD2DInputs[1], vUV.st);

    fragColor = blend*input1 + (1-blend)*input2;
}
```
<img width="1159" alt="スクリーンショット 2017-08-27 4.56.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/77925/5f669d4c-6fbf-aba8-8257-beea3980f79c.png">

こんな感じ。四則演算できるからAdd TOPとかCross TOPよりカスタマイズできる。


# inputにShaderを適用する
ここからが本番。
[GLSL in Touch Designer - Lesson 3](https://www.youtube.com/watch?v=s5ZNmm-rGsY)の内容。

## いい感じのShaderを探す
[Shadertoy](https://www.shadertoy.com/)という、いろんなShaderが共有されまくっている神サイトがあるのでdigしてみましょう。

上記のLessonではこんなシンプルなやつを使っている。
[Simple Radial Blur](https://www.shadertoy.com/view/XsfSDs)
ちょっとつまんないのでこっちを使ってみる。
[edge glow](https://www.shadertoy.com/view/Mdf3zr)

## ShaderをTouchDesignerに最適化する
ものによってはここが手強いのでがんばりましょう。

まずはShadertoyからGLSL TOPの下のText DATにコピペ。

```GLSL:edgeglow
float d;

float lookup(vec2 p, float dx, float dy)
{
    vec2 uv = (p.xy + vec2(dx * d, dy * d)) / iResolution.xy;
    vec4 c = texture(iChannel0, uv.xy);
	
	// return as luma
    return 0.2126*c.r + 0.7152*c.g + 0.0722*c.b;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    d = sin(iTime * 5.0)*0.5 + 1.5; // kernel offset
    vec2 p = fragCoord.xy;
    
	// simple sobel edge detection
    float gx = 0.0;
    gx += -1.0 * lookup(p, -1.0, -1.0);
    gx += -2.0 * lookup(p, -1.0,  0.0);
    gx += -1.0 * lookup(p, -1.0,  1.0);
    gx +=  1.0 * lookup(p,  1.0, -1.0);
    gx +=  2.0 * lookup(p,  1.0,  0.0);
    gx +=  1.0 * lookup(p,  1.0,  1.0);
    
    float gy = 0.0;
    gy += -1.0 * lookup(p, -1.0, -1.0);
    gy += -2.0 * lookup(p,  0.0, -1.0);
    gy += -1.0 * lookup(p,  1.0, -1.0);
    gy +=  1.0 * lookup(p, -1.0,  1.0);
    gy +=  2.0 * lookup(p,  0.0,  1.0);
    gy +=  1.0 * lookup(p,  1.0,  1.0);
    
	// hack: use g^2 to conceal noise in the video
    float g = gx*gx + gy*gy;
    float g2 = g * (sin(iTime) / 2.0 + 0.5);
    
    vec4 col = texture(iChannel0, p / iResolution.xy);
    col += vec4(0.0, g, g2, 1.0);
    
    fragColor = col;
}
```

<img width="891" alt="スクリーンショット 2017-08-27 5.12.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/77925/87a522d9-2e89-5794-a38f-725d608ad1f9.png">
まあこんな感じでエラー出ちゃって動かないはず。
**info DATを使いながらひたすらデバッグしましょう。**

注目ポイントは以下の３つ。

### ①void main()

`void mainImage( out vec4 fragColor, in vec2 fragCoord )`を、
`void main()`に書き換える。
中身の`out vec4 fragColor, in vec2 fragCoord`は、下記のように改行して一番上の行に入れておく。

```GLSL:edgeglow
out vec4 fragColor;
in vec2 fragCoord;
```

### ②変数を外に出す

じっくり見ると、ここで対応が必要なのは`iResolution`、`iChannel0`、`iTime`の3つだけ。

- `iResolution`は解像度なのでuniformで出して別途入力。
- `iChannel0`は`sTD2DInputs[0]`に置き換えればOK。
- `iTime`はTouchDesigner内のTimeCodeと合わせればよいので、こちらもuniformで出して最初の値に水色で`me.time.frame`を入れておく。

[ShaderToyのソースコードをTouchDesignerで使う - Qiita](http://qiita.com/yuichy/items/cb724b2bab887f05e1ae)
こちらを参考にさせて頂きました。矢崎さんあざす！


### ③fragCoord.xy は vUV.xy * iResolution.xy
これでもまだうまくいかない。これはfragCoord.xyのせい。
`fragCoord.xy`はひらたくいうと、**各ピクセルを並列で読み込むための印**なのだが、TouchDesignerでそれに相当するものは`vUV.xy`というやつ。
こいつが少し曲者で、**0-1の間で標準化されている**。

となると解決策は簡単で、**解像度に合わせて引き伸ばせばよし。**
つまり`fragCoord.xy`を`vUV.xy * iResolution.xy`に置き換える。

## 正解者に拍手
```GLSL:edgeglow
out vec4 fragColor;
in vec2 fragCoord;

float d;

uniform vec2 iResolution;
uniform float iTime;

float lookup(vec2 p, float dx, float dy)
{
    vec2 uv = (p.xy + vec2(dx * d, dy * d)) / iResolution.xy;
    vec4 c = texture(sTD2DInputs[0], uv.xy);
	
	// return as luma
    return 0.2126*c.r + 0.7152*c.g + 0.0722*c.b;
}

void main ()
{
    d = sin(iTime * 5.0)*0.5 + 1.5; // kernel offset
    vec2 p = vUV.xy * iResolution;
    
	// simple sobel edge detection
    float gx = 0.0;
    gx += -1.0 * lookup(p, -1.0, -1.0);
    gx += -2.0 * lookup(p, -1.0,  0.0);
    gx += -1.0 * lookup(p, -1.0,  1.0);
    gx +=  1.0 * lookup(p,  1.0, -1.0);
    gx +=  2.0 * lookup(p,  1.0,  0.0);
    gx +=  1.0 * lookup(p,  1.0,  1.0);
    
    float gy = 0.0;
    gy += -1.0 * lookup(p, -1.0, -1.0);
    gy += -2.0 * lookup(p,  0.0, -1.0);
    gy += -1.0 * lookup(p,  1.0, -1.0);
    gy +=  1.0 * lookup(p, -1.0,  1.0);
    gy +=  2.0 * lookup(p,  0.0,  1.0);
    gy +=  1.0 * lookup(p,  1.0,  1.0);
    
	// hack: use g^2 to conceal noise in the video
    float g = gx*gx + gy*gy;
    float g2 = g * (sin(iTime) / 2.0 + 0.5);
    
    vec4 col = texture(sTD2DInputs[0], p / iResolution.xy);
    col += vec4(0.0, g, g2, 1.0);
    
    fragColor = col;
}
```
これでuniformで出した2つのパラメータを、入力データ（今回は1280x720）に合わせて下記のように設定すればいけるはず。
<img width="489" alt="スクリーンショット 2017-08-27 5.23.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/77925/e60d56ee-5f6a-e426-d6fe-e8dd5f021d34.png">

できたやつがコレ。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">深夜に苦節２時間、TouchDesignerでGLSLの読み込みに成功して大喜びのまき <a href="https://t.co/aV7deDhPnD">pic.twitter.com/aV7deDhPnD</a></p>&mdash; さのかずや (@sanokazuya0306) <a href="https://twitter.com/sanokazuya0306/status/901530408734474241">2017年8月26日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

55FPSくらい出る。強い子〜
映像は岐阜県大垣市の大垣駅付近です。大都会！

#快適なTouchDesigner×GLSLライフを
とりあえずひつじさんの[ofxPostGlitch](https://github.com/maxillacult/ofxPostGlitch)を移植したいなーと思ってまーす