GLSLでモデルさんにリアルタイムでかけたエフェクトまとめ

<!-- ================================================= -->
# 1.はじめに
<!-- ================================================= -->
TouchDesignerアドベントカレンダー19日目の記事です。
会社でモデルさんのスチールの撮影現場でリアルタイムでエフェクトかけていくみたいなことをやりました。
制作期間もあまりなくすごい勢いで作りました。
採用されなかったもの含めせっかく作ったのでこの記事でエフェクトの実装部分についてまとめていきます！
ゴリゴリではなくこれからShaderをやってみたい初学者向けな密度かと思います…！
僕もComputeShaderの勉強込みで作りました。
モデルは**KIRARI**さんという方です。
<!-- ================================================= -->
# 2.ComputeShader
<!-- ================================================= -->
今回ComputeShaderを使って4種類ほどエフェクトを作りました。
基本は以下の構成です。
![TouchDesigner 2022.29850_ C__Users_22033_Desktop_テックラボ_2022_Coloso_effect_chromaley.toe_ 2022_12_19 10_33_21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693056/88680fee-3a61-2ead-8131-d776157a1dfe.png)
顔と背景を抜いた素材と`EdgeTOP`で輪郭を抽出した素材、そして`NoiseTOP`の三つが`GLSLMultiTOP`に入力として入ってます。
`LevelTOP`と`BlurTOP`はエフェクトのかかり具合の調整用です。
これらの素材をこねくり回しバリエーションを作っていきました。
元となる考え方はどのエフェクトも同じです。

コードを書く前に`GLSLTOP`,`GLSLMultiTOP`でComputeShaderを扱うためにパラメーターの`Mode`を切り替えます。
ComputeShaderことはじめについては@satoruhigaさんが詳しく記事にまとめてくださっております。
https://qiita.com/satoruhiga/items/13df5e749e0a05850cb3

## 炎っぽいエフェクト
![output.0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693056/fe7b0850-1502-9b36-6ec4-aa26b080ad43.gif)


今回スチール撮影のため必要なかったのですが、`NoiseTOP`の`Transform`に`absTime.seconds`を入れることで気軽にアニメーションさせられます。
実装は以下全文になります。
主に見るところは`vec4 effect`に入ってくる数式です。
バッファに書き込んだ前フレームを`NoiseTOP`の濃淡の分X軸を左右に揺らしつつ、Y軸上方に減算しています。
```GLSL
uniform float uTime;
uniform ivec2 uResolution;
uniform float uIsEffect;

layout (local_size_x = 1, local_size_y = 1) in;
void main()
{
	vec2 uv = vec2(float(gl_GlobalInvocationID.x) / uResolution.x, float(gl_GlobalInvocationID.y) / uResolution.y);
    // ノイズテクスチャ
	vec4 noise = texelFetch(sTD2DInputs[2], ivec2(gl_GlobalInvocationID.xy), 0);
    // EdgeTOP入力
	vec4 edge = texelFetch(sTD2DInputs[1], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
    // バッファに書き込んだ前フレームをノイズでズラしていく
	vec4 effect = imageLoad(sTDComputeOutputs[0], ivec2(gl_GlobalInvocationID.x+(noise.r-0.5)*15,gl_GlobalInvocationID.y-noise.r*10));
    // 顔と背景抜いたモデルさん入力
	vec4 color = texelFetch(sTD2DInputs[0], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
    // 最終アウトプットのエフェクト作成
	vec4 outCol = edge*noise.r*2 + (effect*0.9);

	// エフェクトのオンオフ
	if(uIsEffect == 0){
		outCol = color;
	}
	color.a = 1.0;
	imageStore(sTDComputeOutputs[0], ivec2(gl_GlobalInvocationID.xy), TDOutputSwizzle(outCol));
}
```

## ウェーブエフェクト
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693056/9807db73-9b03-1899-dd5a-6b9926827c0e.jpeg" width="400">

輪郭をNoiseテクスチャーの濃淡の分X軸をズラしてウェーブしてるように見せています。
以下エフェクトで使用しているsin関数はnoiseに数値を掛けて大きくなった値を-1~1の中に丸める意味で使用しています。
```GLSL
vec4 edge = texelFetch(sTD2DInputs[1], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
vec4 effect = imageLoad(sTDComputeOutputs[0], ivec2(gl_GlobalInvocationID.x+sin(exp(noise.r*5))*noise.r*20,gl_GlobalInvocationID.y));
vec4 color = texelFetch(sTD2DInputs[0], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
vec4 outCol = edge+(effect*0.9);
```

## 垂れるエフェクト
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693056/8b47270a-cb41-317f-05bc-f357110d0cc6.jpeg" width="400">

輪郭をX軸をウェーブさせながらY軸に加算してエフェクトを垂らしています。

```GLSL
vec4 edge = texelFetch(sTD2DInputs[1], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y-noise.r),0);
vec4 effect = imageLoad(sTDComputeOutputs[0], ivec2(gl_GlobalInvocationID.x-sin(exp(noise.r*5))*uv.s*15,gl_GlobalInvocationID.y+exp(noise.r)*10));
vec4 color = texelFetch(sTD2DInputs[0], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
vec4 outCol = edge+tan(effect*0.9);
```

## 格子エフェクト
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693056/0711e444-5b98-a444-9376-1bafe6f57e5f.jpeg" width="400">

fract関数で格子にしています。
uvを引き延ばしてから戻すイメージです。
こちらはNoiseテクスチャ不使用。
```GLSL
vec4 edge = texelFetch(sTD2DInputs[1], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
vec4 effect = imageLoad(sTDComputeOutputs[0], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y+sin(fract(uv.t*40)-0.5)*20));
vec4 color = texelFetch(sTD2DInputs[0], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
vec4 outCol = edge+(effect*0.9);
```

## 格子エフェクト応用
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693056/1dd90837-2745-8437-20ee-a6ed6c5510d0.jpeg" width="400">

サイズ変更と格子をX軸にも適用して斜めにズラしただけです。
これだけでも印象が変わりますね。
```GLSL
vec4 edge = texelFetch(sTD2DInputs[1], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
vec4 effect = imageLoad(sTDComputeOutputs[0], ivec2(gl_GlobalInvocationID.x+sin(fract(uv.t*10)-0.5)*50,gl_GlobalInvocationID.y+sin(fract(uv.t*10)-0.5)*20));
vec4 color = texelFetch(sTD2DInputs[0], ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y),0);
vec4 outCol = edge+(effect*0.9);
```

<!-- ================================================= -->
# 3.まとめ
<!-- ================================================= -->
以上簡単ではありますが作ったエフェクトについてまとめました。
今年は細々したものを作っていたため記事何にしようか迷いました。
個人の活動としてはVJをやったりARを開発したり色々挑戦ができて面白い一年でしたが来年は何か一つ大きなテーマを持って開発に取り組もうかなと思います。
ここまで読んでいただきありがとうございました！
皆様良いお年を！