前々回は VertexShaded から GeometryShader へのパラメータ渡しで
built-in のシステム変数 gl_Position を使いました。
VertexShader: gl_Position= vec4( POSITION.xyz, 1.0 ) * PView; GeomteryShader: position= gl_in[0].gl_Position;
gl_Position を使うメリットは下記の通りです。
・宣言なしに使うことが出来る
・VertexShader の段階で書き込んでいるので GeomteryShader が無くても描画できる
前々回のような単なるスルー出力では GeomteryShader が無くても描画可能です。
VertexShader で gl_Position に値を書き込んでいたので、GeomteryShader を attach
せずにそのまま描画することが出来ました。
ただし、常に VertexShader の段階で座標が確定するとは限りません。
頂点からの座標値も、普通の変数を介して GeomteryShader に渡すことが出来ます。

↑基本の状態
// GLSL 1.5 VertexShader gl_Position を使わない
#version 150
uniform vec4 World[3];
uniform mat4 PView;
in vec3 POSITION;
in vec3 NORMAL;
in vec2 TEXCOORD;
out vec4 vPosition;
out vec3 vNormal;
out vec2 vTexcoord;
void main()
{
vPosition= vec4( POSITION.xyz, 1 ) * PView;
vNormal.x= dot( NORMAL.xyz, World[0].xyz );
vNormal.y= dot( NORMAL.xyz, World[1].xyz );
vNormal.z= dot( NORMAL.xyz, World[2].xyz );
vTexcoord= TEXCOORD;
}
// GLSL 1.5 GeometryShader
#version 150
layout(triangles) in;
layout(triangle_strip, max_vertices= 3) out;
in Inputs {
vec4 vPosition;
vec3 vNormal;
vec2 vTexcoord;
} gin[3];
out vec3 vNormal;
out vec2 vTexcoord;
void main()
{
for( int i= 0 ; i< 3 ; i++ ){
gl_Position= gin[i].vPosition;
vNormal= gin[i].vNormal;
vTexcoord= gin[i].vTexcoord;
EmitVertex();
}
EndPrimitive();
}
gl_in を使用していません。
面毎に法線と texcoord をまとめてフラットシェーディング風にしてみます。(↓)

// GeometryShader フラット化
void main()
{
vNormal= normalize( gin[0].vNormal + gin[1].vNormal + gin[2].vNormal );
vTexcoord= (gin[0].vTexcoord + gin[1].vTexcoord + gin[2].vTexcoord)/3;
for( int i= 0 ; i< 3 ; i++ ){
gl_Position= gin[i].vPosition;
EmitVertex();
}
EndPrimitive();
}
きちんと法線を算出している訳じゃないので、本来平面のはずの Quad が Triangle に
分かれて見えてしまいます。今回は気にしないでいきます。
さらに座標もずらしてみます。(↓)

// GeometryShader 座標もずらす
void main()
{
vec3 vnormal= normalize(gin[0].vNormal + gin[1].vNormal + gin[2].vNormal);
vNormal= vnormal;
vTexcoord= ( gin[0].vTexcoord + gin[1].vTexcoord + gin[2].vTexcoord )/3;
vec3 offset= vnormal.xyz*2.0;
for( int i= 0 ; i< 3 ; i++ ){
gl_Position= gin[i].vPosition + vec4( offset.xy, 0.0, 0.0 );
EmitVertex();
}
EndPrimitive();
}
上のスクリーンショットではわからないかもしれませんが、投影後の 2D 座標がずれる
だけなので、形状として厳密に正しい物ではないです。
3D で変形を行うには、Geometry Shader で頂点演算を行う必要があります。
Vertex Shader の方がスルー出力になります。
座標値が vec3 になっている点に注意。任意の型やデータをやりとりできるのが
gl_Position を使わない場合の利点です。
なお正しく面法線を求めていれば、三角形ではなく四角形に押し出されていたはず。
3D 座標(local)でずらした結果(↓)

