ジオメトリシェーダ
ジオメトリシェーダは、レンダリングパイプラインにおいて、バーテックスシェーダの後に配置されており、GPU上でプリミティブの変換や増減が可能なシェーダだそうです。
ポイントスプライト
ポイントスプライトという手法は、CPUからpointsで与えた点群をジオメトリシェーダを用いてtriangleなどに変換しテクスチャを貼ることで、GPUの計算能力を活かしてテクスチャ付きのパーティクルをつくる方法です。
実装してみた
今回はGLSL1.2で使えるジオメトリシェーダ(Geometry Shader)を用いて、ポイントスプライトを実装してみました。OpenGL 4.0 シェーディング言語 -実例で覚えるGLSLプログラミング-に掲載されているコードを参考にしています。ジオメトリシェーダは、Maxのjit.gl.shaderでもちゃんと使えるのですが、ググっても解説がないためつまずくポイントも多かったため、少し詳しく紹介します。
パラメーターの宣言
[html]
<param name="size" type="vec2" default="1. 1." />
[/html]
ジオメトリシェーダに限らない事ですが、Maxから指定するパラメーターは、param要素として指定します。初期値も与えられますがコンマで区切っては駄目みたいなので注意です。
パラメーターのバインド
[html]
<language name="glsl" version="1.2">
<bind param="size" program="gp" />
<bind param="tex0" program="fp" />
<program>
</program>
</language>
[/html]
宣言したパラメーターが、どのプログラマブルシェーダで利用されるかをprogram属性にbindします。例:sizeをgeometry shaderで利用する。例外ですがテクスチャをシェーダで扱いたい場合には、Maxの場合tex0というパラメーターが予約されているようで、宣言していなくても使えました。マルチテクスチャをやる場合については未調査です。
サンプルコードで使われているプログラム名称
- “gp” : geometry shader
- “fp” : fragment shader
- “vp” : vertex shader
ジオメトリシェーダの宣言
[html]
<program name="gp" vertices_out="4" input_type="points" output_type="triangle_strip" type="geometry">
<![CDATA[
/*glslのプログラムを記述*/
]]>
</program>
[/html]
program要素を宣言します。vertices_out / input_type / output_typeが合ってないと実行できません。今回は、”points”を”triangle_strip”に変換し”4”つのvertexが出力されるという設定になっています。
ジオメトリシェーダのコード
[c]
#version 120
#extension GL_EXT_geometry_shader4 : enable
uniform vec2 size;
varying in vec2 texdim0[1];
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_PositionIn[0];
gl_Position.x -= size.x/2;
gl_Position.y += size.y/2;
gl_TexCoord[0].st = vec2(0,0) * texdim0[0];
EmitVertex();
gl_Position.y -= size.y;
gl_TexCoord[0].st = vec2(0,1) * texdim0[0];
EmitVertex();
gl_Position.x += size.x;
gl_Position.y += size.y;
gl_TexCoord[0].st = vec2(1,0) * texdim0[0];
EmitVertex();
gl_Position.y -= size.y;
gl_TexCoord[0].st = vec2(1,1) * texdim0[0];
EmitVertex();
}
[/c]
#extension GL_EXT_geometry_shader4 : enable でジオメトリシェーダを有効化しています。main関数では、Maxから受け取ったsizeを使ってポイント(頂点)を、四つ頂点へと増加させています。特殊な書き方ですがEmitVertex()を呼ぶことでgl_texCoord[0]に設定されている頂点を出力しています。gl_TexCoordには、テクスチャの参照位置を入れていますね。Maxから受け取るテクスチャの座標が正規化いないので、texdim0[0]を掛けています。
JXS TIPS
- 日本語でコメントが書かれているとMaxが即落ちる
- ポイントスプライトで使うテクスチャのバインド先は、jit.gl.shaerではなく、jit.gl のオブジェクト
-
jit.gl.mesh: unbinding vertex array: GL Error: Invalid operation
というエラーが出た場合は、jit.gl.meshのdraw_modeがpointsになっていない可能性があります。
JXS / GLSLコード
繰り返しもありますが、以下にJXSのコードを貼っておきます。これをjit.gl.shaderにjxsとして読み込ませて、jit.gl.meshにバインドすると動きます。
[html]
<jittershader name="mrr.templete.geometry.jxs">
<description>Simple geometry shader test</description>
<param name="size" type="vec2" default="1. 1." />
<language name="glsl" version="1.2">
<bind param="size" program="gp" />
<bind param="tex0" program="fp" />
<program name="vp" type="vertex">
<![CDATA[
#version 120
varying vec2 texdim0;
void main(void)
{
//just send the things as they are
gl_Position = gl_Vertex;
gl_FrontColor = gl_Color;
//for texture dim
texdim0 = vec2 (abs(gl_TextureMatrix[0][0][0]),abs(gl_TextureMatrix[0][1][1]));
}
]]>
</program>
<program name="gp" vertices_out="4" input_type="points" output_type="triangle_strip" type="geometry">
<![CDATA[
#version 120
#extension GL_EXT_geometry_shader4 : enable
uniform vec2 size;
varying in vec2 texdim0[1];
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_PositionIn[0];
gl_Position.x -= size.x/2;
gl_Position.y += size.y/2;
gl_TexCoord[0].st = vec2(0,0) * texdim0[0];
EmitVertex();
gl_Position.y -= size.y;
gl_TexCoord[0].st = vec2(0,1) * texdim0[0];
EmitVertex();
gl_Position.x += size.x;
gl_Position.y += size.y;
gl_TexCoord[0].st = vec2(1,0) * texdim0[0];
EmitVertex();
gl_Position.y -= size.y;
gl_TexCoord[0].st = vec2(1,1) * texdim0[0];
EmitVertex();
}
]]>
</program>
<program name="fp" type="fragment">
<![CDATA[
#version 120
uniform sampler2DRect tex0;
void main (void)
{
gl_FragColor = texture2DRect(tex0, gl_TexCoord[0].st);
}
]]>
</program>
</language>
</jittershader>
[/html]