ジオメトリシェーダでは、頂点シェーダからの出力を使用してプリミティブ生成の処理を行い、任意の数の頂点を出力することができます。
OpenGL ES 2.0 では glDrawElements()
や glDrawArrays()
の引数に GL_POINTS
、GL_LINES
、GL_LINE_STRIP
、GL_LINE_LOOP
を指定することでポイントやラインを描画することができましたが、3DS では、トライアングル(三角形)以外のプリミティブはジオメトリシェーダを使用して生成しなければなりません。トライアングルについては OpenGL ES 2.0 と同様に GL_TRIANGLES
、GL_TRIANGLE_STRIP
、GL_TRIANGLE_FAN
で行いますが、マルチサンプル描画には対応していません。
「5. シェーダプログラム」でも述べたように、3DS では SDK でジオメトリシェーダを提供しています。使用可能なジオメトリシェーダには以下のものがあります。
- ポイントシェーダ
- ラインシェーダ
- シルエットシェーダ
- Catmull-Clark サブディビジョンシェーダ
- ループサブディビジョンシェーダ
- パーティクルシステムシェーダ
これらのジオメトリシェーダを単独で使用することはできません。必ず頂点シェーダもリンクされたバイナリをロードし、頂点シェーダと併せて使用する必要があります。ジオメトリシェーダを使用する場合、4 基ある頂点プロセッサのうちの 1 つがジオメトリプロセッサとして使用されます。また、ブールレジスタの 15 番(b15)はジオメトリシェーダに予約されています。
ジオメトリシェーダの入力データには頂点シェーダの出力が使用されます。ジオメトリシェーダが必要とする頂点属性や入力順序、使用可能な頂点属性などはそれぞれのシェーダプログラムで決められており、頂点シェーダはそれらを正しく出力する必要があります。ジオメトリシェーダへの入力は頂点シェーダが出力するレジスタ番号の小さいものから順番に行われます。出力する頂点属性は #pragma output_map
で定義しますが、属性名 generic
のものはジオメトリシェーダでのみ使用され、後段のプロセス(フラグメント処理など)では扱われない頂点属性となっています。
ジオメトリシェーダには予約ユニフォームが存在し、ポイントシェーダであればビューポートの設定などに使用します。これらの予約ユニフォームはすべて初期値が不定であるため、アプリケーションが必ず値を設定しなければなりません。
9.1. ポイントシェーダ
ポイントシェーダは、頂点座標とポイントサイズから、頂点座標を中心とする辺の長さがポイントサイズの正方形(ポイントプリミティブ)を、2 つの三角形プリミティブを生成して描画します。ポイントプリミティブにテクスチャを貼るため、ポイントシェーダにテクスチャ座標(s と t は下図参照、r は 0.0 固定、q は 1.0 固定)の出力を付加したものがポイントスプライトシェーダです。どちらのシェーダも、グリッドによる調整やマルチサンプル描画には対応していません。
9.1.1. シェーダファイル
頂点シェーダとリンクさせるシェーダファイルは、ポイントシェーダが必要とする頂点属性以外にフラグメント処理のために出力されている頂点属性の数と、ポイントスプライトシェーダがテクスチャ座標を出力する数から決まります。
ポイント: DMP_pointN.obj
ポイントスプライト: DMP_pointSpriteN_T.obj
N はポイントシェーダ(ポイントスプライトシェーダ)が必要とするもの以外の頂点属性の数、T は使用するテクスチャ座標の個数です。
9.1.2. 予約ユニフォーム
ビューポートとポイントサイズの距離減衰の有効・無効を設定する予約ユニフォームが存在します。これらの予約ユニフォームは初期値を持たないため、必ず値を設定しなければなりません。
ビューポート
ビューポートの予約ユニフォーム(dmp_Point.viewport
)には、ビューポートの幅と高さの逆数を glUniform2fv()
で設定します。
距離減衰
ポイントサイズの距離減衰の予約ユニフォーム(dmp_Point.distanceAttenuation
)には、GL_TRUE
(距離減衰が有効)または GL_FALSE
(距離減衰が無効)を glUniform1i()
で設定します。距離減衰を無効にした場合、ポイントサイズにクリップ座標 Wc が乗算されることでウィンドウ座標変換時の Wc による除算が無効になり、ポイントプリミティブが距離による表示サイズ変化の影響を受けなくなります。
予約ユニフォーム |
種別 |
設定する値 |
---|---|---|
|
|
ビューポートを以下の計算式で指定する。 (1 / viewport.width, 1 / viewport.height) |
|
|
ポイントサイズが距離減衰の影響を受けるかどうかを指定する。
|
9.1.3. 頂点シェーダの設定
ポイントシェーダがポイントプリミティブを描画するのに必要なのは、頂点座標とポイントサイズの 2 つです。これらの頂点属性を頂点シェーダから出力する順番は、出力レジスタ番号の小さい方から、頂点座標、ポイントサイズです。ポイントスプライトシェーダの場合は、この後にテクスチャ座標が続きます。使用するテクスチャ座標の個数が複数である場合は、1 つの出力レジスタに 2 つのテクスチャ座標を xy
と zw
に分けてパックしなければなりません。
設定すべき出力頂点属性は、頂点座標が "position
"、ポイントサイズが "generic
"、テクスチャ座標が "texture0
" ~ "texture2
" です。
ポイントシェーダで必要な頂点座標とポイントサイズのほかに頂点カラーを頂点シェーダで出力する場合、シェーダファイル DMP_point1.obj をリンクし、頂点シェーダの #pragma output_map
は以下のように宣言します。
#pragma output_map ( position , o0 ) #pragma output_map ( generic , o1 ) #pragma output_map ( color , o2 )
ポイントスプライトシェーダで必要な頂点座標とポイントサイズ、2 つのテクスチャ座標のほかに頂点カラーを頂点シェーダで出力する場合、シェーダファイル DMP_pointSprite1_2.obj をリンクし、頂点シェーダの #pragma output_map
は以下のように宣言します。
#pragma output_map ( position , o0 ) #pragma output_map ( generic , o1 ) #pragma output_map ( texture0 , o2.xy ) #pragma output_map ( texture1 , o2.zw ) #pragma output_map ( color , o3 )
ポイントスプライトシェーダはテクスチャ座標をポイントスプライト用のテクスチャ座標に置き換えて出力します。頂点シェーダは、ポイントスプライトシェーダで置き換えられるテクスチャ座標の出力レジスタにもダミーの値を書きこまなければならないことに注意が必要です。
9.1.4. 頂点データの入力
ポイントシェーダによるプリミティブの生成を行いたい場合は、glDrawElements()
または glDrawArrays()
の mode
に GL_GEOMETRY_PRIMITIVE_DMP
を渡して呼び出してください。
9.2. ラインシェーダ
ラインシェーダは、2 つの頂点座標から頂点座標を結ぶ直線(ラインプリミティブ)を、2 つの三角形プリミティブを生成して描画します。ラインの幅は予約ユニフォームで設定することができます。ラインシェーダはグリッドによる調整やマルチサンプル描画には対応していません。
与えられた頂点座標を結ぶ線分の傾きとラインの幅から、矩形(平行四辺形)を形作る 4 つの頂点座標が生成されます。4 つの頂点座標は入力された頂点座標から Y 方向(線分の傾きによっては X 方向)に生成されます。
9.2.1. シェーダファイル
頂点シェーダとリンクさせるシェーダファイルは、ラインシェーダが必要とする頂点属性以外にフラグメント処理のために出力されている頂点属性の数から決まります。
頂点座標の指定方法により、セパレートラインとストリップラインの 2 種類に分かれています。セパレートラインは 2 つの頂点座標を組にして 1 本のラインを描画します。ストリップラインは最初の 2 つの頂点座標まではセパレートラインと同じですが、次のラインは 2 つ目の頂点座標と 3 つ目の頂点座標を組にして描画されます。つまり、3 つ目以降の頂点は 1 つ前の頂点座標と組になってライン描画に使用され、全体で 1 本に繋がったラインとなります。
セパレートライン: DMP_separateLineN.obj
ストリップライン: DMP_stripLineN.obj
N はラインシェーダが必要とするもの以外の頂点属性の数です。
9.2.2. 予約ユニフォーム
ラインの幅を設定する予約ユニフォームが存在します。ライン幅の予約ユニフォームは初期値を持たないため、必ず値を設定しなければなりません。
ライン幅
ライン幅の予約ユニフォーム(dmp_Line.width
)には、ラインの幅とビューポートの幅と高さから計算した値を glUniform4fv()
で設定します。
予約ユニフォーム |
種別 |
設定する値 |
---|---|---|
|
|
ラインの幅を以下の計算式で指定する。 (viewport.width / line.width, |
9.2.3. 頂点シェーダの設定
ラインシェーダがラインプリミティブを描画するのに必要なのは、頂点座標です。頂点座標は出力するレジスタ番号の一番小さな番号で頂点シェーダから出力します。
設定すべき出力頂点属性は、頂点座標の "position
" です。
セパレートラインシェーダで必要な頂点座標のほかに頂点カラーを頂点シェーダで出力する場合、シェーダファイル DMP_separateLine1.obj をリンクし、頂点シェーダの #pragma output_map
は以下のように宣言します。
#pragma output_map ( position , o0 ) #pragma output_map ( color , o1 )
ストリップラインシェーダの場合、シェーダファイルが DMP_stripLine1.obj であること以外はセパレートラインシェーダと同じです。
9.2.4. 頂点データの入力
ラインシェーダによるプリミティブの生成を行いたい場合は、glDrawElements()
または glDrawArrays()
の mode
に GL_GEOMETRY_PRIMITIVE_DMP
を渡して呼び出してください。
9.3. シルエットシェーダ
シルエットシェーダはオブジェクトの境界部分のシルエットエッジを生成して描画します。シルエットエッジはオブジェクトの輪郭の描画に利用することができ、シャドウ機能と組み合わせればソフトシャドウの描画に利用することができます。
シルエットエッジの生成には、近接付随三角形(triangle with neighborhood、以下 TWN)と呼ばれるプリミティブをシルエットシェーダに投入しなければなりません。
9.3.1. 近接付随三角形(TWN:triangle with neighborhood)
シルエットエッジを描画するオブジェクトを構成する三角形の 1 つに注目して説明します。
注目した三角形を中心三角形と呼び、それぞれのエッジを共有している 3 つの三角形(近接三角形)を含めた 4 つの三角形が TWN です。
頂点 3、1、4 を中心三角形とすれば、頂点 0、1、3 の三角形、頂点 2、4、1 の三角形、頂点 6、3、4 の三角形の 3 つと中心三角形が TWN です。頂点 3、4、6 を中心三角形とした場合には、頂点 3、1、4 の三角形は別の TWN を構成する三角形の 1 つになります。
TWN は中心三角形のシルエットエッジを検出するために使われ、オブジェクトを TWN で構成することでオブジェクトのシルエットエッジを描画することができます。
9.3.2. シェーダファイル
TWN の頂点をシルエットシェーダに入力する方法として、通常の三角形を入力するのと同じように、TWN を 1 つずつ入力する方法(シルエットトライアングル)と隣り合う TWN を連続して入力する方法(シルエットストリップ)の 2 つの方法が存在します。
シルエットトライアングル: DMP_silhouetteTriangle.obj
シルエットストリップ: DMP_silhouetteStrip.obj
9.3.3. 予約ユニフォーム
シルエットシェーダの予約ユニフォームには、以下のものが存在します。これらの予約ユニフォームは初期値を持たないため、必ず値を設定しなければなりません。
ポリゴンの表裏判定
シルエットシェーダがポリゴンの表裏を判定する方法(dmp_Silhouette.frontFaceCCW
)には、オブジェクトの頂点入力で glFrontFace()
に GL_CCW
を渡している場合は GL_TRUE
を、GL_CW
の場合は GL_FALSE
を glUniform1i()
で設定します。
シルエットエッジの幅
シルエットエッジの幅(dmp_Silhouette.width
)には、法線の x 方向へ乗算する係数を基に計算した値を glUniform2fv()
で設定します。
頂点の w 成分の影響(dmp_Silhouette.scaleByW
)には、シルエットエッジの幅に頂点の w 成分を乗算することで距離減衰を無効にするかどうかを設定します。w 成分を乗算する場合は GL_TRUE
を、シルエットエッジの w 成分を固定値(1.0)にする場合は GL_FALSE
を glUniform1i()
で設定します。
シルエットエッジのカラー
シルエットエッジのカラー(dmp_Silhouette.color
)には、カラー値(R、G、B、A)を glUniform4fv()
で設定します。
オープンエッジ
オープンエッジとは、ほかの三角形とエッジを共有していない中心三角形のエッジのことです。オープンエッジは設定(dmp_Silhouette.acceptEmptyTriangles
)によって常に描画させる(GL_TRUE
)ことや、描画させない(GL_FALSE
)ことができます。
オープンエッジはシルエットエッジとは異なり、頂点の法線を使用せずにラインプリミティブのように描画されます。そのため、角度によってはシルエットエッジとは見え方が異なる場合があります。また、オープンエッジ固有の設定が存在します。
オープンエッジの幅
オープンエッジの幅(dmp_Silhouette.openEdgeWidth
)には、ラインシェーダと同じように幅とビューポートの幅と高さから計算した値を glUniform4fv()
で設定します。
オープンエッジのカラー
オープンエッジのカラー(dmp_Silhouette.openEdgeColor
)には、カラー値( R 、G 、B 、A )を glUniform4fv()
で設定します。
オープンエッジの視点方向へのバイアス
視点方向へのバイアス値(dmp_Silhouette.openEdgeDepthBias
)には、バイアス値(負値で視点に近付き、正値で遠ざかる)を glUniform1fv()
で設定します。オープンエッジの生成には法線が使用されないため、このバイアス値でエッジの見え方を調整します。
オープンエッジの w 成分の乗算
オープンエッジの幅やバイアス値には頂点の w 成分を乗算することができます。それぞれ、dmp_Silhouette.openEdgeWidthScaleByW
と dmp_Silhouette.openEdgeDepthBiasScaleByW
に GL_TRUE
または GL_FALSE
を glUniform1i()
で設定します。
予約ユニフォーム |
種別 |
設定する値 |
---|---|---|
|
|
シルエットエッジの幅を以下の計算式で指定する。 (xscale_f, |
|
|
シルエットエッジに頂点の w 成分を影響させるかどうかを指定する。
|
|
|
シルエットエッジのカラーを指定する。 |
|
|
ポリゴンの表裏の判定方法を指定する。
|
|
|
オープンエッジを描画するかどうかを指定する。
|
|
|
オープンエッジのカラーを指定する。 |
|
|
オープンエッジの幅を以下の計算式で指定する。 (viewport.width / silhouette.width, |
|
|
オープンエッジの視点方向へのバイアス値を指定する。 負の値で視点に近付き、正の値で視点から遠ざかります。 |
|
|
オープンエッジの幅に頂点の w 成分を乗算するかどうかを指定する。
|
|
|
オープンエッジの視点方向へのバイアス値に頂点の w 成分を乗算させるかどうかを指定する。
|
9.3.4. 頂点シェーダの設定
シルエットシェーダがシルエットエッジを描画するのに必要なのは、頂点座標、頂点カラー、法線の 3 つです。これらの頂点属性を頂点シェーダから出力する順番は、出力レジスタ番号の小さい方から、頂点座標、頂点カラー、法線の順です。
設定すべき出力頂点属性は、頂点座標が "position
"、頂点カラーが "color
"、法線が "generic
" です。
シルエットトライアングルで出力する場合はシェーダファイル DMP_silhouetteTriangle.obj をリンクし、頂点シェーダの #pragma output_map
は以下のように宣言します。
#pragma output_map ( position , o0 ) #pragma output_map ( color , o1 ) #pragma output_map ( generic , o2 )
頂点シェーダ内では、アプリケーションから入力された法線にモデルビュー変換を行い、x 成分と y 成分について正規化された値を出力しなければなりません。つまり、頂点シェーダから出力される法線 n' は、視点座標系の法線 n=(nx, ny, nz) から以下のように正規化されなければなりません。
シェーダアセンブラで記述すると以下のようになります。aNormal
がアプリケーションから入力された法線、vNormal
がシルエットシェーダに出力される法線です。
mov TEMP_NORM, CONST_0 dp3 TEMP_NORM.x, aNormal, MATRIX_ModelView[0] dp3 TEMP_NORM.y, aNormal, MATRIX_ModelView[1] mul TEMP, TEMP_NORM, TEMP_NORM add TEMP, TEMP.x, TEMP.y rsq TEMP, TEMP.x mul vNormal, TEMP_NORM, TEMP
シルエットストリップで出力する場合、シェーダファイルが DMP_silhouetteStrip.obj であること以外はシルエットトライアングルで出力する場合と同じです。
9.3.5. 頂点データの入力
シルエットシェーダによるシルエットエッジの描画を行いたい場合は、glDrawElements()
または glDrawArrays()
の mode
に GL_GEOMETRY_PRIMITIVE_DMP
を渡して呼び出してください。また、描画の前に glDisable(GL_CULL_FACE)
でカリングを無効にしなければなりません。
TWN の性質上、シルエットシェーダへの頂点データの入力には、頂点インデックスの使用を推奨します。以降の説明では頂点インデックスの使用を前提としています。なお、頂点インデックスを使用しない場合は、TWN の頂点入力の規則に従って頂点データを並べる必要があります。
9.3.6. シルエットトライアングルのインデックス
シルエットトライアングルは TWN を 1 つずつシェーダに入力する方法で、6 頂点で 1 つの TWN を入力することになります。
入力する頂点の順序は以下のとおりです。
- 中心三角形の第 1 頂点
- 中心三角形の第 2 頂点
- 中心三角形の第 1 頂点と第 2 頂点が形成するエッジを共有する近接三角形のもう 1 つの頂点
- 中心三角形の第 3 頂点
- 中心三角形の第 1 頂点と第 3 頂点が形成するエッジを共有する近接三角形のもう 1 つの頂点
- 中心三角形の第 2 頂点と第 3 頂点が形成するエッジを共有する近接三角形のもう 1 つの頂点
以下の図のオブジェクトを例にインデックスの指定を説明します。
ポリゴンの表裏判定は CCW が設定されていると仮定します。
表になるように選択した中心三角形を(1、4、3)と(3、4、6)とすればインデックスの指定は、
(1、4、3)のインデックス:1、4、2、3、0、6
(3、4、6)のインデックス:3、4、1、6、5、7
のようになります。
中心三角形が縮退している場合、シルエットエッジは生成されません。また、3 つ以上の三角形でエッジを共有するような三角形は考慮していません。
9.3.7. シルエットストリップのインデックス
シルエットストリップは TWN を連続してシェーダに入力する方法で、最初の TWN で 6 頂点の入力が必要ですが、以降は 2 頂点の入力で 1 つの TWN を入力することができます。そのため、同じモデルを描画する場合でも、シルエットトライアングルの倍以上の性能を発揮すると予想されます。
中心三角形の第 2 頂点と第 3 頂点と、その 2 頂点が形成するエッジを共有する頂点の 3 頂点が次の TWN の中心三角形であれば、このシルエットストリップによる入力を行うことができます。つまり、前の TWN の最後の近接三角形が今回の中心三角形になるように頂点を入力していくことになります。
入力する頂点の順序は以下のとおりです。
- 最初の 6 頂点はシルエットトライアングルと同じです。中心三角形の第 2 頂点、第 3 頂点、最後に指定した頂点が次の中心三角形の第 1 ~第 3 頂点となります
- 中心三角形の第 1 頂点と第 3 頂点が形成するエッジを共有する近接三角形のもう 1 つの頂点
- 中心三角形の第 2 頂点と第 3 頂点が形成するエッジを共有する近接三角形のもう 1 つの頂点
- 中心三角形の第 2 頂点、第 3 頂点、3. で指定した頂点が新たな中心三角形の第 1 ~第 3 頂点となり、以後は 2. と 3. の手順を繰り返します
シルエットストリップによる入力を終了する、もしくは次のシルエットストリップを入力したい場合は、3. で指定する頂点の前に中心三角形の第 3 頂点と同じ頂点を入力して終端指定を行います。
上図のオブジェクトを例に(3、8、7)が終端の中心三角形とすると、
シルエットストリップによるインデックス:1、4、2、3、0、8、6、7、5、7、9
のようになります。強調してある "7" の頂点が終端指定です。
終端指定を行ったあとに続けてシルエットストリップの入力を行うには、新たな TWN を構成している 6 頂点の入力から開始します。そのとき、新たなシルエットストリップの最初の中心三角形が glFrontFace
での指定と逆(裏)になる場合は、最初の頂点の入力で同じ頂点を 2 回入力してください。つまり、先の例であれば、
裏から始まるシルエットストリップによるインデックス:1、1、4、2、3、0、8、6、7、5、7、9
のようになります。
終端指定は複数のシルエットストリップを入力する際の区切りに使用しますが、最後のシルエットストリップの入力で終端指定を行わずに、シルエットストリップに接続するような三角形を続けて入力した場合にシルエットエッジが二重に描画される可能性があります。また、シルエットに対してアルファブレンドなどを行う場合は、すべてのシルエットストリップの入力で終端指定を行う必要があります。
中心三角形が縮退している場合、終端指定が行われたと判定されることに注意してください。
9.3.8. オープンエッジ
オープンエッジとは、ほかの三角形とエッジを共有していない中心三角形のエッジのことであることは説明しました。エッジを共有する三角形が存在しないため、そのインデックスの指定方法は通常と異なり、中心三角形のもう 1 つの頂点を指定することになります。ちょうど近接三角形を折り返したような感じです。
図 9-5 のオブジェクトで頂点 0 が存在しない場合を例にすると、
シルエットトライアングルでの(1、4、3)のインデックス:1、4、2、3、4、6
シルエットストリップでのインデックス:1、4、2、3、4、6、5、6、7
のようになります。
オープンエッジには、シルエットエッジとは異なる設定を行わなければなりません。詳細については「9.3.3. 予約ユニフォーム」を参照してください。
9.3.9. シルエットエッジの生成
シルエットエッジは、TWN の中心三角形の向きが表、近接三角形の向きが裏であるときに、中心三角形のエッジ上に新たな矩形ポリゴンを生成して描画されます。中心三角形と近接三角形が共有する 2 つの頂点(1 と 2)と、その頂点から法線(n1 と n2)の方向に追加された 2 つの頂点(1' と 2')によって(2 つの三角形ポリゴンで構成される)矩形ポリゴンが形成されます。
中心三角形の頂点の座標を(x, y, z, w)、法線を(nx, ny, nz)とすると、追加される頂点の座標(x', y', z', w')は以下の式で計算されます。
x' = x + x_factor * nx * w_scale
y' = y + y_factor * ny * w_scale
z' = z
w' = w
x_factor と y_factor、w_scale に適用される値は、シルエットエッジの幅に関する予約ユニフォームで設定します。
9.4. Catmull-Clark サブディビジョンシェーダ
Catmull-Clark サブディビジョンシェーダは、四角形ポリゴンとその周囲の頂点を用いて頂点群を分割し、ポリゴンを滑らかにするシェーダです。以降、この節でサブディビジョンと記述している場合は、この Catmull-Clark サブディビジョンのことを指しています。
9.4.1. サブディビジョンパッチ
ポリゴンへのサブディビジョンの適用は、四角形のみで構成されたポリゴンの集合(Catmull-Clark サブディビジョンパッチ。以降、サブディビジョンパッチ)をシェーダに投入することで行われます。サブディビジョンパッチは、対象となる四角形(中心四角形)とその 4 頂点が形成するエッジを共有している四角形群から構成されています。
サブディビジョンパッチは、すべてが四角形で構成されているポリゴンモデルにしか適用できません。
中心四角形の各頂点は通常、頂点 5、6、10 のように 4 本のエッジを形成しますが、頂点 9 のように 5 本のエッジを形成する頂点や 3 本しかエッジを形成しない頂点が存在する場合、その頂点を特異点と呼び、形成するエッジの本数を価数と呼びます。サブディビジョンパッチが許容する特異点は中心四角形の 1 つのみで、その価数は 3 ~ 12 です。
サブディビジョンパッチの中心四角形に特異点が含まれている場合、特異点からインデックスの指定を開始しなければなりません。また、同じ特異点を含む中心四角形のサブディビジョンパッチがほかに存在する場合、それらのサブディビジョンパッチは連続して入力されなければなりません。場合によってはメッシュに穴が開いてしまいます。
9.4.2. シェーダファイル
頂点シェーダとリンクさせるシェーダファイルは、サブディビジョンシェーダが必要とする頂点属性以外でフラグメント処理のために出力されている頂点属性の数から決まります。
サブディビジョン: DMP_subdivisionN.obj
N はサブディビジョンシェーダが必要とするもの以外の頂点属性の数(1 ~ 6)です。
9.4.3. 予約ユニフォーム
サブディビジョンシェーダの予約ユニフォームには、以下のものが存在します。これらの予約ユニフォームは初期値を持たないため、必ず値を設定しなければなりません。
細分割レベル
細分割レベル(dmp_Subdivision.level
)には、シェーダ処理による分割の細かさを glUniform1f()
で設定します。レベルが高い(数値が大きい)ほど細かく分割されます。最も低い 0 では、サブディビジョンパッチの中心に頂点を 1 つ加え、サブディビジョンパッチに含まれている元の頂点座標の調整が行われます。
クォータニオンの使用
シェーダによって新たに生成された頂点の、頂点座標以外の頂点属性は補間されて出力されますが、クォータニオンは特別な分割処理が必要であるため、シェーダにはクォータニオンが投入されることを通知しなければなりません。
クォータニオンの投入の有無(dmp_Subdivision.fragmentLightingEnabled
)には、クォータニオンを投入する場合は GL_TRUE
を、投入しない場合は GL_FALSE
を glUniform1i()
で設定します。
予約ユニフォーム |
種別 |
設定する値 |
---|---|---|
|
|
細分割レベルを指定する。 0(細分割レベル:低) |
|
|
フラグメントライティングで必要となるクォータニオンを投入するかどうかを指定する。
|
9.4.4. 頂点シェーダの設定
サブディビジョンシェーダが必要とする頂点属性は、頂点座標です。頂点座標は出力するレジスタ番号の一番小さな番号で頂点シェーダから出力します。
設定すべき出力頂点属性は、頂点座標の "position
" とそのほかの属性のいずれか 1 つ、合計で 2 つ以上です。
サブディビジョンシェーダで必要な頂点座標のほかに頂点カラーを頂点シェーダで出力する場合、シェーダファイル DMP_subdivision1.obj をリンクし、頂点シェーダの #pragma output_map
は以下のように宣言します。
#pragma output_map ( position , o0 ) #pragma output_map ( color , o1 )
フラグメントライティングでクォータニオンを必要とする場合、クォータニオンは頂点座標を出力するレジスタの次に小さな番号の出力レジスタで出力しなければなりません。
#pragma output_map ( position , o0 ) #pragma output_map ( quaternion , o1 ) #pragma output_map ( color , o2 )
9.4.5. 頂点データの入力
Catmull-Clark サブディビジョンシェーダによる細分割処理を行いたい場合は、glDrawElements()
の mode
に GL_GEOMETRY_PRIMITIVE_DMP
を渡して呼び出してください。glDrawArrays()
は使用できません。頂点インデックスも頂点バッファを経由して使用しなければなりません。
9.4.6. サブディビジョンパッチのインデックス
サブディビジョンパッチに含まれる頂点の数は、特異点の価数(3 ~ 12)によって変化します。そのため、サブディビジョンパッチは入力する頂点数が固定ではありません。
最初にサブディビジョンパッチのサイズを入力します。サブディビジョンパッチのサイズには、含まれる頂点の数(特異点の価数× 2 + 8。最小 14、最大 32、特異点がなければ 16 )を指定します。32 を超えるサイズのパッチが入力された場合の動作は不定です。
サブディビジョンパッチのインデックスは以下の順で指定します。
- 中心四角形の 4 頂点(順番は
glFrontFace()
での指定に従い、特異点があれば特異点から開始する) - 中心四角形の周囲の頂点(順番は
glFrontFace()
での指定に従う)
図 9-9 のサブディビジョンパッチを例に反時計回りが表とすると、
サブディビジョンパッチのインデックス: 18、9、5、6、10、8、4、0、1、2、3、7、11、17、16、15、14、13、12
のようになります。先頭の 18 はサブディビジョンパッチのサイズです。
この後に続けて、特異点の頂点 9 を含む中心四角形のサブディビジョンパッチを投入していきます。下図の例では、上記のサブディビジョンパッチのインデックスに続いて
サブディビジョンパッチのインデックス:...、18、9、10、16、15、5、6、7、11、17、21、20、19、18、14、13、12、8、4
となります。
9.5. ループサブディビジョンシェーダ
ループサブディビジョンシェーダは、三角形ポリゴンとその周囲の頂点を用いて頂点群を分割し、ポリゴンを滑らかにするシェーダです。以降、この節でサブディビジョンと記述している場合は、このループサブディビジョンのことを指しています。
9.5.1. サブディビジョンパッチ
ポリゴンへのサブディビジョンの適用は、ループサブディビジョンパッチ(以降、サブディビジョンパッチ)をシェーダに投入することで行われます。サブディビジョンパッチは、対象となる三角形(中心三角形)とその 3 頂点それぞれが形成するエッジを共有している頂点群から構成されています。
中心三角形の各頂点が形成するエッジの本数を価数と呼びます。
図 9-11 で頂点 0、1、2 を中心三角形とした場合、頂点 0 の価数は 6、頂点 1 は 7、頂点 2 は 6 となります。サブディビジョンパッチが許容する各頂点の価数は 3 ~ 12 で、中心三角形の 3 頂点の価数の合計が 29 以下でなければなりません。
もし、価数が 2 の頂点(中心三角形のほかの頂点以外にエッジを共有する頂点がない)をサブディビジョンパッチに含めたい場合は、仮想の頂点を増やすことで対応することができます。
9.5.2. シェーダファイル
頂点シェーダとリンクさせるシェーダファイルは、価数以外にサブディビジョンシェーダへ出力する頂点属性を設定した出力レジスタの数から決まります。頂点座標は必ず出力しなければならないため、出力レジスタの数は 1 以上になります。
サブディビジョン:DMP_loopSubdivisionN.obj
N は価数以外に頂点属性を設定した出力レジスタの数(1 ~ 4)です。
9.5.3. 予約ユニフォーム
ループサブディビジョンシェーダの予約ユニフォームは、Catmull-Clark サブディビジョンシェーダと同じです。詳細については、「9.4.3. 予約ユニフォーム」を参照してください。ループサブディビジョンシェーダの予約ユニフォームは初期値を持たないため、必ず値を設定しなければなりません。
細分割レベルが 0 の場合は新たな頂点は追加されませんが、サブディビジョンパッチに含まれている元の頂点座標の調整が行われます。
9.5.4. 頂点シェーダの設定
サブディビジョンシェーダの処理に必要なのは、頂点座標と価数の 2 つです。これらの頂点属性を頂点シェーダから出力する順番は、出力レジスタ番号の小さい方から、頂点座標、そのほかの頂点属性(あれば)、価数の順です。
設定すべき出力頂点属性は、頂点座標が "position
"、価数が "generic
" です。
サブディビジョンシェーダで必要な頂点座標のほかに頂点カラーを頂点シェーダで出力する場合、シェーダファイル DMP_loopSubdivision2.obj(頂点座標も出力レジスタ数に含まれるため)をリンクし、頂点シェーダの #pragma output_map
は以下のように宣言します。
#pragma output_map ( position , o0 ) #pragma output_map ( color , o1 ) #pragma output_map ( generic, o2 )
フラグメントライティングでクォータニオンを必要とする場合、クォータニオンは頂点座標を出力するレジスタの次に小さな番号の出力レジスタで出力しなければなりません。また、価数以外の出力レジスタの数が 4 までに制限されていますので、価数以外の頂点属性が 5 つ以上存在する場合は、1 つのレジスタに複数の頂点属性をパックする必要があります。ただし、クォータニオンをほかの頂点属性とパックすることはできません。
#pragma output_map ( position , o0 ) #pragma output_map ( quaternion , o1 ) #pragma output_map ( color , o2 ) #pragma output_map ( texture0 , o3.xy ) #pragma output_map ( texture1 , o3.zw ) #pragma output_map ( generic , o4 )
9.5.5. 頂点データの入力
ループサブディビジョンシェーダによる細分割処理を行いたい場合は、glDrawElements()
の mode
に GL_GEOMETRY_PRIMITIVE_DMP
を渡して呼び出してください。glDrawArrays()
は使用できません。頂点インデックスも頂点バッファを経由して使用しなければなりません。
9.5.6. サブディビジョンパッチのインデックス
サブディビジョンパッチに含まれる頂点の数は、中心三角形の価数の合計によって変化します。そのため、サブディビジョンパッチは入力する頂点数が固定ではありません。
最初にサブディビジョンパッチのサイズを入力します。サブディビジョンパッチのサイズには、中心三角形を構成する頂点の価数の合計 + 3 を指定します。
サブディビジョンパッチのインデックスは以下の順で指定します。
- 中心三角形を構成する 3 頂点(順番は
glFrontFace()
での指定に従い、先頭から v0、v1、v2 とする)のインデックス - v0 とエッジを共有する頂点すべて(順番は任意ですが、同じ頂点を含むほかのサブディビジョンパッチも同じ順序でなければなりません)
- v1 とエッジを共有する頂点すべて(順番は任意ですが、同じ頂点を含むほかのサブディビジョンパッチも同じ順序でなければなりません)
- v2 とエッジを共有する頂点すべて(順番は任意ですが、同じ頂点を含むほかのサブディビジョンパッチも同じ順序でなければなりません)
- 固定値 12 と中心三角形の 3 頂点( "12"、v0、v1、v2 の順)
- v0 と v2 と三角形を形成する中心三角形以外の頂点(e00 とする)
- v0 と v1 と三角形を形成する中心三角形以外の頂点(e10 とする)
- v1 と v2 と三角形を形成する中心三角形以外の頂点(e20 とする)
- v0 とエッジを共有し、e00 から反時計回りで隣接している頂点
- v1 とエッジを共有し、e10 から反時計回りで隣接している頂点
- v2 とエッジを共有し、e20 から反時計回りで隣接している頂点
- v0 とエッジを共有し、e10 から時計回りで隣接している頂点
- v1 とエッジを共有し、e20 から時計回りで隣接している頂点
- v2 とエッジを共有し、e00 から時計回りで隣接している頂点
図 9-11 のサブディビジョンパッチを例に反時計回りが表とすると、
サブディビジョンパッチのインデックス:
22、 0、1、2、 1、2、12、3、4、5、 2、0、5、6、7、8、9、 0、1、9、10、11、12、
12、0、1、2、 12、5、9、 3、6、10、 4、11、8
のようになります。1 行目の 22 はサブディビジョンパッチのサイズです。2 行目の 12 が固定値です。
ほかのサブディビジョンパッチで v0 と同じ頂点を中心三角形に使用する場合は、今回の順番(1、2、12、3、4、5)でエッジを共有する頂点を指定しなければなりません。v1 と v2 も同様です。
サブディビジョンパッチでは頂点が重複して指定されることになりますが、頂点処理後のキャッシュにヒットするため、事実上パフォーマンスに関するペナルティは発生しません。
9.6. パーティクルシステムシェーダ
パーティクルシステムシェーダは、ベジェ曲線に沿って大量のポイントスプライト(パーティクル)を描画するパーティクルシステムのためのシェーダです。
パーティクルが描画されるベジェ曲線はシェーダに入力される 4 つの制御点で定義されます。制御点はそれぞれが持つバウンディングボックス内でランダムな位置に決定され、それによってベジェ曲線も変化します。
パーティクルのカラー、サイズ、テクスチャ座標の回転角度などはベジェ曲線上の位置で補間されて決定されます。
9.6.1. シェーダファイル
頂点シェーダとリンクさせるシェーダファイルは、パーティクルシステムシェーダがサポートする機能から決まります。
パーティクルシステム: DMP_particleSystem_X_X_X_X.obj
X は 0 または 1 でパーティクルシステムの機能を制御し、先頭から、パーティクル時間のクランプの ON/OFF、テクスチャ座標回転の ON/OFF、パーティクルカラーの RGBA 成分使用/アルファ成分のみ使用、テクスチャ座標 2 の出力を制御することができます。必ずしも、0 が OFF、1 が ON ではありませんので、下表を参照してリンクするシェーダファイルを決定してください。
ファイル名 |
時間のクランプ |
テクスチャ座標回転 |
RGBA カラー |
テクスチャ座標 2 |
---|---|---|---|---|
*_0_0_0_0.obj |
クランプする |
使用する |
なし(アルファのみ) |
出力しない |
*_0_0_0_1.obj |
クランプする |
使用する |
なし(アルファのみ) |
出力する |
*_0_0_1_0.obj |
クランプする |
使用する |
使用する |
出力しない |
*_0_0_1_1.obj |
クランプする |
使用する |
使用する |
出力する |
*_0_1_0_0.obj |
クランプする |
なし |
なし(アルファのみ) |
出力しない |
*_0_1_0_1.obj |
クランプする |
なし |
なし(アルファのみ) |
出力する |
*_0_1_1_0.obj |
クランプする |
なし |
使用する |
出力しない |
*_0_1_1_1.obj |
クランプする |
なし |
使用する |
出力する |
*_1_0_0_0.obj |
なし |
使用する |
なし(アルファのみ) |
出力しない |
*_1_0_0_1.obj |
なし |
使用する |
なし(アルファのみ) |
出力する |
*_1_0_1_0.obj |
なし |
使用する |
使用する |
出力しない |
*_1_0_1_1.obj |
なし |
使用する |
使用する |
出力する |
*_1_1_0_0.obj |
なし |
なし |
なし(アルファのみ) |
出力しない |
*_1_1_0_1.obj |
なし |
なし |
なし(アルファのみ) |
出力する |
*_1_1_1_0.obj |
なし |
なし |
使用する |
出力しない |
*_1_1_1_1.obj |
なし |
なし |
使用する |
出力する |
表中の "*" は "DMP_particleSystem" の略です。
9.6.2. 予約ユニフォーム
パーティクルシステムシェーダの予約ユニフォームには、以下のものが存在します。これらの予約ユニフォームは初期値を持たないため、必ず値を設定しなければなりません。
カラー
パーティクルカラー(dmp_PartSys.color
)には、各制御点でのパーティクルのカラーを 4x4 行列(1 行目に第 1 制御点の RGBA、2 行目に第 2 制御点の RGBA、以降同様)にして、glUniformMatrix4fv()
で設定します。この設定はリンクするシェーダファイルが RGBA カラーを使用するファイル(DMP_particleSystem_X_X_1_X.obj)でのみ有効です。
RGBA カラーを使用する場合は、アルファ成分のみを使用する場合に比べてパフォーマンスが悪くなります。カラー成分を使用しない場合は、アルファ成分のみを使用するシェーダファイル(DMP_particleSystem_X_X_0_X.obj)をリンクすることをお勧めします。
アスペクト
パーティクルのアスペクト(dmp_PartSys.aspect
)には、各制御点でのパーティクルのサイズ、テクスチャ座標の回転角、テクスチャ座標のスケーリング、アルファ成分値を 4x4 行列(1 行目から、第 1、第 2、第 3、第 4 制御点)にして、glUniformMatrix4fv()
で設定します。
サイズ
パーティクルのサイズ(アスペクトの第 1 列)には、1.0 以上の値を設定します。
パーティクルのサイズの最小値と最大値はパーティクルサイズ(dmp_PartSys.pointSize
)に glUniform2fv()
で設定します。パーティクルの描画は画面サイズを考慮しなければなりませんので、ビューポート(dmp_PartSys.viewport
)にはビューポートの幅と高さの逆数を glUniform2fv()
で設定してください。また、パーティクルのサイズに距離減衰を適用する場合は、距離減衰の因子(dmp_PartSys.distanceAttenuation
)に以下の減衰後サイズの計算式で使用する減衰係数(a、b、c)を glUniform3fv()
で設定してください。
derived_size は距離減衰適用後のサイズ、size は元のサイズ、d は視点からの距離です。
テクスチャ座標
テクスチャ座標に対して、回転(アスペクトの第 2 列)とスケーリング(アスペクトの第 3 列)を各制御点で指定することができます。
パーティクルシステムが出力するテクスチャ座標は、テクスチャ座標 0 とテクスチャ座標 2 です。回転はどちらのテクスチャ座標でもサポートしていますが、スケーリングはテクスチャ座標 2 のみがサポートしています。これらの設定はリンクするシェーダファイルで有効・無効を制御することができます。
- テクスチャ座標 2 の出力する(DMP_particleSystem_X_X_X_1.obj)・出力しない(DMP_particleSystem_X_X_X_0.obj)
- テクスチャ座標の回転およびスケーリングを行う(DMP_particleSystem_X_0_X_X.obj)・行わない(DMP_particleSystem_X_1_X_X.obj)
回転角はラジアンで設定します。時計回りが正の値です。
テクスチャ座標 0 に出力されるテクスチャ座標は、パーティクルの左下が(0, 0)、右下が(1, 0)、左上が(0, 1)、右上が(1, 1)です。テクスチャ座標 2 に出力されるテクスチャ座標は、パーティクルの左下が(-1, -1)、右下が(1, -1)、左上が(-1, 1)、右上が(1, 1)です。
設定した回転角を A、スケーリング値を R とすれば、テクスチャ座標 0 は、
左下:( 0.5×(1.0 + (-cosA + sinA)), 0.5×(1.0 + (-cosA - sinA)) )
右下:( 0.5×(1.0 + (cosA + sinA)), 0.5×(1.0 + (-cosA + sinA)) )
左上:( 0.5×(1.0 + (-cosA - sinA)), 0.5×(1.0 + (cosA - sinA)) )
右上:( 0.5×(1.0 + (cosA - sinA)), 0.5×(1.0 + (cosA + sinA)) )
となり、テクスチャ座標 2 は、
左下:( R×(-cosA + sinA), R×(-cosA - sinA) )
右下:( R×(cosA + sinA), R×(-cosA + sinA) )
左上:( R×(-cosA - sinA), R×(cosA - sinA) )
右上:( R×(cosA - sinA), R×(cosA + sinA) )
となります。
アルファ成分
パーティクルのアルファ成分(アスペクトの第 4 列)には、0.0 ~ 1.0 の値を設定します。アルファ成分のみを使用するシェーダファイル(DMP_particleSystem_X_X_0_X.obj)をリンクした場合は、この設定が使用されます。
発生数
パーティクルの最大発生数(dmp_PartSys.countMax
)には、(発生させるパーティクルの最大数 - 1 )の値を glUniform1fv()
で設定します。0.0 以上の値を設定しなければなりません。ただし、シェーダプログラムの実装により、パーティクルの発生数は最大で 255 個に制限されており、この予約ユニフォームで 256 より大きい値を設定してもパーティクルは 255 個までしか発生しません。
実行時間と速度
パーティクルシステムには時間の概念があります。パーティクルシステムの時間(dmp_PartSys.time
)には、現在時間を glUniform1fv()
で設定します。この現在時間はパーティクルごとに実行時間へとランダムに変換され、パーティクルは実行時間の経過で第 1 制御点から第 4 制御点へと移動していきます。パーティクルは実行時間が 0.0 の時点で第 1 制御点に発生し、1.0 の時点で第 4 制御点に到達します。
実行時間をクランプするシェーダファイル(DMP_particleSystem_0_X_X_X.obj)をリンクした場合、実行時間が 1.0 以上になったパーティクルは描画されなくなります。そのため、アプリケーションで実行時間をリセットしなければ、単純に時間を経過させていると、ある時点からパーティクルが発生しなくなってしまいます。
実行時間をクランプしないシェーダファイル(DMP_particleSystem_1_X_X_X.obj)をリンクした場合、実行時間が 0.0 ~ 1.0 の範囲を繰り返します。つまり、第 4 制御点に到達したパーティクルは再び第 1 制御点から発生します。
パーティクルの速度(dmp_PartSys.speed
)には、パーティクルが進む速度を glUniform1fv()
で設定します。
ランダム値
制御点のバウンディングボックス内での位置やパーティクルの実行時間の決定には、ランダム関数が使用されています。アプリケーションからは、このランダム関数で使用するランダムシードと係数を指定することができます。
ランダム関数は以下のような乱数発生関数(Pseudo random number generator のアルゴリズム)に似た実装がなされています。
ランダムシード(dmp_PartSys.randSeed
)には、ベジェ曲線の x 成分、y 成分、z 成分、パーティクルの実行時間で上式の X0 にあたる値を順番に配列にして glUniform4fv()
で設定します。
ランダム関数の係数(dmp_PartSys.randomCore
)には、上式の(a, b, m, 1/m)を glUniform4fv()
で設定します。
予約ユニフォーム |
種別 |
設定する値 |
---|---|---|
|
|
各制御点でのカラーを指定する。 (R, G, B, A) * 4vec 各成分は 0.0 ~ 1.0 |
|
|
各制御点でのアスペクトを指定する。 (perticle_size, rotation_angle, scale, alpha) * 4vec perticle_size は 1.0 以上、alpha は 0.0 ~ 1.0 |
|
|
パーティクルシステムの現在時間を指定する。 |
|
|
パーティクルの進む速度を指定する。 0.0 以上 |
|
|
パーティクルの最大発生数を(最大発生数 - 1)で指定する。 0.0 以上 |
|
|
各ランダム関数のランダムシードを指定する。 (ベジェ曲線の x 成分にかかる値, ベジェ曲線の y 成分にかかる値, ベジェ曲線の z 成分にかかる値, パーティクルの実行時間にかかる値) |
|
|
ランダム関数の係数を指定する。 (a, b, m, 1/m) |
|
|
距離減衰の因子を指定する。 |
|
|
ビューポートを以下の計算式で指定する。 (1 / viewport.width, 1 / viewport.height) |
|
|
パーティクルサイズの最小値と最大値を指定する。 それぞれ 0.0 以上 |
9.6.3. 頂点シェーダの設定
パーティクルシステムシェーダの処理に必要なのは、1 つの制御点に対して頂点座標と頂点座標を中心とするバウンディングボックスの xyz 成分の半径サイズをクリップ座標系に変換した 4x4 行列です。これらの頂点属性を頂点シェーダから出力する順番は、出力レジスタ番号の小さい方から、頂点座標、変換後行列の 1 行目、2 行目、3 行目、4 行目の順です。
設定すべき出力頂点属性は、頂点座標が "position
"、変換後の行列が "generic
" です。
#pragma output_map ( position , o0 ) #pragma output_map ( generic , o1 ) #pragma output_map ( generic , o2 ) #pragma output_map ( generic , o3 ) #pragma output_map ( generic , o4 )
バウンディングボックスの xyz 成分それぞれの半径サイズを Rx、Ry、Rz とし、射影変換行列を Mproj、モデルビュー変換行列を Mmodelview とすると、クリップ座標系への変換は以下の式で表されます。
シェーダアセンブラで記述すると以下のようになります。aBoundingBox
がアプリケーションから入力された半径サイズの xyz 成分をベクトルにしたもの、vBoundingBox1
~ vBoundingBox4
がパーティクルシステムシェーダに出力される行列です。このコード例ではバウンディングボックスの半径サイズをアトリビュートで入力していますが、パーティクルシステムシェーダが必要とするデータは 4 頂点分だけですので、頂点座標も含めて、すべてをユニフォームで設定する実装も可能です。
mov TEMP_BOX[0], CONST_0 mov TEMP_BOX[1], CONST_0 mov TEMP_BOX[2], CONST_0 mov TEMP_BOX[3], CONST_0 mov TEMP_BOX[0].x, aBoundingBox.x mov TEMP_BOX[1].y, aBoundingBox.y mov TEMP_BOX[2].z, aBoundingBox.z m4x4 TEMP_MAT, MATRIX_Project, MATRIX_ModelView m4x4 TEMP_MAT, TEMP_MAT, TEMP_BOX mov vBoundingBox1, TEMP_MAT[0] mov vBoundingBox2, TEMP_MAT[1] mov vBoundingBox3, TEMP_MAT[2] mov vBoundingBox4, TEMP_MAT[3]
9.6.4. 頂点データの入力
glDrawArrays()
での呼び出しには対応していません。パーティクルシステムシェーダを使用する場合は、glDrawElements()
の mode
に GL_GEOMETRY_PRIMITIVE_DMP
を渡して呼び出してください。