プロジェクション変換 (Direct3D 9)

プロジェクション変換は、カメラの内部を制御していると考えることができます。これは、カメラ用のレンズを選択することに似ています。 このトランスフォームは、3 種類のトランスフォームの中で最も複雑です。 このプロジェクション変換の説明は、次のトピックにまとめられます。

射影行列とは、通常、スケーリングおよび遠近法による射影です。 射影トランスフォームでは、視錐台を立方体に変換します。 視錐台の近くの端は遠くの端よりも小さいので、このトランスフォームにはカメラの近くのオブジェクトを拡大するという効果があり、これによってシーンに遠近感が生まれます。

視錘台では、ビュー トランスフォーム空間の原点とカメラの間の距離が任意の値 D として定義され、射影行列は次のようになります。

射影行列の図

ビュー行列は、z 方向に - D だけ平行移動することによって、カメラを原点に平行移動します。平行移動行列は、次のようになります。

平行移動行列の図

平行移動行列に射影行列を乗算 (T*P) すると、それらを合成した射影行列ができます。これは、次のようになります。

合成した射影行列の図

次の図は、パースペクティブ (遠近投影) トランスフォームで視錐台を新しい座標空間に変換する方法を示しています。 視錐台が立方体になること、およびシーンの右上角にあった原点が中心に移動することに注目してください。

パースペクティブ トランスフォームで視錐台を新しい座標空間に変更する方法の図

パースペクティブ トランスフォームでは、x 方向と y 方向の限界は -1 と 1 です。 z 方向の限界は、前方面については 0 で、後方面については 1 です。

この行列は、カメラから付近のクリップ面まで、指定された距離に基づいてオブジェクトを平行移動およびスケーリングします。しかし、この行列は視野 (fov) を考慮せず、遠くのオブジェクトに対して生成する z 値はほとんど同じになる可能性があるので、深度比較が困難になります。 この問題を解決するために、次の行列は、ビューポートのアスペクト比を考慮して頂点を調整し、アスペクト比をパースペクティブ射影に合わせます。

パースペクティブ射影の行列の図

この行列では、Zₙ は付近のクリップ面の z 値です。 変数 w、h、および Q には、次のような意味があります。 fovw および fovₖ は、ビューポートの水平方向および垂直方向の視野を表します (ラジアン単位)。

変数の意味の公式

アプリケーションでは、視野角度を使って x と y のスケール係数を定義することは、ビューポートの水平寸法と垂直寸法 (カメラ空間の) を使用するのに比べて不便な場合があります。 数値演算として、次に示す w と h の 2 つの式ではビューポートの寸法を使用します。これらの式は上の公式と同等です。

w および h 変数の意味の公式

これらの式では、Zₙ は付近のクリップ面の位置を表し、変数 Vw と Vₕ はカメラ空間でのビューポートの幅と高さを表しています。

C++ アプリケーションの場合、これら 2 つのディメンションは D3DVIEWPORT9 構造体の Width メンバーと Height メンバーに直接対応します。

どの式を使用する場合でも、必ず、Zₙ をできるだけ大きな値に設定します。カメラに極端に近い z 値は大幅には変化しないからです。 これにより、16 ビットの z バッファーを使う深度比較は多少複雑になります。

ワールド変換とビュー変換と同様に、 IDirect3DDevice9::SetTransform メソッドを呼び出してプロジェクション変換を設定します。

プロジェクション マトリックスの設定

次の ProjectionMatrix サンプル関数は、前面と背面のクリッピング面、および水平および垂直の視野を設定します。 ビューのフィールドは pi ラジアン未満にする必要があります。

D3DXMATRIX 
ProjectionMatrix(const float near_plane, // Distance to near clipping 
                                         // plane
                 const float far_plane,  // Distance to far clipping 
                                         // plane
                 const float fov_horiz,  // Horizontal field of view 
                                         // angle, in radians
                 const float fov_vert)   // Vertical field of view 
                                         // angle, in radians
{
    float    h, w, Q;

    w = (float)1/tan(fov_horiz*0.5);  // 1/tan(x) == cot(x)
    h = (float)1/tan(fov_vert*0.5);   // 1/tan(x) == cot(x)
    Q = far_plane/(far_plane - near_plane);

    D3DXMATRIX ret;
    ZeroMemory(&ret, sizeof(ret));

    ret(0, 0) = w;
    ret(1, 1) = h;
    ret(2, 2) = Q;
    ret(3, 2) = -Q*near_plane;
    ret(2, 3) = 1;
    return ret;
}   // End of ProjectionMatrix

マトリックスを作成した後、D3DTS_PROJECTIONを指定して IDirect3DDevice9::SetTransform で設定します。

D3DX ユーティリティ ライブラリには、プロジェクション マトリックスの設定に役立つ次の関数が用意されています。

W に優しいプロジェクション マトリックス

Direct3D では、ワールド行列、ビュー行列、および射影行列によって変換された頂点の w 成分を利用して、深度バッファーまたはフォグ エフェクトの計算を深度をベースに実行できます。 このような計算では、射影行列で w を正規化して、ワールド空間の z と等価にする必要があります。 つまり、射影行列に 1 ではない (3,4) 係数が含まれる場合、(3,4) 係数の逆数を使ってすべての係数をスケーリングすることで、適切な行列を作成しなければなりません。 対応していない行列を使用すると、フォグ エフェクトと深度バッファーが正しく適用されません。

次の図は、対応していない射影行列、および同じ行列をスケーリングして視点との相対フォグが有効になるようにした行列を示しています。

対応していない射影行列、および視点との相対フォグが有効になっている行列の図

上の図では、すべての変数がゼロ以外の値であるとします。 視線相対霧の詳細については、「 視線相対と Z ベースの深さ」を参照してください。 w ベースの深度バッファリングの詳細については、「 深度バッファー (Direct3D 9)」を参照してください。

Direct3D では、現在設定されている射影行列を使って w ベース深度の計算を実行します。 したがって、トランスフォームで Direct3D を使用しない場合であっても、アプリケーションでは、目的の w ベース機能を取得するために適切な射影行列を設定しておく必要があります。

変換