TouchDesignerではジオメトリシェーダもサポートされています。使ってみましょう!
準備
とりあえず適当にSphere
を出します。Sphere
のPrimitive TypeはPolygonにしておきます。
VertexShaderとFragmentShader
次にGLSL MAT
をGeo COMP
にマテリアルとして設定します。その後、GLSLで記述したシェーダーを格納するText DAT
を3つ作り、それぞれVertex、Geometry、Fragmentと名前を変更してGLSL MAT
のパラメーターに紐付けます。
ここまで出来たら、バーテックスシェーダとフラグメントシェーダを記述していきましょう。(Phong MATなどからOutputしたもので全然OKですが、ジオメトリシェーダがメインなので最小限にしています。)
out int cameraIndex;
void main(){
cameraIndex = TDCameraIndex();
gl_Position = TDDeform(P);
}
out vec4 fragColor;
void main(){
fragColor = vec4(1.0);
}
GeometryShaderを記述する
昔のパイプラインでは、頂点はCPU側で用意するものであり、GPU側でその数を増やしたり減らしたりすることはできませんでした。そこで、この頂点を増減する機能をGPU上で実現するシェーダーステージが組み込まれました。それがジオメトリシェーダになります。頂点の増減によって、実質的にプリミティブを操作することができます。
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の情報を受け流しただけなので見た目には変化が起きていません。
GeometryShaderでポリゴンに分割して動かす
では、今回はHello Worldとしてポリゴンに分割し法線方向に動かしてみます。
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();
}
}
ポリゴンが分割された状態で動くようになりました。これで表現の幅を広げていけそうですね!
参考
シェーダの基礎的な知識は「ゲーム制作者になるための3Dグラフィックス技術」を一読されるのがおすすめです。TouchDesigner特有のUniformに関しては、公式Wikiの「Write a GLSL Material」をご覧ください。