Platform SDK: DirectX

ステップ 2.3 : 3D トランスフォームを実行する

[Visual Basic]

ここでは、C および C++ でのアプリケーション開発について説明する。Visual Basic については、「Direct3D 直接モード Visual Basic チュートリアル」を参照すること。

[C++]

シーンはほぼ完成しているが、立方体の頂点を手動で回転、平行移動、およびスケーリングする処理が残っている。まず、D3DTLVERTEX 構造体の配列 g_pvCube に立方体の未トランスフォーム データを格納する。最初の 3 つのパラメータは、スクリーン座標の各頂点の未トランスフォームの位置座標を表している。

    g_pvCube[0] = D3DTLVERTEX( D3DVECTOR(-1,-1,-1 ), 0, 0xffffffff, 0, 0, 0 );
    g_pvCube[1] = D3DTLVERTEX( D3DVECTOR( 1,-1,-1 ), 0, 0xff00ffff, 0, 0, 0 );
    g_pvCube[2] = D3DTLVERTEX( D3DVECTOR(-1, 1,-1 ), 0, 0xffff00ff, 0, 0, 0 );
    g_pvCube[3] = D3DTLVERTEX( D3DVECTOR( 1, 1,-1 ), 0, 0xff0000ff, 0, 0, 0 );
    g_pvCube[4] = D3DTLVERTEX( D3DVECTOR(-1,-1, 1 ), 0, 0xffffff00, 0, 0, 0 );
    g_pvCube[5] = D3DTLVERTEX( D3DVECTOR( 1,-1, 1 ), 0, 0xff00ff00, 0, 0, 0 );
    g_pvCube[6] = D3DTLVERTEX( D3DVECTOR(-1, 1, 1 ), 0, 0xffff0000, 0, 0, 0 );
    g_pvCube[7] = D3DTLVERTEX( D3DVECTOR( 1, 1, 1 ), 0, 0xff000000, 0, 0, 0 );

次に、プログラマ定義関数 TransformVertices を呼び出して、立方体の 8 つの頂点を手動でトランスフォームする。

    TransformVertices( pd3dDevice, g_pvCube, 8 );

ただし、3D トランスフォームを実装する前に、ビューポートの高さと幅を取得する必要がある。この情報は、レンダリング ウィンドウに合わせてトランスフォーム済み頂点をスケーリングするために使用される。

HRESULT TransformVertices( LPDIRECT3DDEVICE7 pd3dDevice,
                           D3DTLVERTEX* pvVertices, DWORD dwNumVertices )
{
    // ビューポートの幅と高さを取得する。この情報は、レンダリング ウィンドウに合わせて
    // トランスフォーム済み頂点をスケーリングするのに必要である。
    D3DVIEWPORT7 vp;
    pd3dDevice->GetViewport( &vp );
    DWORD dwClipWidth  = vp.dwWidth/2;
    DWORD dwClipHeight = vp.dwHeight/2;

Direct3D は行列を使用してトランスフォームを行う。3D トランスフォームを実行するには、現在の行列セットの値を取得する必要がある。このためには、レンダリング デバイスで IDirect3DDevice7::GetTransform を呼び出す。

    D3DMATRIX matWorld, matView, matProj;
    pd3dDevice->GetTransform( D3DTRANSFORMSTATE_WORLD,      &matWorld );
    pd3dDevice->GetTransform( D3DTRANSFORMSTATE_VIEW,       &matView );
    pd3dDevice->GetTransform( D3DTRANSFORMSTATE_PROJECTION, &matProj );
    

行列連結には行列乗算を使用する。

    D3DMATRIX matTemp = matWorld * matView;
    D3DMATRIX matSet = matTemp * matProj;

上記のコードでは、トランスフォームの連結値が合成行列 matSet, から算出される。連結の際には、処理を行う順序で行列が乗算されていることを確認する。

行列連結の詳細については、「行列連結」を参照すること。

次に、現在の行列セットを使用して各頂点をトランスフォームする。

    for( DWORD i=0; i<dwNumVertices; i++ )
    {
        // 未トランスフォームの頂点の位置を取得する。
        FLOAT x = pvVertices[i].sx;
        FLOAT y = pvVertices[i].sy;
        FLOAT z = pvVertices[i].sz;
 
        // 現在の行列セットでトランスフォームする。
        FLOAT xp = matSet._11*x + matSet._21*y + matSet._31*z + matSet._41;
        FLOAT yp = matSet._12*x + matSet._22*y + matSet._32*z + matSet._42;
        FLOAT zp = matSet._13*x + matSet._23*y + matSet._33*z + matSet._43;
        FLOAT wp = matSet._14*x + matSet._24*y + matSet._34*z + matSet._44;

最後に、スクリーン座標に合わせて頂点をスケーリングする。次のコードでは、まず各座標が wp 値で除算されて、座標が 3D 空間から 2D デバイス座標に平面化される。その次に、x および y 成分がデバイス座標からスクリーン座標に変換される。

        pvVertices[i].sx  = ( 1.0f + (xp/wp) ) * dwClipWidth;
        pvVertices[i].sy  = ( 1.0f - (yp/wp) ) * dwClipHeight;
        pvVertices[i].sz  = zp / wp;
        pvVertices[i].rhw = wp;
    }

3D トランスフォームの詳細については、「3D トランスフォーム」を参照すること。

注 :  ビューポートでデバイス座標がとる範囲は、-1 から +1 の間の値である。また、sz 座標が Z バッファで使用される。

これで立方体の 8 つの頂点をトランスフォームできた。次は、完成したシーンのフレームをレンダリングする。これについては、「ステップ 2.4 : シーンをレンダリングする」で説明する。