1.はじめに

CT部エンジニアの薄葉です。
2025年の10月末にTouchDesignerのPOPsが公式ビルドに加わり、実際の案件でも前向きに取り入れられたり作例が沢山増えてきました。

そんなPOPsの中で一際リファレンスやチュートリアルの少ないオペレーターがGLSL Advanced POPではないかと思います。
GLSL POPやGLSL Copy POPはなんとなく使い方分かるけどGLSL Advanced POPでは何をしたらいい?というような方の一助になればという記事になります。

2.GLSL Advanced POPの特徴

今回の記事の内容から派生して作ったもの↑

GLSL POPでは入力されたオブジェクトのPoint、Vertex、Primitiveを操作でき、GLSL Copy POPでは入力されたPoint(頂点)の位置にオブジェクトを複製し並列で処理することができます。

対してGLSL Advanced POPはPoint、Vertex、Primitiveの操作は勿論、新しく追加することができること、そしてPoints、Lines、LineStrips、Triangle、QuadなどPrimitiveTypeを比較的自由に定義することができます。
次の項目でどのように追加していくかまとめていきます。

3.基本的な実装

GLSL Advanced POPで頂点などを追加する際、GLSLの記述だけでは実現できずパラメーターも適宜変更する必要があります。
今回GLSL Advanced POPの入力に入るオペレーターの流れは共通して以下になります。
PointPOPのパラメーターのCreate Point Primitiveはオフにします。
Create Point Primitiveがオンだとメッシュを貼る際上手くいきません。
GLSL側で色を付ける為、AttributePOPではNew AttributeでColorを追加しています。


・Points
まず最初の一歩、頂点を追加してみましょう。
(1.0,1.0,0.0)の座標に頂点を置いています。

Max Pointsを1に設定
void main() {
    // 現在の処理スレッドのIDを取得
    const uint id = TDIndex();

    // 最初の1点目(IDが0)の場合のみ処理を行う
    if (id == 0) {
        // 位置の設定 
        oTDPoint_P[id] = vec3(1.0, 1.0, 0.0);
    }
}

Lines
頂点を2個追加し、その2点を線でつなぎます。

Max Pointsを2、Max Linesを1に設定

const uint NUM_POINTS = 2;        // 点の数
const uint NUM_PRIMS = 1;         // 線の数(プリミティブ数)
const uint VERTS_PER_PRIM = 2;    // 1つの線を構成する頂点数(始点と終点で2つ)

void main() {
    const uint id = TDIndex();

    // ----------------------------------------------------
    // ポイント属性の処理
    // ----------------------------------------------------
    if (id < NUM_POINTS) {
        // 位置の設定
        if (id == 0) {
            // 始点
            oTDPoint_P[id] = vec3(0.0, 0.0, 0.0);
        } else {
            // 終点
            oTDPoint_P[id] = vec3(1.0, 1.0, 0.0);
        }

        // 色を設定
        oTDPoint_Color[id] = vec4(1.0, 0.0, 0.0, 1.0);
    }

    // ----------------------------------------------------
    // インデックスバッファの処理
    // ----------------------------------------------------
    if (id < NUM_PRIMS) {
        
        uint vertIndexOffset = id * VERTS_PER_PRIM;

        // 線を結ぶための「点のID」を指定
        I[vertIndexOffset]     = 0; // 線の始点は Point ID 0
        I[vertIndexOffset + 1] = 1; // 線の終点は Point ID 1
    }
}

Lineで三角形を作る
3個の頂点を3本の線で繋いで閉じた三角形を作ります。

Max Pointsを3、Max Linesを3に設定
const uint NUM_POINTS = 3;        // 点の数 (0, 1, 2)
const uint NUM_PRIMS = 3;         // 線の数 (3本で閉じる)
const uint VERTS_PER_PRIM = 2;    // 1本の線は2つの頂点でできている

