【GLSL】ISFをTouchDesigner上で動かしてみた


##■概要
この記事は**「[ISF](https://editor.isf.video/)」がまとめられたサイトにあるシェーダーのプログラムコードを、「TouchDesigner」というVJソフトの「GLSL」にぶち込んで動かそう！**ということを書いてます。

ISFとは*`「Interactive Shader Format」`*の略で、僕も知ったばかりで詳しくないのでググってみたところ、**「GLSLベースのシェーダフォーマット」**らしいです。


TouchDesignerでGLSLを勉強していた時、[**TDSWのチュートリアル動画**](https://www.youtube.com/watch?v=YhhWQTBAp8E&list=PLjqkEfiud5LMWrH1rzNFRGMvfycjIOJcr&index=100&t=610s)から「ISF」なるものを知りました。この動画では触れられていない他のシェーダーもいじってみよう！ということで学んだことを試しにやってみることにしました。



##■動作環境
>
MacOS：Big Sur ver.11.2.1
TouchDesigner：099 2020 27390

##■試してみる
うまくいくとこんな感じに動きます
![ezgif-6-398df4cf8a0c.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315671/574f5d1f-6499-8443-a391-c2e9c9942008.gif)

（ちなみにこの画像は`「MINAMI MIYAJIMA」`というスクエアを延々と手書きで書きに書いてる人の作品です。いま`JITSUZAISEI`という[**ただいまクラウドファンディングで60万以上資金調達してるプロジェクト**](https://camp-fire.jp/projects/view/373174?list=projects_fresh)に関わってるんですが，よければ興味持ってください笑 こそっと宣伝です。笑）

では進めていきまする。

###■ISF
まずは、ISFのサイトを開きます。
>
*ISF (Dual Side Scroller And Flip.fs　by VIDVOX)*
https://editor.isf.video/shaders/5e7a7f767c113618206dde08

開くと、こういう画面が現れると思います。
![スクリーンショット 2021-03-28 13.02.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315671/7e19c575-14cb-66c2-e5f6-26b68d891361.png)

そしたら左側にある**” </> ”**のマークを押してください。

するとコードが出てくると思います。`これを全部コピー`してください。
コメントアウトの部分も含めてコピーしてください。
![スクリーンショット 2021-03-28 13.03.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315671/ee739edf-ae90-5a5b-a2e5-f9c5947af064.png)

###■TouchDesigner
タッチデザイナーの方も開いていきます！
開いたら、まず**MovieFileinTOP**を置きます。そこから右につないで**glslTOP**を置きます。

とりあえずは以下の画像のように各オペレーターを配置してください。
![スクリーンショット 2021-03-28 22.13.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315671/af0f8ff7-a6ed-bb30-5722-23f36fb979dc.png)

glslTOPの下に出てきた**glsl_pixel（DAT）**の**Edit**を押してください。

そこにさっきISFのサイトからコピーしたプログラムをコピペします。

```glsl
// uniform float exampleUniform;

一旦、この辺にコピペ！！！！

out vec4 fragColor;
```



###●完成形！
ちなみに，最終的なコードは以下のようなものになります。

```glsl
/*{
  "CREDIT": "VIDVOX",
  "DESCRIPTION": "Mirror slide the top, bottom, left and right sections of the input image. Based on side scroller and flip by Brian Chasalow.",
  "CATEGORIES": [
    "Geometry Adjustment"
  ],
  "INPUTS": [
    {
      "NAME": "inputImage",
      "TYPE": "image"
    },
    {
      "NAME": "slidetop",
      "TYPE": "float",
      "MIN": 0,
      "MAX": 2,
      "DEFAULT": 0
    },
    {
      "NAME": "slideleft",
      "TYPE": "float",
      "MIN": 0,
      "MAX": 2,
      "DEFAULT": 0
    },
    {
      "NAME": "mirrorHorizontaltop",
      "TYPE": "bool",
      "MIN": false,
      "MAX": true,
      "DEFAULT": true
    },
    {
      "NAME": "mirrorVerticaltop",
      "TYPE": "bool",
      "MIN": false,
      "MAX": true,
      "DEFAULT": true
    },
    {
      "NAME": "slidebot",
      "TYPE": "float",
      "MIN": 0,
      "MAX": 2,
      "DEFAULT": 0
    },
    {
      "NAME": "slideright",
      "TYPE": "float",
      "MIN": 0,
      "MAX": 2,
      "DEFAULT": 0
    },
    {
      "NAME": "mirrorHorizontalbot",
      "TYPE": "bool",
      "MIN": false,
      "MAX": true,
      "DEFAULT": true
    },
    {
      "NAME": "mirrorVerticalbot",
      "TYPE": "bool",
      "MIN": false,
      "MAX": true,
      "DEFAULT": true
    }
  ]
}*/



uniform float slidetop;
uniform float slideleft;
uniform bool mirrorHorizontaltop;
uniform bool mirrorVerticaltop;
uniform float slidebot;
uniform float slideright;
uniform bool mirrorHorizontalbot;
uniform bool mirrorVerticalbot;






out vec4 fragColor;
void main()
{
	// vec4 color = vec4(1.0);

	vec2 pt = vUV.st;
	float slide = (vUV.y > 0.5) ? slidetop : slidebot;
	float shift = (vUV.x < 0.5) ? slideleft : slideright;

	bool mirrorHorizontal = (vUV.y > 0.5) ? mirrorHorizontaltop : mirrorHorizontalbot;
	bool mirrorVertical = (vUV.x < 0.5) ? mirrorVerticaltop : mirrorVerticalbot;
	pt.x += slide;
	pt.y += shift;
	vec2 moddedRetard = mod(pt,1.0);
	
	if(mirrorHorizontal && pt.x >= 1.0 && pt.x <= 2.0)
		moddedRetard = vec2(1.0-moddedRetard.x, moddedRetard.y);
	if(mirrorVertical && pt.y >= 1.0 && pt.y <= 2.0)
		moddedRetard = vec2(moddedRetard.x, 1.0-moddedRetard.y);
	
	vec4 pixel = texture(sTD2DInputs[0], moddedRetard);
	fragColor = pixel;

	// fragColor = TDOutputSwizzle(color);
}
```


---



###●置き換えるコードは？
**＊ここからはたぶんこの記事の肝になります。**

isfからコピペしたコメントアウトのコード下にある**「void main(void){}」の中のプログラム**を，タッチのデフォルトで書かれてた

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

→→→この辺にコピペ！！！

}
```
→→→この辺にコピペ！！！にコピペしときましょう

###●Uniform
タッチのglslでは，uniform変数を使っていろいろできるっぽいです。

>
*GLSLにはユニフォーム変数という概念があります。通常GLSLコードの中で定義された変数を変更するとGLSLプログラムは再度コンパイルしないと有効になりませんが、uniform 指定子をつけた変数に関してはGLSLプログラム外部から動的にパラメーターを変更できるようにする、という仕組みです。TouchDesignerでももちろんこの機能が使えます。*

ってことらしいです。笑

ここではisfからコピペしてきたコメントアウト部分を参考に**"NAME"**を**Uniform変数**に割り当てていきます。

たとえば，

```
{
      "NAME": "slidetop",
      "TYPE": "float",
      "MIN": 0,
      "MAX": 2,
      "DEFAULT": 0
    },
```
これだと，**"TYPE":"float"** で **"NAME":"slidetop"** なので， 

```glsl
uniform float slidetop;
```
に置き換えられます。
（"DEFAULT"の値はまた後で使います）

この作業をすると，

```glsl
uniform float slidetop;
uniform float slideleft;
uniform bool mirrorHorizontaltop;
uniform bool mirrorVerticaltop;
uniform float slidebot;
uniform float slideright;
uniform bool mirrorHorizontalbot;
uniform bool mirrorVerticalbot;
```
が出来上がって，`glsl_info`に出ていたエラーもだいぶ減ったと思います。
（エラーが着実に減ってくとほっとするよね。）

他の置き換え部分は，
**`「isf_FragNormCoord」`** → **`「vUV」`** 
**`「IMG_NORM_PIXEL」`** → **`「texture」`** 
**`「inputImage」`** → **`「sTD2DInputs[0]」`** 
**`「gl_FragColor」`** → **`「fragColor」`** 

です！！！笑

***え？なんでそれを置き換えるってわかるの？***

となった私なので，これらを少し解説していきます。

**「isf_FragNormCoord」**は，
>*これは便利な変数であり、現在のフラグメントの正規化された座標を表します（[0,0]は左下、[1,1]は右上）*

で，**「vUV」**は，
>*uvは、画像の左下が (0, 0)、右上が (1, 1) になるような2次元の数字で、今どのピクセルを処理しているのかをシェーダー側で知ることのできる情報です。*

ってことで**[0,0][1,1]**って表記からわかるように，置き換えれそうですね！笑

**「IMG_NORM_PIXEL」**は，
>*シェーダーでのtexture2D（）またはtexture2DRect（）の呼び出しは、それぞれIMG_NORM_PIXEL（）またはIMG_PIXEL（）に置き換える必要があります。*

**「texture」**は
>*外部からの入力のテクスチャも簡単に扱えるようになっています。デフォルトで生成されるコードの vec4 color = texture(sTD2DInputs[0], vUV.st); の部分のコメントアウトを外せば1番目に接続されたテクスチャをそのまま表示する動作になります。*

ってところから，ともにテクスチャを扱うときのコードなんですかね？笑


**「sTD2DInputs[0]」**は，glslTOPのオペレーターを繋ぐところ（今回ならmoviefileinTOPと繋がっている）が関係していて，3口あるので[0]〜[2]までできます。


このそれぞれの置き換えについて，記事にする身としてはちゃんと調べようと思って，この記事がすごく参考になりましたのでリンク共有しときます。

>*映像音響処理概説 2018 第十七回 GLSLについて*
http://satoruhiga.com/TDWS2018/day17/

>*ISF Variables Reference | ISF Documentation*
https://docs.isf.video/ref_variables.html#automatically-declared-variables
https://docs.isf.video/ref_converting.html



これ一読するだけでもGLSLを理解しやすくなると思います！


あとはデフォルトでタッチの方にあった

```glsl
// vec4 color = texture(sTD2DInputs[0], vUV.st);
vec4 color = vec4(1.0);
fragColor = TDOutputSwizzle(color);
```
はここではもう使わないので，コメントアウトするなり消すなりしておきましょう。



最初はエラーを吐きまくっていたと思うのですが、
最終的にはこのようにglsl_info(DAT)でエラーがなくなれば成功です！
![スクリーンショット 2021-03-28 22.22.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315671/e855b99a-a0c0-ed48-78fd-b9b08ae26fc1.png)


####●glslTOP
あともう一踏ん張りです！
![スクリーンショット 2021-03-28 13.07.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315671/dc7ea270-a1f1-46cc-24fc-1dcb0d272055.png)
**`glslTOP/Load Umiform NamesのLoad`**（この画像の真ん中ら辺にあるボタン）を押してあげると，

![スクリーンショット 2021-03-28 13.07.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315671/39280b55-9baf-ee09-86a8-6553e0c3e25d.png)
こんな感じで，**Uniform変数**がずらっと出てくるはずです。
あとはあの`"DEFAULT"`の値を,`Vectors/Value/value x`に入れていけばOK！
(名前が似ててややこしいから気をつけてね)

```
 "NAME": "DEFAULT":
"slidetop"  0
"slideleft"  0
"mirrorHorizontaltop" true（1）
"mirrorVerticaltop" true(1)
"slidebot" 0
"slideright" 0
"mirrorHorizontalbot" true(1)
"mirrorVerticalbot" true(1)
```
(trueは１，falseは０なので，それで対応した数字を入れてます。)



#ここまでお疲れ様でした！🎉
無事にプログラムを実行できたことを祈っています！

あとは自由に，TOPとかで画像を加工したり，LFO CHOPで動きを自動にしたり，画像を差し替てみたり，コードいじってみたり，違うISFのコードを試してみたり，いろいろ遊んでみてください！



##■まとめ
基本的にはエラーを読んで置き換えていく流れになると思います。

こういう`プログラム言語の翻訳`みたいなことって，**「四角を書きたい」**という本質に対して「javaだったらこう書く」「htmlならこう書く」「glslならこう書く」みたいな感覚ってのがありますよね。

以前にGoogleのColab上でTensorFlowを使って機械学習かじってた時に，これTouchDesigner上で動かせたら楽しそうだな，と思って試してみてたところ，NVIDIAのGPUじゃないと動かなかったり，なんでかわかんないけどライブラリが読み込めなかったり，今回やったような`プログラム言語の翻訳`みたいなことが全然できなくてうまくいかなかった経験があったので，今回はその翻訳がうまくいってよかった。**求めていれば，違う形で叶うことがある**って感じがした。

それも，師匠や正しい先生のような，今回ならTDSWのチュートリアル動画で解説されていた[**原田康さん**](https://twitter.com/chimanaco)がいてくれたこそできたことだなと痛感した。**こういったことを学ぼうと思うと、わりと図書館とか大学とかが必要だったのに，今ではそれが大きく変わったように思う。**解説とかチュートリアルって本当にありがたい。だからこそ自分も少しでもこう言った記事を通じてオープンサイエンスに貢献できればなと思う。




twitter:https://twitter.com/jp_dummy
twitter:https://twitter.com/JITSUZAISEI