2009/09/05
OpenGL 3.2 GeometryShader をもう少し使ってみる
前々回は VertexShaded から GeometryShader へのパラメータ渡しで
built-in のシステム変数 gl_Position を使いました。
gl_Position を使うメリットは下記の通りです。
・宣言なしに使うことが出来る
・VertexShader の段階で書き込んでいるので GeomteryShader が無くても描画できる
前々回のような単なるスルー出力では GeomteryShader が無くても描画可能です。
VertexShader で gl_Position に値を書き込んでいたので、GeomteryShader を attach
せずにそのまま描画することが出来ました。
ただし、常に VertexShader の段階で座標が確定するとは限りません。
頂点からの座標値も、普通の変数を介して GeomteryShader に渡すことが出来ます。

↑基本の状態
gl_in を使用していません。
面毎に法線と texcoord をまとめてフラットシェーディング風にしてみます。(↓)

きちんと法線を算出している訳じゃないので、本来平面のはずの Quad が Triangle に
分かれて見えてしまいます。今回は気にしないでいきます。
さらに座標もずらしてみます。(↓)

上のスクリーンショットではわからないかもしれませんが、投影後の 2D 座標がずれる
だけなので、形状として厳密に正しい物ではないです。
3D で変形を行うには、Geometry Shader で頂点演算を行う必要があります。
Vertex Shader の方がスルー出力になります。
座標値が vec3 になっている点に注意。任意の型やデータをやりとりできるのが
gl_Position を使わない場合の利点です。
なお正しく面法線を求めていれば、三角形ではなく四角形に押し出されていたはず。
3D 座標(local)でずらした結果(↓)

以前は 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 に注意。

関連エントリ
・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 シェーダー管理
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 シェーダー管理