void main() {
    const uint id = TDIndex();

    // ----------------------------------------------------
    // ポイント属性の処理
    // ----------------------------------------------------
    if (id < NUM_POINTS) {
        // 位置の設定 (三角形になるように配置)
        if (id == 0) {
            oTDPoint_P[id] = vec3(0.0, 0.0, 0.0); // 左下
        } 
        else if (id == 1) {
            oTDPoint_P[id] = vec3(1.0, 0.0, 0.0); // 右下
        } 
        else {
            oTDPoint_P[id] = vec3(0.5, 1.0, 0.0); // 上 (頂点)
        }

        // 色を設定
        oTDPoint_Color[id] = vec4(1.0, 0.0, 0.0, 1.0);
    }

    // ----------------------------------------------------
    // インデックスバッファの処理
    // ----------------------------------------------------
    if (id < NUM_PRIMS) {
        
        uint vertIndexOffset = id * VERTS_PER_PRIM;

        // 現在のID
        uint p1 = id;
        
        // 最後の点は0に戻すために「剰余演算(%)」を使います
        // 0 -> 1
        // 1 -> 2
        // 2 -> 0 (3にならず0に戻る)
        uint p2 = (id + 1) % NUM_POINTS;

        I[vertIndexOffset]     = p1; 
        I[vertIndexOffset + 1] = p2; 
    }
}

Line Strips
頂点を4個追加し、その4点を1つの線でつなぎます。

Max Pointsを4、Max Line Stripsを1、Max Line Strip Vertsを4に設定
const uint NUM_POINTS = 4;        // 点の数 (0, 1, 2, 3)
const uint NUM_PRIMS = 1;         // ストリップの数
const uint VERTS_PER_PRIM = 4;    // 1つの線を構成する頂点数

void main() {
    const uint id = TDIndex();

    // ----------------------------------------------------
    // ポイント属性の処理 
    // ----------------------------------------------------
    if (id < NUM_POINTS) {
        // 位置の設定 
        if (id == 0) {
            oTDPoint_P[id] = vec3(0.0, 0.0, 0.0);
        } 
        else if (id == 1) {
            oTDPoint_P[id] = vec3(1.0, 0.0, 0.0);
        } 
        else if (id == 2) {
            oTDPoint_P[id] = vec3(1.0, 1.0, 0.0);
        } 
        else { // id == 3
            oTDPoint_P[id] = vec3(2.0, 1.0, 0.0);
        }

        // 色を設定
        oTDPoint_Color[id] = vec4(1.0, 0.0, 0.0, 1.0);
    }

    // ----------------------------------------------------
    // インデックスバッファの処理 (ストリップの定義)
    // ----------------------------------------------------
    // ストリップは1本だけなので、id=0 の時だけ実行
    if (id < NUM_PRIMS) {
        
        uint vertIndexOffset = id * VERTS_PER_PRIM;

        // LineStripでは、インデックスを順番に並べるだけで自動的に繋がる
        // 0 -> 1 -> 2 -> 3 と繋ぐ
        I[vertIndexOffset]     = 0;
        I[vertIndexOffset + 1] = 1;
        I[vertIndexOffset + 2] = 2;
        I[vertIndexOffset + 3] = 3;
    }
}

三角メッシュを貼る
3個の頂点を繋いでメッシュを貼った三角形を作ります。
以下のコードを見て気付くかもしれませんが、今までと同じで追加した頂点をどの順番で繋ぐかを定義してるだけでメッシュを貼る処理が見当たりません。
これはGLSL Advanced POPのパラメーターに委ねられている部分になります。
これまでMax LinesやMax Line Stripsなど繋ぐのに必要な数を設定していましたが、三角メッシュを作る場合Max Trianglesを1にするだけでGLSL側で繋いだ頂点にメッシュを貼ってくれます。

Max Pointsを3、Max Trianglesを1に設定
const uint NUM_POINTS = 3;        // 点の数 (変更なし)
const uint NUM_PRIMS = 1;         // プリミティブ数は「1つ」の三角形
const uint VERTS_PER_PRIM = 3;    // 1つの三角形は「3つ」の頂点でできている

