Motivation

TouchDesigner に Touch しないで Emacs で使いたい。

Python インターフェース

td Module - TouchDesigner 099 Wiki

TouchDesigner には Python 向けのモジュール td が用意されているので、これを利用して Emacs で作業を完結させる仕組みを検討します。td は TouchDesigner の起動時に実行される TouchInit.py で自動的に読み込まれるため import は不要で、sys, re, math, inspect, warnings, collections などの基本的なモジュールも同様です。

まずは、下図のような入力映像を加工し出力する構成を Python から生成します。

Texture の作成

入力映像と GLSL の TOP を作成して接続。

$ emacs = op('/emacs')
$ vdi = emacs.create(videodeviceinTOP)
$ gls = emacs.create(glslTOP)
$ gls.inputConnectors[0].connect(vdi)

Uniform の定義

入力テクスチャの解像度を GLSL に渡すための uniform 変数。

$ gls.par.uniname0 = 'resolution'
$ gls.par.value0x.expr = "op('%s').width" % vdi.name
$ gls.par.value0y.expr = "op('%s').height" % vdi.name

GLSL の同期

glslTOP 生成時の Pixel Shader が textDAT のため fileinDAT に変更してシェーダのファイルパスを登録。

$ fii = op(gls.par.pixeldat).changeType(fileinDAT)
$ fii.par.file = 'emacs.glsl'
$ fii.par.refresh = True

emacs.glsl

シェーダファイルは入力をそのまま出力する処理で一旦作成。

out vec4 fragColor;
void main()
{
    fragColor = texture(sTD2DInputs[0], vUV.st);
}

Perform Mode の切替

Perform mode - TouchDesigner 099 Wiki

Perform Mode 用のウインドウを作成して登録。

$ emacs.create(windowCOMP).par.winop = gls
$ ui.performMode = True

Python の実行

ここまでのスクリプトを Emacs で記述し TouchDesigner で実行します。この結果 TouchDesigner は出力結果のウインドウのみになるため、以降は Touch しないで Emacs から GLSL を変更し Design を確認する環境になります。

emacs = op('/emacs')
vdi = emacs.create(videodeviceinTOP)
gls = emacs.create(glslTOP)
gls.inputConnectors[0].connect(vdi)
gls.par.uniname0 = 'resolution'
gls.par.value0x.expr = "op('%s').width" % vdi.name
gls.par.value0y.expr = "op('%s').height" % vdi.name
fii = op(gls.par.pixeldat).changeType(fileinDAT)
fii.par.file = 'emacs.glsl'
fii.par.refresh = True
emacs.create(windowCOMP).par.winop = gls
ui.performMode = True

ポストエフェクトの反映

動作確認のためにシェーダを変更した結果が以下になります。uniform 変数 resolution からは事前に定義しておいた videodeviceinTOP の解像度が参照可能になっており、また fileinDAT による GLSL の同期が確認できます。

uniform vec2 resolution;

out vec4 fragColor;
void main()
{
    mat4x3 m, s;
    const int r = 4 + 1;
    const mat4x2 d = mat4x2(-r, -r, 0, -r, -r, 0, 0, 0);
    for (int u=0; u<r; u++) {
        for (int v=0; v<r; v++) {
            for (int i=0; i<4; i++) {
                vec3 c = texture(sTD2DInputs[0], vUV.st + (vec2(u, v) + d[i]) / resolution.xy).rgb;
                m[i] += c;
                s[i] += c*c;
            }
        }
    }
    m /= r*r;
    s /= r*r;

    vec3 rgb;
    float minL = 1.0;
    for (int i=0; i<4; i++) {
        float l = dot((s[i] - m[i]*m[i]).rgb, vec3(0.30, 0.59, 0.11));
        if (l < minL) {
            rgb = m[i];
            minL = l;
        }
    }

    fragColor = TDOutputSwizzle(vec4(rgb, 1.0));
}