ポイントシェーダやラインシェーダなどのジオメトリシェーダで生成したプリミティブも、最終的にはすべて三角形プリミティブに変換されて生成されます(トライアングル生成、トライアングルセットアップ)。そして、生成された三角形に対してカリング処理やクリッピング処理、ウィンドウ座標変換処理が行われ、ラスタライズでフラグメントの集合に変換されます。OpenGL ES 2.0 と異なり、シザーテストはラスタライズの段階で行われます。これ以降の処理は、生成されたフラグメントが対象となります。
ラスタライズ以降の処理は固定パイプラインで実装されているため、フラグメントに割り当てることができる頂点属性は固定されています。主な頂点属性としては以下のものがあります。
- ウィンドウ座標
- デプス値
- テクスチャ座標およびその偏微分値
- クォータニオン
- 視点ベクトル
- 頂点カラー(絶対値を求めてからラスタライズされます)
10.1. カリング
生成された三角形(ポリゴン)は頂点の指定順によって、視点に対して表面を向けているか、裏面を向けているかが判定されます。カリングはこの表裏判定をもとに、ポリゴンをラスタライズするかどうかを判断する機能です。
10.1.1. 表裏判定
ポリゴンの表裏はウィンドウ座標系での三角形の頂点がどのような順序で指定されているかで判定されます。判定方法としては、時計回りに指定されているときに表とするか、反時計回りに指定されているときに表とするかの 2 種類があり、glFrontFace()
で指定することができます。
void glFrontFace(GLenum mode);
mode
には、時計回り(GL_CW
)または反時計回り(GL_CCW
)を指定します。デフォルトは反時計回り(GL_CCW
)です。
10.1.2. 使用方法
カリングの使用方法は OpenGL と同じです。
カリングの有効・無効
カリングの有効・無効の制御は、cap
に GL_CULL_FACE
を渡して glEnable()
または glDisable()
を呼び出すことで行います。現在の設定を取得するには、cap
に GL_CULL_FACE
を渡して glIsEnabled()
を呼び出してください。デフォルトではカリングは無効になっています。
カリング面の指定
ラスタライズしない面(カリング面)の指定は glCullFace()
で行います。
void glCullFace(GLenum mode);
mode
に指定するカリング面は、以下の値から選択することができます。
設定値 |
カリング面 |
---|---|
|
表 |
|
裏 |
|
両面 |
10.2. クリッピング
クリッピングは、指定されたクリップ平面で定義される半空間とビューボリュームが交差する領域(クリップボリューム)でプリミティブをクリップする(切り抜く)機能です。3DS のクリッピングは OpenGL ES 1.1 のクリッピング相当の機能を持っていますが、その制御はすべて予約ユニフォームで行われます。また、クリッピング対象の三角形に対して新たな頂点の生成と三角形の生成を行い、複数の三角形に分解するように実装されています。
GPU の頂点処理で行われる座標変換では 24 ビットの浮動小数点数が使用されるため、ニアとファーの比が大きい場合にファークリップ平面でクリッピング処理が正しく行われないことがあります。なるべく、ニアとファーの比を大きくしないようにクリップボリュームを設定するか、ファークリップ平面付近にポリゴンを配置しないようにしてください。
10.2.1. 予約ユニフォーム
クリッピングの予約ユニフォームには、以下のものが存在します。
クリッピングの有効・無効
クリッピング機能を有効にするには、予約ユニフォーム(dmp_FragOperation.enableClippingPlane
)に glUniform1i()
で GL_TRUE
を設定してください。デフォルトでは無効(GL_FALSE
)に設定されています。
クリップ平面
クリップ平面の指定は 4 つの係数を予約ユニフォーム(dmp_FragOperation.clippingPlane
)に glUniform4f()
で設定することで行われます。4 つの係数を p1、p2、p3、p4 とすると、クリップボリュームは以下の式を満たす点の集合で表されます。
これらの係数はクリップ座標系で定義されていなければなりませんので、OpenGL ES 標準で使用するクリップ平面にモデルビュー変換と透視投影変換を行った値を指定してください。OpenGL ES の仕様と異なり、3DS では Z 座標が 0 ~ -Wc の範囲にクリップされるため、透視投影変換に OpenGL ES 互換の行列をそのまま使用することはできません。デフォルトではすべての係数は 0.0 に設定されています。
射影変換に OpenGL ES 互換の行列を使用する際の注意事項については、関連する内容が「8.4. クリップ座標系の注意事項」にありますので、そちらを参照してください。
予約ユニフォーム |
種別 |
設定する値 |
---|---|---|
|
|
クリッピングの有効/無効を指定する。
|
|
|
クリッピング平面の 4 つの係数を指定する。 (デフォルト)(0.0, 0.0, 0.0, 0.0) |
10.3. ウィンドウ座標系への変換
オブジェクト座標系で表されたポリゴンモデルの頂点座標がウィンドウ座標系へと変換されるまでに、頂点座標は以下の 4 つの処理を経ています。
- モデルビュー変換
オブジェクト座標系から視点座標系へと変換します。
- 射影変換
視点座標系からクリップ座標系へと変換します。変換結果が 0 ≧ Zc ≧ -Wc であることに注意してください。
- 透視法除算
w 値を使って、クリップ座標系から正規化されたデバイス座標系へと変換します。
- ビューポート変換
ビューポートの設定に従って、正規化されたデバイス座標系からウィンドウ座標系へと変換します。
頂点シェーダ(ジオメトリシェーダ)から出力される頂点属性の座標系はクリップ座標系でなければなりません。そのため、一般的に頂点シェーダ内でモデルビュー変換と射影変換を行うことになります。トライアングル生成で生成された三角形は、透視法除算とビューポート変換によって表示領域での位置が確定します。これらの三角形はラスタライズでフラグメントに変換され、フラグメントライティングなどのプロセスで使用されることになります。
この座標変換の流れを図にすると、以下のようになります。
10.3.1. ビューポートの設定
正規化されたデバイス座標系からウィンドウ座標系への変換は以下の式で行われます。
Xw、Yw、Zw :ウィンドウ座標系での座標
Xd、Yd、Zd :正規化されたデバイス座標系での座標
px、py :ビューポートの幅と高さ
ox、oy :ビューポートの中心点
n、f :クリップ空間のニア平面、ファー平面のデプス値
上式の n と f に適用される値は glDepthRangef()
で設定することができます。
void glDepthRangef(GLclampf zNear, GLclampf zFar);
zNear
、zFar
に指定した値は、どちらも 0.0 ~ 1.0 の範囲にクランプされて設定されます。デフォルトでは、zNear
には 0.0、zFar
には 1.0 が指定されています。
px、py、ox、oy はいずれもビューポートの設定値から計算することができます。ビューポートの設定は glViewport()
で行います。
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
x
と y
にはビューポートの始点(左下)座標を指定します。負の値を指定すると GL_INVALID_VALUE
エラーが生成されます。また、4 の倍数でない値を指定した場合は処理性能が低下(偶数ならば約 1/2、奇数ならば約 1/3)します。その場合は、4 の倍数になるようにビューポートを拡張し、透視投影変換行列を拡張されたビューポートで正しく描画されるようにする調整と、余分な領域を描画しないようにシザーテストを適用する処置で回避してください。
width
と height
にはビューポートの幅と高さを指定します。負の値を指定すると GL_INVALID_VALUE
エラーが生成されます。幅と高さの最大値はどちらも 1024 です。
px には width
、py には height
、ox には (x
+ width) / 2、
oy には (y
+ height
) / 2 がそれぞれ適用されます。
10.3.1.1. ビューポートが 1023×1016 より大きな場合に正しく描画されない
ハードウェアの不具合により、glViewport()
でビューポートを設定する際に以下の条件を満たすと正しく描画が行われません。
-
width
が 1023 より大きい設定の場合、ウィンドウ左端の x 座標を 0 としたときに(ウィンドウ座標の x 座標 – x)が 1023 以上となるピクセルを含むポリゴンが描画されると、そのポリゴン全体が描画されません。 -
height
が 1016 より大きい設定の場合、ウィンドウ下端の y 座標を 0 としたときに(ウィンドウ座標の y 座標 – y)が 1016 以上となるピクセルを含むポリゴンが描画されると、GPU がハングアップします。
この問題を回避するには、ビューポートの width
に 1023 より大きい値を設定しない、かつ height
に 1016 より大きい値を設定しないことにより、問題の起こる座標のピクセルが描画されないようにします。
サイズが 1024×1024 のレンダートゥーテクスチャを行う場合は 1023×1016 の領域にだけ描画し、テクスチャとして使用するときに 1023×1016 をテクスチャの有効領域とするようにテクスチャ座標を調整する必要があります。
1024×1024 の全領域に描画したい場合は、ビューポートの width
が 1023、height
が 1016 を超えないようにしつつ、ビューポートのオフセットを変更して描画領域をずらしながら、分割して描画します。例えば、glViewport(0, 0, 512, 512)
、glViewport(512, 0, 512, 512)
、glViewport(0, 512, 512, 512)
、glViewport(512, 512, 512, 512)
の 4 つに分割して描画すれば問題は起こりません。
また、シザリングで問題の領域にピクセルが描かれないようにする方法では、不具合を回避することはできません。
10.3.2. ポリゴンオフセット
ポリゴンオフセットは、ポリゴンをラスタライズしてフラグメントに変換する際のデプス値にオフセットを加算することで、同一平面に重なって存在するポリゴンのように、デプス値の分解能不足によってフラグメントの前後関係が定まらない状態を解決する機能です。ポリゴンオフセットは、ウィンドウ座標変換処理の際に行われます。
ポリゴンオフセットの有効・無効
ポリゴンオフセットの有効・無効の制御は、cap
に GL_POLYGON_OFFSET_FILL
を渡して glEnable()
または glDisable()
を呼び出すことで行います。現在の設定を取得するには、cap
に GL_POLYGON_OFFSET_FILL
を渡して glIsEnabled()
を呼び出してください。デフォルトではポリゴンオフセットは無効になっています。
オフセット値の指定
ポリゴンオフセットが有効であるときに、デプス値に与えられるオフセット値は glPolygonOffset()
で指定することができます。
void glPolygonOffset(GLfloat factor, GLfloat units);
OpenGL では factor
と units
によってオフセット値が決定されますが、3DS では units
のみでオフセット値が決定されます。factor
の値は設定されますが、オフセット値には考慮されません。
オフセット値には、ウィンドウ座標系でのデプス値に違いが現れる最小値(固定値)と units
を乗算した値が適用されます。頂点処理後の頂点座標の z 値が 24 ビット浮動小数点数で実装されていますので、ポリゴンの z 値が 1.0 に近い場合は units
の設定が 128 の倍数単位でなければ効果が得られません。確実に効果を得るには、units
に 128 の倍数を設定してください。
デプスバッファに書き込まれる値は、オフセット値が加算されたあとのデプス値です。
10.3.3. w バッファ
w バッファとは、透視投影を行わずにウィンドウ座標系のデプス値を算出する機能です。w バッファは以下の予約ユニフォームで制御することができます。w バッファを有効にした場合、glDepthRangef()
の設定は無効となります。
デプス値に対するスケール
w バッファ有効時のデプス値は以下の式で計算されます。
Zw = -scalew × Zc
Zw がウィンドウ座標系でのデプス値、Zc がクリップ座標系での z 値、scalew がスケール値です。このスケール値は予約ユニフォーム(dmp_FragOperation.wScale
)に設定された浮動小数点数で、0.0 以外の値が設定されたときに w バッファが有効となります。スケール値は Zw が 0.0 ~ 1.0 の範囲に収まるように設定してください。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
デプス値に対するスケールを指定する。 (デフォルト)0.0 |
ポリゴンオフセットが有効になっている場合
w バッファとポリゴンオフセットの機能がともに有効になっている場合、ポリゴンオフセットのオフセット値には glPolygonOffset()
で指定した units
に Wc(クリップ座標の w 値)を乗算したものが適用されます。
10.4. シザーテスト
シザーテストは、ウィンドウ座標系で指定された範囲の外にあるフラグメントを棄却して、後のプロセスで処理されるフラグメントを削減する機能です。
10.4.1. 使用方法
処理が行われるパイプラインでの位置は異なりますが、仕様自体は OpenGL のシザーテストと変わりはありません。
シザーテストの有効・無効
シザーテストの有効・無効の制御は、cap
に GL_SCISSOR_TEST
を渡して glEnable()
または glDisable()
を呼び出すことで行います。現在の設定を取得するには、cap
に GL_SCISSOR_TEST
を渡して glIsEnabled()
を呼び出してください。デフォルトではシザーテストは無効になっています。無効に設定されている場合、フラグメントの棄却は行われません。
シザーボックスの指定
フラグメントを通過させる範囲(シザーボックス)の指定は glScissor()
で行います。
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
x
と y
はウィンドウ座標系でのシザーボックスの始点(左下)座標です。width
と height
はシザーボックスの幅と高さです。width
または height
に 0 を指定した場合は GL_INVALID_VALUE
のエラーが生成されます。デフォルトでは値が設定されていませんので、シザーテストを有効にしたときはシザーボックスを必ず指定してください。
始点座標のフラグメントはシザーボックスに含まれますが、x 座標が (x
+ width
) または y 座標が (y
+ height
) のフラグメントはシザーボックスに含まれません。
10.5. ラスタライズルール
PICA グラフィックスコアによるポリゴンのラスタライズ(フラグメントの生成)は、以下のルールに従って行われます。
- ピクセルの中心座標 (x + 0.5, y + 0.5)(x および y は整数)がポリゴンの内部にある。
- ポリゴンの辺(エッジ)がピクセルの中心座標を通過している場合は、Bottom-Left の規則に従い、通過している辺が下辺または左辺ならばフラグメントが生成され、上辺または右辺ならばフラグメントが生成されない。
※ X 軸の負の方向を左、Y 軸の負の方向を下とする。
下図は 2 つのポリゴンのラスタライズ結果をもとに、Bottom-Left の規則がどのように適用されるのかを示しています。2 つのポリゴンは (5.5, 0.5), (5.5, 5.5), (0.5, 5.5) の 3 頂点によるポリゴンと、(5.5, 0.5), (0.5, 0.5), (0.5, 5.5) の 3 頂点によるポリゴンとし、前者に対して生成されるフラグメントを赤、後者に対して生成されるフラグメントを青で示しています。
ラスタライズルールは、以下のように適用されています。
2 つのポリゴンの境界部分では、赤いポリゴンの左辺がピクセルの中心を通過しているため、赤で塗られます。
x 座標 0.5 が中心であるピクセルでは、青いポリゴンの左辺がピクセルの中心を通過しているため、青で塗られます。
x 座標 5.5 が中心であるピクセルでは、赤いポリゴンの右辺がピクセルの中心を通過しているため、塗られません。
y 座標 0.5 が中心であるピクセルでは、青いポリゴンの下辺がピクセルの中心を通過しているため、青で塗られます。
y 座標 5.5 が中心であるピクセルでは、赤いポリゴンの上辺がピクセルの中心を通過しているため、塗られません。