void main() {
    const uint id = TDIndex();

    // ----------------------------------------------------
    // ポイント属性の処理 
    // ----------------------------------------------------
    // 3つの点の位置を決めます。
    if (id < NUM_POINTS) {
        if (id == 0) {
            oTDPoint_P[id] = vec3(0.0, 0.0, 0.0); // 左下
        } 
        else if (id == 1) {
            oTDPoint_P[id] = vec3(1.0, 0.0, 0.0); // 右下
        } 
        else {
            oTDPoint_P[id] = vec3(0.5, 1.0, 0.0); // 上
        }

        // 色を設定
        oTDPoint_Color[id] = vec4(1.0, 0.0, 0.0, 1.0);
    }

    // ----------------------------------------------------
    // インデックスバッファの処理
    // ----------------------------------------------------
    if (id < NUM_PRIMS) {
        
        uint vertIndexOffset = id * VERTS_PER_PRIM;

        // 三角形を構成する3つの点のIDを書き込みます
        I[vertIndexOffset]     = 0; 
        I[vertIndexOffset + 1] = 1; 
        I[vertIndexOffset + 2] = 2; 
    }
}

四角メッシュを貼る
3個の頂点を繋いだら三角メッシュが貼れたので、4個の頂点を繋げば四角メッシュが貼れます。
こちらもGLSL Advanced POPのパラメーターに委ねられています。

Max Pointsを4、Max Quadsを1に設定
const uint NUM_POINTS = 4;        // 点の数 (0, 1, 2, 3)
const uint NUM_PRIMS = 1;         // 四角形の数 (1枚)
const uint VERTS_PER_PRIM = 4;    // Quadは「4つ」の頂点で1つの面を作る

void main() {
    const uint id = TDIndex();

    // ----------------------------------------------------
    // ポイント属性の処理 
    // ----------------------------------------------------
    if (id < NUM_POINTS) {
        if (id == 0) {
            oTDPoint_P[id] = vec3(0.0, 0.0, 0.0); // 左下
        } 
        else if (id == 1) {
            oTDPoint_P[id] = vec3(1.0, 0.0, 0.0); // 右下
        } 
        else if (id == 2) {
            oTDPoint_P[id] = vec3(1.0, 1.0, 0.0); // 右上
        }
        else {
            oTDPoint_P[id] = vec3(0.0, 1.0, 0.0); // 左上 (id=3)
        }

        // 色を設定
        oTDPoint_Color[id] = vec4(1.0, 0.0, 0.0, 1.0);
    }

    // ----------------------------------------------------
    // インデックスバッファの処理 
    // ----------------------------------------------------
    if (id < NUM_PRIMS) {
        
        uint vertIndexOffset = id * VERTS_PER_PRIM;

        // Quadを構成する4つの点のIDを書き込みます
        // 0 -> 1 -> 2 -> 3 の順で四角形を結びます
        I[vertIndexOffset]     = 0; 
        I[vertIndexOffset + 1] = 1; 
        I[vertIndexOffset + 2] = 2;
        I[vertIndexOffset + 3] = 3;
    }
}

4.応用編

・三角メッシュ2個で四角を作成
三角メッシュを2個作成し四角を作ります。
ポイントは左上と右下の頂点を2個の三角は共有しているところです。
また頂点にColorを2色設定している為メッシュが貼られた時にグラデーションになります。

Max Pointsを4、Max Trianglesを2に設定
const uint NUM_POINTS = 4;        // 点の数 (0, 1, 2, 3)
const uint NUM_PRIMS = 2;         // 三角形の数は「2つ」
const uint VERTS_PER_PRIM = 3;    // 三角形なので頂点は3つ

