Share via


射影トランスフォーム (Direct3D 9)

射影トランスフォームは、カメラの内部の制御と考えることができます。つまり、射影トランスフォームは、カメラのレンズを選ぶことに似ています。このトランスフォームは、3 種類のトランスフォームの中で最も複雑です。射影トランスフォームについては、以下のトピックにまとめて説明します。

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

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

Math fomula

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

Math fomula

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

Math fomula

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

Cuboid diagram

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

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

Math fomula

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

Math fomula

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

Math fomula

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

C++ アプリケーションでは、幅と高さの寸法は、D3DVIEWPORT9 構造体の Width メンバーおよび Height メンバーに直接対応します。

どの式を使用する場合でも、必ず、Zn をできるだけ大きな値に設定します。カメラに極端に近い 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

行列の作成後、IDirect3DDevice9::SetTransform で D3DTS_PROJECTION を指定して行列を設定します。

D3DX ユーティリティ ライブラリには、射影行列を設定する次の関数が用意されています。

W バッファーに有効な射影行列

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

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

Math fomula

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

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