3Dポリゴンの表示


~これでゲームが作れる?~




■今回のファイルは

今回は、三角錐を回してみます。

次のソースを、ダウンロードしてください。

今回も、draw.cpp を書き換えただけです。

■頂点の定義

今回は、頂点を変更します。といっても、さらに簡単になっていて、3次元座標と、頂点色だけです。

struct CUSTOMVERTEX
{
    FLOAT x, y, z;  // 位置
    DWORD color;    // 色
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)

さらに、トライアングルファンを使ってみます。原点と、xy面、yz面及び、zx面の 1.0 の頂点にある三角形3つを表示します。

    CUSTOMVERTEX vertices[] =
    {
        //     x,      y,    z,  color
        {   0.0f,   0.0f, 0.0f, D3DCOLOR_RGBA(0xff, 0xff, 0xff, 0xff), },
        {   1.0f,   0.0f, 0.0f, D3DCOLOR_RGBA(0xff, 0x00, 0x00, 0xff), },
        {   0.0f,   1.0f, 0.0f, D3DCOLOR_RGBA(0x00, 0xff, 0x00, 0xff), },
        {   0.0f,   0.0f, 1.0f, D3DCOLOR_RGBA(0x00, 0x00, 0xff, 0xff), },
        {   1.0f,   0.0f, 0.0f, D3DCOLOR_RGBA(0xff, 0x00, 0x00, 0xff), },
    };

色は、(x,y,z)=(1,0,0) が赤、(0,1,0) が緑、(0,0,1) が青です。

■初期化

今回は、いつもの頂点定義に加え、SetRenderState で、レンダリングするときの設定をしています。
もちろん、通常の描画で行ってもいいのですが、変更が起きないし、SetRenderState は重いらしいので、ここで設定します。

HRESULT InitRender(LPDIRECT3DDEVICE8 lpD3DDEV)
{
    CUSTOMVERTEX vertices[] =
    {
        //     x,      y,    z,  color
        {   0.0f,   0.0f, 0.0f, D3DCOLOR_RGBA(0xff, 0xff, 0xff, 0xff), },
        {   1.0f,   0.0f, 0.0f, D3DCOLOR_RGBA(0xff, 0x00, 0x00, 0xff), },
        {   0.0f,   1.0f, 0.0f, D3DCOLOR_RGBA(0x00, 0xff, 0x00, 0xff), },
        {   0.0f,   0.0f, 1.0f, D3DCOLOR_RGBA(0x00, 0x00, 0xff, 0xff), },
        {   1.0f,   0.0f, 0.0f, D3DCOLOR_RGBA(0xff, 0x00, 0x00, 0xff), },
    };
    HRESULT hr;
    
    hr = lpD3DDEV->CreateVertexBuffer( 5*sizeof(CUSTOMVERTEX),0,
                      D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &pVB);
    if(FAILED(hr)) return E_FAIL;

    VOID* pVertices;
    hr = pVB->Lock( 0, sizeof(vertices), (BYTE**)&pVertices, 0);
    if(FAILED(hr)) return E_FAIL;
    memcpy( pVertices, vertices, sizeof(vertices) );
    pVB->Unlock();

    lpD3DDEV->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    lpD3DDEV->SetRenderState(D3DRS_LIGHTING, FALSE);

    return S_OK;
}

D3DRS_CULLMODE は、ポリゴンを表裏のあるものかどうかの設定をします。 D3DCULL_NONE にすると、表裏の概念がありません。 D3DCULL_CW 等に設定すると、裏を向いた時は、表示をしなくなります。
D3DRS_LIGHTING は、ライトの設定です。ここでは、ライトを当てないようにします。 ライトの計算は、基本的に重いので、なるべく使わないように心がけるべきだと思います。

■描画

ここが、今回、一番ごちゃごちゃしています。