void main() {
    const uint id = TDIndex();

    // ----------------------------------------------------
    // ポイント属性の処理 
    // ----------------------------------------------------
    if (id < NUM_POINTS) {
        
        // --- 位置の設定 (正方形) ---
        if (id == 0) {
            oTDPoint_P[id] = vec3(0.0, 0.0, 0.0); // 左下
        } 
        else if (id == 1) {
            oTDPoint_P[id] = vec3(1.0, 0.0, 0.0); // 右下
        } 
        else if (id == 2) {
            oTDPoint_P[id] = vec3(0.0, 1.0, 0.0); // 左上
        }
        else { // id == 3
            oTDPoint_P[id] = vec3(1.0, 1.0, 0.0); // 右上
        }

        // --- 色を設定 (対角線で色を変える) ---
        // 左下(0) と 右上(3) を「赤」
        // 右下(1) と 左上(2) を「青」にします
        
        if (id == 0 || id == 3) {
            oTDPoint_Color[id] = vec4(1.0, 0.0, 0.0, 1.0); // 赤
        } 
        else {
            oTDPoint_Color[id] = vec4(0.0, 0.0, 1.0, 1.0); // 青
        }
    }

    // ----------------------------------------------------
    // インデックスバッファの処理 
    // ----------------------------------------------------
    if (id < NUM_PRIMS) {
        
        uint vertIndexOffset = id * VERTS_PER_PRIM;

        if (id == 0) {
            // --- 1つ目の三角形 (左下側) ---
            // 0(赤) -> 1(青) -> 2(青)
            I[vertIndexOffset]     = 0; 
            I[vertIndexOffset + 1] = 1; 
            I[vertIndexOffset + 2] = 2; 
        } 
        else {
            // --- 2つ目の三角形 (右上側) ---
            // 1(青) -> 3(赤) -> 2(青)
            I[vertIndexOffset]     = 1; 
            I[vertIndexOffset + 1] = 3; 
            I[vertIndexOffset + 2] = 2; 
        }
    }
}

・立方体を作成
三角メッシュを12個作成し立方体を作ります。
12個の三角が8個の頂点を共有して繋げていきます。
8個の頂点はそれぞれ座標に応じて色が割り当てられておりカラフルなグラデーションの立方体になります。

Max Pointsを8、Max Trianglesを12に設定
const uint NUM_POINTS = 8;        // 頂点の数 (立方体の角は8つ)
const uint NUM_PRIMS = 12;        // 三角形の数 (6面 × 2枚 = 12)
const uint VERTS_PER_PRIM = 3;    // 三角形なので3頂点

void main() {
    const uint id = TDIndex();

    // ----------------------------------------------------
    // ポイント属性の処理 
    // ----------------------------------------------------
    if (id < NUM_POINTS) {
        vec3 p = vec3(0.0);
        
        // 下の面 (Y = 0)
        if      (id == 0) p = vec3(0.0, 0.0, 0.0); // 左下奥
        else if (id == 1) p = vec3(1.0, 0.0, 0.0); // 右下奥
        else if (id == 2) p = vec3(1.0, 0.0, 1.0); // 右下手前
        else if (id == 3) p = vec3(0.0, 0.0, 1.0); // 左下手前
        
        // 上の面 (Y = 1)
        else if (id == 4) p = vec3(0.0, 1.0, 0.0); // 左上奥
        else if (id == 5) p = vec3(1.0, 1.0, 0.0); // 右上奥
        else if (id == 6) p = vec3(1.0, 1.0, 1.0); // 右上手前
        else              p = vec3(0.0, 1.0, 1.0); // 左上手前 (id=7)

        oTDPoint_P[id] = p;

        // 色を座標に基づいて設定 (XYZ を RGB にマッピング)
        oTDPoint_Color[id] = vec4(p, 1.0); 
    }

    // ----------------------------------------------------
    // インデックスバッファの処理
    // ----------------------------------------------------
    if (id < NUM_PRIMS) {
        
        // 立方体の全インデックスリスト (12枚の三角形 × 3頂点 = 36個)
        const int idxList[36] = int[](
            // 1. 手前の面 (Z=1, ID: 3,2,6,7)
            3, 2, 6,  3, 6, 7,
            // 2. 右の面   (X=1, ID: 2,1,5,6)
            2, 1, 5,  2, 5, 6,
            // 3. 奥の面   (Z=0, ID: 1,0,4,5)
            1, 0, 4,  1, 4, 5,
            // 4. 左の面   (X=0, ID: 0,3,7,4)
            0, 3, 7,  0, 7, 4,
            // 5. 上の面   (Y=1, ID: 7,6,5,4)
            7, 6, 5,  7, 5, 4,
            // 6. 下の面   (Y=0, ID: 0,1,2,3)
            0, 1, 2,  0, 2, 3
        );

        uint vertIndexOffset = id * VERTS_PER_PRIM;
        
        // 配列から該当する3つの頂点IDを取り出して書き込む
        I[vertIndexOffset]     = idxList[id * 3];
        I[vertIndexOffset + 1] = idxList[id * 3 + 1];
        I[vertIndexOffset + 2] = idxList[id * 3 + 2];
    }
}

