OpenGLでテセレーションとかインスタンシングとか試している割に、基本的なジオメトリシェーダーでポイントスプライトを実装してみるということをやっていなかったので、改めて試してみます。
方針
とりあえず四角形の4頂点をポイントスプライトとして描画することを考える。DirectXであれ最近のOpenGLであれ、頂点そのものを描画する機能はシェーダーを使って描画しなくてはいけなくなっているようなので。また、ポイントスプライトといっているけど、テクスチャ準備は今やりたいことから考えると余計なので、頂点カラーをつかってその頂点を描画する方向で考える。
動作結果
先に動いているのを示します。こんな状態になってます。単に四角形を回転させているだけなのですが、その頂点だけを描画すると、不思議と四角形が回転しているようには見えない気がします。
実装
四角形の4頂点を用意して、描画の際には glDrawArrays( GL_POINTS )を使って描画命令を発行する。
他にもワールド、ビュー、プロジェクションの行列は必要なのでこれらはシェーダーに送る。これらのC++側の実装コードはこんな感じです。
glUniformMatrix4fv( glGetUniformLocation( shaderProgram, "WorldMatrix" ), 1, GL_FALSE, glm::value_ptr(world) ); glUniformMatrix4fv( glGetUniformLocation( shaderProgram, "ViewProjMatrix" ), 1, GL_FALSE, glm::value_ptr(pv) ); glDrawArrays( GL_POINTS, 0, 4);
シェーダープログラムの実装
各シェーダープログラムは以下のような感じに実装しています。ジオメトリシェーダーで頂点を4つの頂点に増やしてトライアングルストリップ化しています。またこのときのスプライトの大きさは適当です。本来なら視点からの距離で拡大縮小すると使い物になるんでしょう、たぶん。
#version 410 core layout( location=0 ) in vec3 inPosition; layout( location=1 ) in vec4 inColor; uniform mat4 WorldMatrix; out VS_Out { vec3 WorldPosition; vec4 Color; } Out; void main() { Out.WorldPosition = (WorldMatrix * vec4(inPosition,1) ).xyz; Out.Color = inColor; }
#version 410 core layout(points) in; layout(triangle_strip, max_vertices = 4) out; in VS_Out { vec3 WorldPosition; vec4 Color; } GSIn[]; out GS_OUT { vec4 Color; } GSOut; uniform mat4 gViewProjMatrix; void main() { vec4 center = gViewProjMatrix* vec4(GSIn[0].WorldPosition, 1); gl_Position = center; gl_Position.x = center.x - 0.5; gl_Position.y = center.y + 0.5; GSOut.Color = GSIn[0].Color; EmitVertex(); gl_Position.x = center.x - 0.5; gl_Position.y = center.y - 0.5; EmitVertex(); gl_Position.x = center.x + 0.5; gl_Position.y = center.y + 0.5; EmitVertex(); gl_Position.x = center.x + 0.5; gl_Position.y = center.y - 0.5; EmitVertex(); EndPrimitive(); }
#version 410 core in GS_OUT { vec4 Color; } PSIn; out vec4 out_color0; void main() { out_color0 = PSIn.Color; }
おまけ
今回は単に4頂点に拡張して四角形をそのまま描画していますが、中心点と四隅の距離を計算して一定の範囲内なら描画、範囲外ならピクセルを棄却(discard命令)とすると、丸い形状を描画することができます。