void Render(LPDIRECT3DDEVICE8 lpD3DDEV)
{
    // ローカル、ワールド座標系の変換
    D3DXMATRIX mWorld, mRotX, mRotY, mTrans;
    D3DXMatrixTranslation(&mTrans, 0,0,0);
    float time = (float)timeGetTime();
    D3DXMatrixRotationY(&mRotY, time/600.0f);
    D3DXMatrixRotationX(&mRotX, 0.0f);
    mWorld = mRotX * mRotY * mTrans;
    lpD3DDEV->SetTransform(D3DTS_WORLD, &mWorld);
    
    // ワールド、ビュー座標系の変換
    D3DXMATRIX mView, mProj;
    D3DXMatrixLookAtLH(&mView
                    ,&D3DXVECTOR3(0,3.0f,5.0f)    // カメラ位置
                    ,&D3DXVECTOR3(0,0,0)          // カメラの注目点
                    ,&D3DXVECTOR3(0,1,0)          // 上の向き
                    );
    lpD3DDEV->SetTransform(D3DTS_VIEW,  &mView);
    
    // ビュー、スクリーン座標系の変換
    D3DXMatrixPerspectiveFovLH(&mProj
        ,60.0f*PI/180.0f                        // 視野角
        ,1.0f                                   // アスペクト比
        ,0.01f                                  // 最近接距離
        ,100.0f                                 // 最遠方距離
        );
    lpD3DDEV->SetTransform(D3DTS_PROJECTION, &mProj);
    
    // ポリゴンの描画
    lpD3DDEV->SetStreamSource( 0, pVB, sizeof(CUSTOMVERTEX) );
    lpD3DDEV->SetVertexShader( D3DFVF_CUSTOMVERTEX );
    lpD3DDEV->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 3 );
}

さて、3つのマトリックスを設定する必要があります。

最初は、ローカル-ワールドマトリックスです。
このマトリックスは、ポリゴンの3次元空間での位置を設定します。 mTrans は、位置を指定します。今回は、(0,0,0) なので、原点にポリゴン(の基準位置)はあります。
mRotX, mRotY は、それぞれ、X 軸、Y 軸周りのポリゴンの回転です。 今回は、timeGetTime() で時間を調べて、Y 軸周りに回るようにしてみました。 Y 軸周りの回転は、コマの様にくるくる回ります。

次にワールド-ビューマトリックスです。
これは、カメラの位置です。
今回は、(0,3,5)にカメラをおきます。原点より少し手前上です。
これ以外に、カメラが見ている点を設定します(ポリゴンを見るように原点(ポリゴンの位置)を指定しました)。 また、上の方向を指定します。今回は、Y軸の方向にしました。 ここを変更すると、酔っ払ったアングルができますが、ほとんどこの設定でいいでしょう。

そして、ビュー-スクリーンマトリックスです。
これは、カメラの性能です。
最初の引数で、視野角を設定します。パノラマなどの特殊な効果をするときは、ここを変更します。 アスペクト比は、ピクセルの形です。まぁ、今のディスプレイなら、1.0でしょう (PS 等のコンシューマ機では、320 x 448 の解像度を使う場合があり、このときには、0.5になったりします)。 最近接(最遠方)距離は、手前と、後方がどのくらいまで見えるかで、ポリゴンの大きさから適当に決めます。 今回は、単位をmと見立て、ポリゴンの大きさが、1m x 1m x 1m とすると、1cm から、100m ぐらいの範囲には、 ほぼ収まるだろうと思ったので、この数字にしました。

以上が、各マトリックスの設定です。 この処理は難しく見えますが、定型処理なので、一度作ったらほとんど変更しません。
こんなもんなんだと思ってもらってぜんぜんかまいません。
ただ、

    mWorld * mView * mProj;

の行列を使うと一気にポリゴンのデータから、ディスプレイのスクリーン座標が計算できることは、 覚えておいたほうがいいと思います。SetTransform で複雑な設定をしているように感じるかもしれませんが、 実際には行列の掛け算しかしません。

最後に、トライアングルストリップで、扇形にポリゴンを3枚表示します。

■後片付け

後片付けは、いつものように、使ったものを開放するだけです。

void CleanRender()
{
    RELEASE(pVB);
}

どうでしょうか?これで、とりあえず3次元ポリゴンが表示できます。 理論は、他の本、ページに譲るとして、使って慣れてしまうのがいいと思います。
プロの?ゲーム開発の場でも、各行列がどうなっているのかぜんぜん知らない人もいます。 でも使えてしまうのです。
ようは、設定している変数の意味がわかっているかどうかです。
個人的には、ここで深く考えるよりも、この先にあるもっと面白いことにチャレンジしろって感じです。
でも、ちゃんとした本などで数式もマスターすべきですよ。





もどる

imagire@gmail.com