・立方体をアニメーションさせる
せっかくリアルタイムで動かしているのでアニメーションさせてみます。
Uniform変数としてuHeightを定義し上面の頂点を上下させます。

const uint NUM_POINTS = 8;        // 頂点の数
const uint NUM_PRIMS = 12;        // 三角形の数
const uint VERTS_PER_PRIM = 3;    // 三角形なので3頂点

void main() {
    const uint id = TDIndex();

    // ----------------------------------------------------
    // ポイント属性の処理 
    // ----------------------------------------------------
    if (id < NUM_POINTS) {
        vec3 p = vec3(0.0);
        
        // 下の面 (Y = 0) は固定
        if      (id == 0) p = vec3(0.0, 0.0, 0.0); // 左下奥
        else if (id == 1) p = vec3(1.0, 0.0, 0.0); // 右下奥
        else if (id == 2) p = vec3(1.0, 0.0, 1.0); // 右下手前
        else if (id == 3) p = vec3(0.0, 0.0, 1.0); // 左下手前
        
        // 上の面 (Y = uHeight)
        else if (id == 4) p = vec3(0.0, uHeight, 0.0); // 左上奥
        else if (id == 5) p = vec3(1.0, uHeight, 0.0); // 右上奥
        else if (id == 6) p = vec3(1.0, uHeight, 1.0); // 右上手前
        else              p = vec3(0.0, uHeight, 1.0); // 左上手前 (id=7)

        oTDPoint_P[id] = p;

        // 色を設定 (高さが変わると色も変わる)
        oTDPoint_Color[id] = vec4(p, 1.0); 
    }

    // ----------------------------------------------------
    // インデックスバッファの処理
    // ----------------------------------------------------
    if (id < NUM_PRIMS) {
        
        const int idxList[36] = int[](
            // 1. 手前の面
            3, 2, 6,  3, 6, 7,
            // 2. 右の面
            2, 1, 5,  2, 5, 6,
            // 3. 奥の面
            1, 0, 4,  1, 4, 5,
            // 4. 左の面
            0, 3, 7,  0, 7, 4,
            // 5. 上の面
            7, 6, 5,  7, 5, 4,
            // 6. 下の面
            0, 1, 2,  0, 2, 3
        );

        uint vertIndexOffset = id * VERTS_PER_PRIM;
        
        I[vertIndexOffset]     = idxList[id * 3];
        I[vertIndexOffset + 1] = idxList[id * 3 + 1];
        I[vertIndexOffset + 2] = idxList[id * 3 + 2];
    }
}

・GLSL Copy POP風な実装
入力された頂点の位置に立方体を作成し、CHOPの値をArray(配列)として読み込み各々上面が上下するようにします。

ArraysでCHOPの値を配列として使えるよう設定
#define CUBE_WIDTH 1.0        // 横幅と奥行きのサイズ
const uint NUM_CUBE_POINTS = 8;    // 頂点数
const uint NUM_CUBE_PRIMS = 12;    // 三角形数

// 色の配列
const vec4 instanceColors[4] = vec4[](
    vec4(1.0, 0.1, 0.1, 1.0), // 赤
    vec4(0.1, 1.0, 0.1, 1.0), // 緑
    vec4(0.1, 0.1, 1.0, 1.0), // 青
    vec4(1.0, 1.0, 0.1, 1.0)  // 黄
);

