GLSLを用いて単純なジオメトリシェーダを用いたプログラムを実装する. ジオメトリシェーダとは†ジオメトリシェーダはバーテックスシェーダとフラグメントシェーダの間に位置するシェーダである. バーテックスシェーダは頂点ごとの処理,フラグメントシェーダはフラグメント(ピクセル)ごとの処理であり, この二つだけでは,例えば,入力された三角形ポリゴンから点群を生成して描画する, ポリゴンのテッセレーション,ディスプレースメントマップなどの処理は難しい. これらの処理のためにはプリミティブごとの処理を行い,かつ,新しいプリミティブを作り出すことができるシェーダが必要である. これがジオメトリシェーダである. 例えば,ポリゴンのテッセレーションを行いたい場合,1ポリゴンをプリミティブとして入力とし, ジオメトリシェーダ内で新しい複数のポリゴンを生成して出力することで処理を実現できる. このとき,ジオメトリシェーダへの入力,出力はポリゴンとなる. また,ポリゴンから点群を生成したい場合,1ポリゴンをプリミティブとして入力して, ジオメトリシェーダ内で新しい点群を生成して出力する. このときの入力はポリゴンで出力は点となる. このように,ジオメトリシェーダを用いる場合,その入力と出力プリミティブが場合によって異なるため, シェーダプログラムのパラメータとして入力,出力を設定しなければならない. ジオメトリシェーダのビルド†GLSLについてで述べているように,シェーダのビルドを行う必要がある. ジオメトリシェーダのコンパイルは,targetにGL_GEOMETRY_SHADERを渡すこと以外は, 他のシェーダ(頂点シェーダ,フラグメントシェーダ)と同じであるので,GLSLについてを参照してほしい. 他のシェーダとビルドにおいて異なるのがシェーダへの入出力の設定で, これはシェーダオブジェクト登録(glAttachShader)とプログラムのリンク(glLinkProgram)の間で行う. 入出力設定にはglProgramParameterを用いる. void glProgramParameteri(GLuint program, GLenum pname, GLint value); programにglCreateProgramで作ったプログラムオブジェクト,pname,valueにパラメータと値を設定する.パラメータと値は,
ジオメトリシェーダを含むプログラムのリンクを行う関数の例を以下に示す.
シェーダソースファイルからコンパイル,リンクを行う関数の例を以下に示す. 返値はrxGLSL型の変数となっており,シェーダファイル名,プログラムオブジェクトなどを格納する構造体である.
もし,vertex_outに負の値を指定したらシステムの最大出力頂点数を調べて設定する. 最大出力頂点数について†上記のCreateGLSLFromFileではGL_MAX_GEOMETRY_OUTPUT_VERTICESでシステムの最大出力頂点数を調べて設定しているが, ジオメトリシェーダでは最大出力頂点数の他に最大出力要素数(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS)も制限されている. 最大出力要素数,最大出力頂点数を調べて出力するコード例を以下に示す. GLint nc, nv; glGetIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &nc); cout << "maximum number of components : " << nc << endl; glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &nv); cout << "max output vertices : " << nv << endl; GeForce GTX 480を持つPC上で実行した結果を以下に示す(OpenGLバージョン,GLEWバージョンも出力した). OpenGL Ver. 4.1.0 Glew Ver. 1.5.7 maximum number of components : 1024 max output vertices : 1024 最大出力要素数における要素とは頂点における座標や色のことであり, 通常1頂点は8つの要素(4つの位置要素と4つの色要素)を持つ. そうすると,この環境上では最大出力頂点数は1024となっているが,実際には最大出力要素数の制限から 128(=1024/8)に制限される.位置要素のみにした場合でも256(=1024/4)となる. ジオメトリシェーダのビルドイン変数と関数†頂点シェーダのgl_PositionやglTexCoord[]のようにジオメトリシェーダにもビルドイン変数がいくつか用意されている. 頂点シェーダとフラグメントシェーダのみの場合は,これらのビルドイン変数はシェーダ間で共通になっていたが, ジオメトリシェーダでは頂点シェーダで処理された複数の頂点を読み込む場合があり, そのため,in変数とout変数で異なるものが定義されている. 例えば,頂点シェーダでgl_Positionに設定された値は, ジオメトリシェーダ側からはgl_PositionIn[]配列を用いてアクセスできる. この配列の大きさは変数gl_VerticesInで定義され,gl_VerticesInの値はGL_GEOMETRY_INPUT_TYPE_EXTにどれを指定したかで決定される. これらの値を用いてジオメトリシェーダの出力用変数gl_Position変数に値を設定後,ビルドイン関数であるEmitVertex()を実行することで ラスタライザに頂点座標が出力される. ビルドイン変数,関数を以下に示す. varying in vec4 gl_FrontColorIn[gl_VerticesIn]; varying in vec4 gl_BackColorIn[gl_VerticesIn]; varying in vec4 gl_FrontSecondaryColorIn[gl_VerticesIn]; varying in vec4 gl_BackSecondaryColorIn[gl_VerticesIn]; varying in vec4 gl_TexCoordIn[gl_VerticesIn][]; varying in float gl_FogFragCoordIn[gl_VerticesIn]; varying in vec4 gl_PositionIn[gl_VerticesIn]; varying in float gl_PointSizeIn[gl_VerticesIn]; varying in vec4 gl_ClipVertexIn[gl_VerticesIn]; varying out vec4 gl_FrontColor; varying out vec4 gl_BackColor; varying out vec4 gl_FrontSecondaryColor; varying out vec4 gl_BackSecondaryColor; varying out vec4 gl_TexCoord[]; varying out float gl_FogFragCoord; void EmitVertex(); void EndPrimitive(); 具体的な使い方は下記のコード例を参照. ユーザ定義のvarying変数†ビルドイン変数と同様にユーザ定義の変数を用いて頂点シェーダからジオメトリシェーダへ, ジオメトリシェーダからラスタライザを介してフラグメントシェーダに値を渡すことができる. 注意として,頂点シェーダとフラグメントシェーダのみの場合と異なり,varying変数が入力(varying in)か出力(varying out)かを 明示的に指定する必要がある.また,頂点シェーダの出力とジオメトリシェーダの入力は数が異なるので, ジオメトリシェーダ側の変数は同じ変数名の配列となる. 例えば,頂点シェーダ側で varying float Alpha; varying vec3 Nrm; とした場合,ジオメトリシェーダでは, varying in float Alpha[3]; varying in vec3 Nrm[3]; とする.ただし,ここでは入力としてGL_TRIANGLESが指定されたとしている. フラグメントシェーダに出力する場合,ジオメトリシェーダでは, varying out vec3 LightPos; のようにし,これをEmitVertex()する各頂点で値を設定する. LightPos = ... EmitVertex(); フラグメントシェーダでは, varying vec3 LightPos; として受け取る. ジオメトリシェーダの例†ジオメトリシェーダを用いた単純な例として,入力された三角形ポリゴンを縮小して描画する例を示す. 三角形の重心を縮小の中心として利用する. 頂点シェーダだけでは重心を求めることができないためジオメトリシェーダを用いる. まず,頂点シェーダではPhong反射モデルに基づき頂点色を決定する.
ジオメトリシェーダでは頂点の情報をビルドイン変数の varying in vec4 gl_PositionIn[gl_VerticesIn]; から取得して,三角形ポリゴンの重心を算出し,さらに, varying in vec4 gl_FrontColorIn[gl_VerticesIn]; にから頂点色を取得して設定する. 重心座標に基づき縮小したポリゴン頂点座標を計算して,gl_Positionに設定する. gl_FrontColor, gl_Positionに頂点情報を設定したら,ジオメトリシェーダ用の関数 void EmitVertex(); を呼び出して,頂点の設定を完了する.これを三角形ポリゴンの3頂点全てで行った後, void EndPrimitive(); を呼び出して,プリミティブ(ここでは三角形ポリゴン)の設定を完了する.
フラグメントシェーダでは単純に色をピクセルに渡すだけである.
レンダリング結果†レンダリング結果を以下に示す. ソースコード† |