GLSL
Shader
TouchDesigner

TouchDesigner 頂点テクスチャフェッチを使って波のシミュレーションをする

こんにちは。
人生初のアドベントカレンダー執筆なので、ものすごく緊張しています。
改善点・間違っている点などあったらバシバシご指摘ください。

デモ

今回はこれをつくります。
wave-_1_.gif

ソースはこちら👈

頂点テクスチャフェッチ(VTF)とは

参考 : 頂点テクスチャフェッチ(VTF)
上のページに記載されているように、頂点テクスチャフェッチとはテクスチャの各ピクセルのRGBAにデータを格納して、それを頂点シェーダ内で参照することです。

波動方程式を計算する

ではまず、波動方程式を計算しその結果を格納するテクスチャをつくっていきます。
このテクスチャに格納するデータは
R = 波の高さ
G = 速度
とします。

ノードは以下のように組みます。
feedback1のTarget TOPはnull1 とします。
constant1は黒にし、Common>Pixel Formatを16-bit float(RGBA)に、Common>Resolutionを500, 500に設定します。
スクリーンショット 2017-12-20 18.19.37.png
ここで、constant1は初期値を表しています。
R = 0, G = 0なので、波の高さの初期値 = 0, 速度の初期値 = 0 ということになります。
また、Feedback TOPを使うことで、GLSL内で1フレーム前の計算結果を参照することができるようになります。

では、glsl1のPixel Shaderを記述していきましょう。
参考 : [WebGL] 波動方程式を勉強してみる

precision mediump float;

uniform vec2 resolution;
uniform vec2 mouse;
uniform int isActive;

float k = 0.5;
float attenuation = 0.99;
float range = 0.01;
float force = 0.00003;

out vec4 fragColor;

void main()
{
    vec4 pre_data = texture(sTD2DInputs[0], vUV.st); 
    vec2 pixel = 1.0/resolution;
    float accel = texture(sTD2DInputs[0], vUV.xy + pixel*vec2(0,-1)).x
                + texture(sTD2DInputs[0], vUV.xy + pixel*vec2(0,1)).x
                + texture(sTD2DInputs[0], vUV.xy + pixel*vec2(-1,0)).x
                + texture(sTD2DInputs[0], vUV.xy + pixel*vec2(1,0)).x
                - texture(sTD2DInputs[0], vUV.xy).x * 4.0;

    accel *= k;
    float vel = (pre_data.y + accel) * attenuation;
    float u = pre_data.x + vel;

    if(isActive == 1){
        float len = distance(vUV.xy, mouse);
        if(len < range) u += 1.0/len*force;
    }

    fragColor = TDOutputSwizzle(vec4(u, vel, 0, 1.0));
}

fragColorのRには波の高さ(u)、Gには速度(vel)を格納します。

uniform変数は以下のように設定します。
resolutionはconstant1の解像度に合わせます。
スクリーンショット 2017-12-21 2.28.41.png

mouse, isActiveについては、Panel CHOPのinsideu, insidev, selectチャンネルをエクスポートします。
スクリーンショット 2017-12-20 20.51.27.png

これで、マウスをドラッグしたポイントから波紋がでるようになりました。

Particleを動かしてみる

Particleを動かして計算結果を確認してみたいと思います。

以下のようにノードを組みます。
grid1のRows, Columnsを、先ほどのconstant1の解像度と同じ500に設定します。
また、convert1のConvert toをParticlesにし、geo1のRender>Materialにはglsl2を設定します。
スクリーンショット 2017-12-20 21.07.15.png

次に、先ほど実装した波動方程式の結果をglsl2の頂点シェーダで参照しましょう。
glsl2のuniform変数に、計算結果を格納したnull1を入れます。
スクリーンショット 2017-12-20 21.16.18.png

Vertex Shaderは以下のように記述します。
テクスチャのRに格納した波の高さを、Particleのzに割り当てます。

uniform sampler2D result;
void main() 
{
    float height = texture(result, uv[0].xy).x;
    gl_Position = TDWorldToProj(TDDeform(vec3(P.xy, height)));
}

Pixel Shaderでは適当な色を指定します。

out vec4 fragColor;
void main()
{
    TDCheckDiscard();
    vec4 color =  vec4(0.0, 0.25, 1.0, 1.0);
    TDAlphaTest(color.a);
    fragColor = TDOutputSwizzle(color);
}

最後にカメラの位置を調整し、Open Viewerで波の動きを確認してみてください。

参考

https://wgld.org/d/webgl/w071.html
https://qiita.com/edo_m18/items/e8efac43a93641c37ad2
https://qiita.com/ToyoshiMorioka/items/91c4e1427c2b4296c2a4

最後に

私を含む大学生4人でメディアアートクリエイティブチーム Reflect Initial (リフレクト イニシャル) を立ち上げ、制作活動を行なっています。

私たちはみな大学でメディアアートを専攻しているわけではなく、ただひたすらメディアアートが大好きだという思いから独学で学び制作をしています。もっともっとReflect Initialを知ってもらいたくここで紹介させていただきました。どうか応援よろしくお願いします。

Reflect InitialのTwitterぜひフォローしてください!
Check it out 👉 @refect_toe