TouchDesignerでトゥーンシェーディング風の表現

[TouchDesigner Advent Calendar 2019](https://qiita.com/advent-calendar/2019/touchdesigner) 19日目の記事にしました。

# はじめに
初のQiita投稿です。お手柔らかにお願いします。


今回はTouchDesignerでできるトゥーンシェーディング風の表現を3つ紹介したいと思います。
【サンプルファイルは[こちら](https://drive.google.com/file/d/1ak33pfMMl3KrrDFtApu5QhrpV5TXLm6m/view?usp=sharing)】


#方法その①　GLSL MAT小技
1つ目はシェーダーを一行も書かないGLSL MAT小技です。

1. phong MATを出す
2. phong MAT→RGBタブの最下部にある"Output Shader..."のところをクリック→OKをクリック
3. 新しくできたphongGLSLのvectors1タブのuShininessの値を100から0に変更

![TouchDesigner 2019.19930_ C__Users_doray_Desktop_NewProject.1.toe_ 2019-12-18 20-30-29_3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529920/907f47dd-5410-8dee-28cf-aef1400fe901.gif)


通常のphong MATだとShininessを1より小さい値に下げることができないので、一度GLSL MATに変換するのが肝です。

一応断っておきますが、これはバグ(?)的な挙動で、まったくもってトゥーンシェーディングではないです・・・（なぜこうなるのかは、[この辺](https://github.com/mrdoob/three.js/issues/6057)ですこし書かれてました。）

ただ使いどころによってそれっぽく見えなくもないというのと、何より早くて簡単というので最初にこの方法を紹介しました。質感の品質的に高いとは言えないので、一発芸にでもどうぞ。



# 方法その②　TOPを用いた方法
2つ目は[こちら](https://www.dailymotion.com/video/x2vfbzt)のビデオチュートリアルで紹介されていた、TOPを用いた方法です。


Threshold TOPでピクセルの色をしきい値によって0/1に振り分け、そのしきい値の段階に応じて色をベタ塗りして最後にComposite TOPのoverで合わせてます。


![top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529920/71c1779d-420b-2c20-2e3c-eaa0a22efad1.png)



このピクセルの色をしきい値によって0/1に振り分け、その段階に応じて色をベタ塗りというのは本来のトゥーンシェーディングのやり方と基本的には同じなので、かなりそれに近い表現になっていると思います。

加えてこちらの方法もコードを書かなくていいので、初心者でもかなりとっつきやすいかなと思います。


# 方法その③　ちゃんとシェーダー書く
![glsl_toonshading.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529920/07c57e10-1c3f-fed5-2935-0cd652dbd8ce.png)



3つ目はトゥーンシェーディング風というより、GLSL MATでコードを書く普通のトゥーンシェーディングです。
諦めてコードを書きましょう。

```pixelshader.glsl

in vec3 worldSpaceNorm;
in vec4 worldSpacePos;

uniform vec3 Kd; // ディフューズ反射率
uniform vec3 Ka; // アンビエント反射率
uniform float dimmer;

int level = 3; //diffuseの計算で使う階調の数。方法その2でいうとThreshold TOPで分けた数
float scaleFactor = 1.0 / level; //diffueの計算で使う階調の値

out vec4 fragColor;
vec3 toonShade()
{
    vec3 viewVec = uTDMats[0].camInverse[3].xyz; //カメラの位置取得
    vec3 lightVec = uTDLights[0].position.xyz; //ライトの位置取得
		
    vec3 s = normalize(lightVec - worldSpacePos.xyz); //光源方向のベクトルsを計算
    float cosine = max(0.0,dot(s,worldSpaceNorm)); //dot関数でsとノーマル(法線)の内積を計算し、max関数で負の値を取り除く
    vec3 diffuse = Kd * floor( cosine * level ) * scaleFactor; //floor関数でcosineとlevelを乗算したものを整数に切り捨て。その後scaleFactorを乗算して再び0～1の値にする。その結果にdiffuse反射率(Kd)を乗算
     
    return vec3(dimmer) * (Ka + diffuse); //アンビエントとdiffuseを合わせたものを、dimmerに乗算してフラグメントの最終的な色を取得
}

void main()
{   
   fragColor = TDOutputSwizzle(vec4(toonShade(), 1.0));
}
```

・コメントを多めに書いたのでわからない方は参考にしてみてください


今回のこちらのコードは『[OpenGL 4.0 シェーディング言語](https://www.amazon.co.jp/OpenGL-%E3%82%B7%E3%82%A7%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E-%E5%AE%9F%E4%BE%8B%E3%81%A7%E8%A6%9A%E3%81%88%E3%82%8BGLSL%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-David-Wolff/dp/4862461891)』で紹介されていたトゥーンシェーディングのサンプルをTouchDesigner用に移植したものです。



その他にもTouchDesigner上でのトゥーンシェーディングは[こちら](https://www.nogson.blog/entry/2018/10/21/164200)のブログでも紹介されてました。ほぼほぼ同じですが、微妙に異なるところもあるので今回紹介したコードと見比べてみるのもいいかもしれないです。


# まとめ
ここまでトゥーンシェーディング的な表現方法を3つ紹介してきました。
出力結果を比較するとこんな感じです。
※輪郭線は別途Edge TOPで追加しました。

方法その①
![sample1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529920/fc4e5a83-7c36-7817-1a37-ffa8dd19c8cf.jpeg)


方法その②
![amend2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529920/b3e69159-f8fa-6937-682e-886dfcad97ab.jpeg)


方法その③
![sample3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529920/9f13f275-ae3d-56e1-6f34-e38a9e03721d.jpeg)


微妙に質感の異なるトゥーンシェーディング的な表現が3つできました。方法その②と③で見た目的にほとんど違いがないだけに、方法その①のなんちゃって感が目立ちますね。

このようにTouchDesignerなどの自由度や柔軟性が高い環境では、何か一つの表現をとっても、そこに至る方法は複数存在します。
**やりたい（表現したい）ことや状況に応じて、それに合わせた最適な方法をとっていけたらいいなと思いました。**