void main() {
    const uint id = TDIndex();
    
    uint numInputPoints = TDInputNumPoints(0); 
    
    const uint numPointsPerInstance = NUM_CUBE_POINTS; 
    const uint numPrimsPerInstance = NUM_CUBE_PRIMS;   
    
    uint numOutputPoints = numInputPoints * numPointsPerInstance; 
    uint numOutputPrims  = numInputPoints * numPrimsPerInstance;

    // ----------------------------------------------------
    // ポイント属性の処理 
    // ----------------------------------------------------
    if (id < numOutputPoints) {
        
        // 現在処理しているのが「何番目の立方体か」
        uint instanceIndex = id / numPointsPerInstance; 
        
        // 立方体の中の「どの頂点か」
        uint pointIndexInInstance = id % numPointsPerInstance; 

        // 入力ポイント(インスタンスの基準位置)を取得
        vec3 centerPos = TDInPoint_P(0, instanceIndex, 0);

        // 高さの取得
        // インスタンス番号(instanceIndex)を使って、uHeightから個別の高さを取得
        float h = texelFetch(uHeight, int(instanceIndex)).x;

        // --- ローカル座標の計算 ---
        float w = CUBE_WIDTH * 0.5; // 中心から左右への幅
        vec3 localPos = vec3(0.0);

        // 下の面 (Y = 0.0 固定)
        if      (pointIndexInInstance == 0) localPos = vec3(-w, 0.0, -w); // 左下奥
        else if (pointIndexInInstance == 1) localPos = vec3( w, 0.0, -w); // 右下奥
        else if (pointIndexInInstance == 2) localPos = vec3( w, 0.0,  w); // 右下手前
        else if (pointIndexInInstance == 3) localPos = vec3(-w, 0.0,  w); // 左下手前
        
        // 上の面 (Y = h で変動)
        // texelFetchで取得した h を使用
        else if (pointIndexInInstance == 4) localPos = vec3(-w, h, -w); // 左上奥
        else if (pointIndexInInstance == 5) localPos = vec3( w, h, -w); // 右上奥
        else if (pointIndexInInstance == 6) localPos = vec3( w, h,  w); // 右上手前
        else                                localPos = vec3(-w, h,  w); // 左上手前

        // 基準位置にローカル座標を加算
        oTDPoint_P[id] = centerPos + localPos; 
        
        // 色を設定
        uint colorArrayIndex = instanceIndex % 4; 
        oTDPoint_Color[id] = instanceColors[colorArrayIndex]; 
    }
    
    // ----------------------------------------------------
    // インデックスバッファの処理 
    // ----------------------------------------------------
    if (id < numOutputPrims) {
        
        uint instanceIndex = id / numPrimsPerInstance; 
        uint primIndexInInstance = id % numPrimsPerInstance; 
        
        uint vertIndexOffset = id * 3;
        uint startVertId = instanceIndex * numPointsPerInstance;

        const int idxList[36] = int[](
            3, 2, 6,  3, 6, 7, // 手前
            2, 1, 5,  2, 5, 6, // 右
            1, 0, 4,  1, 4, 5, // 奥
            0, 3, 7,  0, 7, 4, // 左
            7, 6, 5,  7, 5, 4, // 上
            0, 1, 2,  0, 2, 3  // 下
        );

        I[vertIndexOffset]     = startVertId + idxList[primIndexInInstance * 3];
        I[vertIndexOffset + 1] = startVertId + idxList[primIndexInInstance * 3 + 1];
        I[vertIndexOffset + 2] = startVertId + idxList[primIndexInInstance * 3 + 2];
    }
}

5.まとめ

GLSL Advanced POPを使ってみた感想ですが、作りたいOutputが頭の中にある状態でないと使うのが難しいオペレーターに感じました。
記事を読んで感じたかと思いますが、他のPOPのオペレーターが強力な為、簡単な表現はわざわざGLSL Advanced POPを使わなくても他で代替できてしまう為です。

ただし、追及すれば独自の面白い表現が作れる可能性も十分秘めているので、個人的には今後も表現に取り入れていきたいなという思いです。

以上GLSL Advanced POPに関する記事でした。



■ワントゥーテンでは中途採用募集中です!

1→10(ワントゥーテン)のカルチャーや、作品のクリエイティブに共感し、自身のより高い成長を求めている方からのご応募をお待ちしています!