// GLSL 1.5 VertexShader
#version 150
in vec3 POSITION;
in vec3 NORMAL;
in vec2 TEXCOORD;
out vec3 vPosition; // vec4 → vec3
out vec3 vNormal;
out vec2 vTexcoord;
void main()
{
vPosition= POSITION;
vNormal= NORMAL;
vTexcoord= TEXCOORD;
}
// GLSL 1.5 GeometryShader
#version 150
uniform vec4 World[3];
uniform mat4 PView;
layout(triangles) in;
layout(triangle_strip, max_vertices= 3) out;
in Inputs {
vec3 vPosition; // vec4 → vec3
vec3 vNormal;
vec2 vTexcoord;
} gin[3];
out vec3 vNormal;
out vec2 vTexcoord;
vec4 transform( vec3 pos )
{
return vec4( pos, 1 ) * PView;
}
void main()
{
vec3 vnormal= normalize(gin[0].vNormal + gin[1].vNormal + gin[2].vNormal);
vNormal.x= dot( vnormal.xyz, World[0].xyz );
vNormal.y= dot( vnormal.xyz, World[1].xyz );
vNormal.z= dot( vnormal.xyz, World[2].xyz );
vTexcoord= ( gin[0].vTexcoord + gin[1].vTexcoord + gin[2].vTexcoord )/3;
for( int i= 0 ; i< 3 ; i++ ){
gl_Position= transform( gin[i].vPosition + vnormal * 1.5 );
EmitVertex();
}
EndPrimitive();
}
以前は VertexShader で受け取っていた uniform 変数 World, PView が
GeometryShader に移動しています。
OpenGL + GLSL の場合、Link したあとに uniform の設定を行うので C++ 側のコード
は何も修正しなくて済みます。結構便利です。
Direct3D だと各シェーダー毎に ConstantBuffer の設定が必要なので、
VSSetConstantBuffers() → GSSetConstantBuffers() と C++ の呼び出し側も
変更しなければなりませんでした。
ここまできたら VertexShader は不要な気もします。
一応試してみましたが VertexShader の省略は出来ませんでした。
Direct3D の Effect (FX) だと VertexShader と GeometryShader に同じ内容を
割り当てることで省略できます。
GeometryShader でポリゴンを増やしてみます。中に元の形状が内部に置いてある状態です。
共有頂点分の演算が重複するので、こういうケースでは VertexShader を併用
した方が効率がよいかもしれません。
out の layout で max_vertices= 6 に注意。

// GLSL 1.5 GeometryShader
#version 150
uniform vec4 World[3];
uniform mat4 PView;
layout(triangles) in;
layout(triangle_strip, max_vertices= 6) out;
in Inputs {
vec3 vPosition;
vec3 vNormal;
vec2 vTexcoord;
} gin[3];
out vec3 vNormal;
out vec2 vTexcoord;
vec4 transform( vec3 pos )
{
return vec4( pos, 1 ) * PView;
}
void setnormal( vec3 normal )
{
vNormal.x= dot( normal.xyz, World[0].xyz );
vNormal.y= dot( normal.xyz, World[1].xyz );
vNormal.z= dot( normal.xyz, World[2].xyz );
}
void main()
{
for( int i= 0 ; i< 3 ; i++ ){
setnormal( gin[i].vNormal );
vTexcoord= gin[i].vTexcoord;
gl_Position= transform( gin[i].vPosition );
EmitVertex();
}
EndPrimitive();
vec3 vnormal= normalize( gin[0].vNormal + gin[1].vNormal + gin[2].vNormal );
setnormal( vnormal );
vTexcoord= ( gin[0].vTexcoord + gin[1].vTexcoord + gin[2].vTexcoord )/3;
for( int i= 0 ; i< 3 ; i++ ){
gl_Position= transform( gin[i].vPosition + vnormal * 2.0 );
EmitVertex();
}
EndPrimitive();
}
関連エントリ
・OpenGL GLSL のバージョン
・OpenGL 3.2 の GeometryShader
・OpenGL のはじめ方
・OpenGL ES 2.0 Emulator
・OpenGL ES 2.0
・OpenGLES2.0 DDS テクスチャを読み込む
・OpenGLES2.0 Direct3D とのフォーマット変換
・OpenGLES 2.0 頂点フォーマットの管理
・OpenGLES2.0 の頂点
・OpenGLES2.0 D3D座標系
・OpenGLES2.0 シェーダー管理