1.はじめに
TouchDesignerアドベントカレンダー19日目の記事です。
会社でモデルさんのスチールの撮影現場でリアルタイムでエフェクトかけていくみたいなことをやりました。
制作期間もあまりなくすごい勢いで作りました。
採用されなかったもの含めせっかく作ったのでこの記事でエフェクトの実装部分についてまとめていきます!
ゴリゴリではなくこれからShaderをやってみたい初学者向けな密度かと思います…!
僕もComputeShaderの勉強込みで作りました。
モデルはKIRARIさんという方です。
2.ComputeShader
今回ComputeShaderを使って4種類ほどエフェクトを作りました。
基本は以下の構成です。
顔と背景を抜いた素材とEdgeTOP
で輪郭を抽出した素材、そしてNoiseTOP
の三つがGLSLMultiTOP
に入力として入ってます。
LevelTOP
とBlurTOP
はエフェクトのかかり具合の調整用です。
これらの素材をこねくり回しバリエーションを作っていきました。
元となる考え方はどのエフェクトも同じです。
コードを書く前にGLSLTOP
,GLSLMultiTOP
でComputeShaderを扱うためにパラメーターのMode
を切り替えます。
ComputeShaderことはじめについては@satoruhigaさんが詳しく記事にまとめてくださっております。
https://qiita.com/satoruhiga/items/13df5e749e0a05850cb3
炎っぽいエフェクト
今回スチール撮影のため必要なかったのですが、NoiseTOP
のTransform
にabsTime.seconds
を入れることで気軽にアニメーションさせられます。
実装は以下全文になります。
主に見るところはvec4 effect
に入ってくる数式です。
バッファに書き込んだ前フレームをNoiseTOP
の濃淡の分X軸を左右に揺らしつつ、Y軸上方に減算しています。
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));
}
ウェーブエフェクト
輪郭をNoiseテクスチャーの濃淡の分X軸をズラしてウェーブしてるように見せています。
以下エフェクトで使用しているsin関数はnoiseに数値を掛けて大きくなった値を-1~1の中に丸める意味で使用しています。
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);
垂れるエフェクト
輪郭をX軸をウェーブさせながらY軸に加算してエフェクトを垂らしています。
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);
格子エフェクト
fract関数で格子にしています。
uvを引き延ばしてから戻すイメージです。
こちらはNoiseテクスチャ不使用。
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);
格子エフェクト応用
サイズ変更と格子をX軸にも適用して斜めにズラしただけです。
これだけでも印象が変わりますね。
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を開発したり色々挑戦ができて面白い一年でしたが来年は何か一つ大きなテーマを持って開発に取り組もうかなと思います。
ここまで読んでいただきありがとうございました!
皆様良いお年を!