TouchDesignerでGeometryShader

TouchDesignerではジオメトリシェーダもサポートされています。使ってみましょう！

<img width="1677" alt="スクリーンショット 2018-02-27 17.04.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/102231/0f1f9308-a61e-f9ac-8c54-bdc6689ad5c3.png">


## **準備**
とりあえず適当に`Sphere`を出します。`Sphere`のPrimitive TypeはPolygonにしておきます。

![スクリーンショット 2018-02-27 17.14.08.png](https://qiita-image-store.s3.amazonaws.com/0/102231/364eb680-740a-6a28-d72c-70104701538b.png)

## **VertexShaderとFragmentShader**
次に`GLSL MAT`を`Geo COMP`にマテリアルとして設定します。その後、GLSLで記述したシェーダーを格納する`Text DAT`を3つ作り、それぞれVertex、Geometry、Fragmentと名前を変更して`GLSL MAT`のパラメーターに紐付けます。

![スクリーンショット 2018-02-27 17.32.08.png](https://qiita-image-store.s3.amazonaws.com/0/102231/f7985243-ca1a-8c1d-7f73-8ea28eb49cf1.png)

ここまで出来たら、バーテックスシェーダとフラグメントシェーダを記述していきましょう。（Phong MATなどからOutputしたもので全然OKですが、ジオメトリシェーダがメインなので最小限にしています。）


```glsl:Vertex
out int cameraIndex;

void main(){
    cameraIndex = TDCameraIndex();
    gl_Position = TDDeform(P);
}
```

```glsl:Fragment
out vec4 fragColor;

void main(){
    fragColor = vec4(1.0);
}
```

## **GeometryShaderを記述する**
昔のパイプラインでは、頂点はCPU側で用意するものであり、GPU側でその数を増やしたり減らしたりすることはできませんでした。そこで、この頂点を増減する機能をGPU上で実現するシェーダーステージが組み込まれました。それがジオメトリシェーダになります。頂点の増減によって、実質的にプリミティブを操作することができます。

```glsl:Geometry
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

in int[] cameraIndex;

void main(){
    vec3 P0 = gl_in[0].gl_Position.xyz;
    vec3 P1 = gl_in[1].gl_Position.xyz;
    vec3 P2 = gl_in[2].gl_Position.xyz;
    
    gl_Position = uTDMats[cameraIndex[0]].worldCamProj * vec4(P0, 1.0);
    EmitVertex();

    gl_Position = uTDMats[cameraIndex[1]].worldCamProj * vec4(P1, 1.0);
    EmitVertex();
    
    gl_Position = uTDMats[cameraIndex[2]].worldCamProj * vec4(P2, 1.0);
    EmitVertex();
}
```
冒頭、1行目の`layout(〜) in;`の箇所ではジオメトリシェーダに入ってくるプリミティブを、2行目`layout(〜) out;`では出力するプリミティブを指定します。そして、`gl_in[i].gl_Position`で入力されたプリミティブのPosition情報を取り出しています。

パイプラインの中でジオメトリシェーダのステージは、バーテックスシェーダとフラグメントシェーダの間に位置しています。そのため、バーテックスシェーダでは座標変換(TDでは`TDWorldToProj()`)をせずに、そのままジオメトリシェーダへPosition情報を送ります。最後に、`gl_Position`へ座標変換行列をかけたPosition情報をセットし、`EmitVertex()`で頂点を次のステージへと送り出します。上記では、そのままPositionの情報を受け流しただけなので見た目には変化が起きていません。

<img width="1678" alt="スクリーンショット 2018-03-01 17.17.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/102231/ba283423-3ce9-df0e-d57d-a963571aa88b.png">

## **GeometryShaderでポリゴンに分割して動かす**

では、今回はHello Worldとしてポリゴンに分割し法線方向に動かしてみます。

```glsl:Geometry
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

uniform float time;
uniform float dispScale;

in int[] cameraIndex;

void main(){
	
    vec3 P0 = gl_in[0].gl_Position.xyz;
    vec3 P1 = gl_in[1].gl_Position.xyz;
    vec3 P2 = gl_in[2].gl_Position.xyz;

    vec3 d0 = P0 - P1;
    vec3 d1 = P2 - P1;

    for(int i = 0; i < 3; i++){
        vec4 P = gl_in[i].gl_Position;
        vec3 N = normalize(cross(d1, d0));
        
        float disp = 2.0 + sin(time * 5.0) * dispScale;     
        P += vec4(P.xyz + N * disp, 1.0);
        
        gl_Position = uTDMats[cameraIndex[i]].worldCamProj * P;
        EmitVertex();
    }
}
```

<img width="1679" alt="スクリーンショット 2018-03-01 12.55.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/102231/334460bf-578a-47b2-02d3-bb5153b4ae8b.png">

ポリゴンが分割された状態で動くようになりました。これで表現の幅を広げていけそうですね！

## **参考**
シェーダの基礎的な知識は[「ゲーム制作者になるための3Dグラフィックス技術」](https://www.amazon.co.jp/%E3%82%B2%E3%83%BC%E3%83%A0%E5%88%B6%E4%BD%9C%E8%80%85%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE3D%E3%82%B0%E3%83%A9%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E3%82%B9%E6%8A%80%E8%A1%93-%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88-%E8%A5%BF%E5%B7%9D-%E5%96%84%E5%8F%B8/dp/4844333542)を一読されるのがおすすめです。TouchDesigner特有のUniformに関しては、公式Wikiの[「Write a GLSL Material」](https://www.derivative.ca/wiki099/index.php?title=Write_a_GLSL_Material#Touchdesigner_specific_Uniforms)をご覧ください。