4面体を回転しながら描画する

timeGetTime を取得して DirectX の D3DXMatrixRotationY でワールド座標を自動的に回転しながら4面体を描画します。
★このページの後部に「D3DXMatrixLookAtLH() と D3DXMatrixPerspectiveFovLH()」の説明があります。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プロジェクトの作成

  1. 空の新規プロジェクト(Trans)を作成して下さい。
    プロジェクトの作成方法は Direct3D デバイスを取得する を参照して下さい。
  2. 「プログラムの説明」を参考にしてソースプログラムファイルを作成して下さい。
    プロジェクト/既存項目の追加から格納したファイルをプロジェクトに追加して下さい。
    ファイル名 ファイルの説明
    Trans.cpp ソースプログラムファイル
  3. 以前は「プロジェクト/プロパティ」から「リンカ/入力/追加する依存関係」でライブラリをリンクしていたのですが、 このプログラムでは #pragma を使っているのでリンクの操作は不要です。
        #pragma once
        #pragma comment(lib,"dxguid.lib")
        #pragma comment(lib,"d3d9.lib")
        #pragma comment(lib,"d3dx9.lib")
        #pragma comment(lib,"winmm.lib")
        
  4. ビルドから実行を選ぶと、コンパイルに続いて実行が行われます。
    ページ先頭の画面が表示されて4面体が回転していたら完成です。

プログラムの説明

3D(三次元)グラフィックスで立体を表示する基本的なプログラムです。
  1. #define と #include と Direct3D の領域です。
    g_pD3D は Direct3D のオブジェクトです。
    g_pDEV は Direct3D のデバイスです。
        #define     NAME    "DirectX9 D3D"
        #include    <d3d9.h>
        #include    <d3dx9.h>
        #pragma once
        #pragma comment(lib,"dxguid.lib")
        #pragma comment(lib,"d3d9.lib")
        #pragma comment(lib,"d3dx9.lib")
        #pragma comment(lib,"winmm.lib")
    
        #define SAFE_DELETE(p)  { if (p) { delete (p);     (p)=NULL; } }
        #define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } }
        #define EMSG(x)     MessageBox(NULL,x,"DirectX D3D",MB_OK);
    
        //Global Area
        LPDIRECT3D9             g_pD3D= NULL;
        LPDIRECT3DDEVICE9       g_pDEV= NULL;
        
  2. 頂点フォーマットの設定です。
    4面体の頂点座標(X,Y,Z)と頂点の色を定義しています。
    D3D で三角形を描画する の頂点座標は画面上のピクセル値でしたが、 今回は DirectX で一般的に使われる三次元座標で定義します。
    三次元座標では、左下を基点にして、「右(X), 上(Y)」に行くほど座標が大きくなります。
    D3DFVF_XYZRHW の場合は、ピクセル座標が固定されていましたが、今回(D3DFVF_XYZ)は座標を回転しながら描画します。
    隣り合った三角形の一辺がつながるように頂点を定義(TRIANGLESTRIP)していきます。
        //頂点フォーマット
        #define D3DFVF_VERTEX  (D3DFVF_XYZ | D3DFVF_DIFFUSE)
        typedef struct _D3DLVERTEX_
        {   float   x,y,z;
            DWORD   color;
        }   D3DVERTEX;
    
        //四面体の定義
        D3DVERTEX vtx[6]=
        { { -5,    0,      5,      0xff0000ff  },   //青の頂点
          {  5,    0,      5,      0xffff0000  },   //赤の頂点
          {  0,    8.16f,  2.11f,  0xffffffff  },   //白の頂点
          {  0,    0,     -3.66f,  0xff00ff00  },   //緑の頂点
          { -5,    0,      5,      0xff0000ff  },   //青の頂点
          {  5,    0,      5,      0xffff0000  }    //赤の頂点
        };
        
  3. WinMain() 関数です。
    参照しないパラメータ名は省略しています。
    実は3Dプログラムの最初の難関が Direct3D デバイスの取得です。
    このプログラムでは最も簡単な方法でデバイスを取得していますが、全ての状況下で動くとは限りません。
    私はハードやプログラムの種類により何種類かの関数を用意して使い分けています。
    このプログラムでは timeGetTime() でY軸を中心に自動的に回転します。
    描画が timeGetTime() で変化するようなプログラムは、メッセージループの中から常に Draw() 関数を呼び出します。
        INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, INT)
        {   MSG     msg;
                     :
                     :
            if (FAILED(Init3DDev(hWnd)))    return FALSE;
    
            ShowWindow(hWnd,SW_SHOWDEFAULT);
            UpdateWindow(hWnd);
            ZeroMemory(&msg,sizeof(msg));
            while(msg.message!=WM_QUIT)
            {   if (PeekMessage(&msg,NULL,0U,0U,PM_REMOVE))
                {   TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else    Draw();
            }
            Cleanup();
            UnregisterClass(NAME,wc.hInstance);
            return TRUE;
        }
        
  4. 出来るだけ簡単な方法でデバイスを取得してみました。
    全ての状況下(ハード構成 etc)で動くとは限らないことをご承知下さい。
    デバイスの取得は Back Buffer を取得して円を描画する を参照して下さい。
        HRESULT Init3DDev(HWND hWnd)
        {   D3DDISPLAYMODE          d3ddm;
            D3DPRESENT_PARAMETERS   d3dpp; 
    
            if (NULL==(g_pD3D=Direct3DCreate9(D3D_SDK_VERSION)))    return E_FAIL;
            if (FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddm)))   return E_FAIL;
            ZeroMemory(&d3dpp,sizeof(d3dpp));
            d3dpp.Windowed= TRUE;
            d3dpp.SwapEffect= D3DSWAPEFFECT_DISCARD;
            d3dpp.BackBufferFormat= d3ddm.Format;
    
            if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,
                                     D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&g_pDEV)))
            return E_FAIL;
            return S_OK;
        }
        
  5. 4面体を描画する関数です。
    Clear() で画面をクリアします。
    BeginScene() でシーンの開始を設定します。
    SetupMatrices() は描画環境を設定する関数です。この後で説明します。
    SetFVF() で頂点フォーマットをセットします。
    DrawPrimitiveUP() で4面体を描画します。4は4面体の面(三角形)の数です。
    EndScene() でシーンの処理を終了します。
    Present() で作成したシーンを描画します。
        void Draw(void)
        {
            //黒で塗りつぶして消去
            g_pDEV->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0,0);
            if (SUCCEEDED(g_pDEV->BeginScene()))
            {   SetupMatrices();
                //頂点フォーマットの設定
                g_pDEV->SetFVF(D3DFVF_VERTEX);
                //直接データを渡して描画
                g_pDEV->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,4,vtx,sizeof(D3DVERTEX));
                g_pDEV->EndScene();
            }
            g_pDEV->Present(NULL,NULL,NULL,NULL);
        }
        
  6. 描画環境を設定する関数です。
    D3DXMatrixRotationY() と SetTransform() でY軸を中心にワールド座標を回転します。
    timeGetTime()/1000.0f で回転速度が決まります。
    レンダリング関数はメッセージループから直接呼び出されるので、マシンの性能によって呼び出される間隔が異なります。
    高性能のマシンでは、1秒間に百回以上も呼び出されることもあれば、低性能のマシンでは十回を切るかも知れません。
    そこで timeGetTime() を使って、呼び出される間隔が違っても回転速度が変わらないようにします。
    性能の低いマシンでは多少動きが荒くなるかも知れませんが、同じよう回転することができます。
    1000.0f の値を変えて試して下さい。
    D3DXMatrixLookAtLH() で View 座標を設定します。
    D3DXMatrixPerspectiveFovLH() で透視変換の設定をします。
    SetRenderState() でZバッファとライトの設定(使用しない)を行っています。
        void  SetupMatrices(void)
        {   D3DXMATRIX      matView;
            D3DXMATRIX      matProj;
            D3DXMATRIX      matWorld;
    
            //ワールド座標の回転
            D3DXMatrixRotationY(&matWorld,timeGetTime()/1000.0f);
            g_pDEV->SetTransform(D3DTS_WORLD,&matWorld);
            //View 座標の設定
            D3DXMatrixLookAtLH(&matView,&D3DXVECTOR3(0.0f, -5.0f, -30.0f),
                                        &D3DXVECTOR3(0.0f, 0.0f, 0.0f),
                                        &D3DXVECTOR3(0.0f, 1.0f, 0.0f));
            g_pDEV->SetTransform(D3DTS_VIEW,&matView);
            //透視変換の設定
            D3DXMatrixPerspectiveFovLH(&matProj,D3DX_PI/4,1.0f,0.0f,100.0f);
            g_pDEV->SetTransform(D3DTS_PROJECTION,&matProj);
            //Zバッファの設定
            g_pDEV->SetRenderState(D3DRS_ZENABLE,D3DZB_FALSE);
            g_pDEV->SetRenderState(D3DRS_LIGHTING,FALSE);
        }
        
  7. D3DXMatrixLookAtLH() 関数の説明です。
    引数 説明
    &matView 作成する行列へのポインタ
    (0.0f,-5.0f,-30.0f) カメラの位置を表す3次元ベクトル
    (0.0f,0.0f,0.0f) カメラの注視点を表す3次元ベクトル
    (0.0f,1.0f,0.0f) 上方向を表す3次元ベクトル
  8. D3DXMatrixLookAtLH() を次のように設定すると真上から見下すことになります。
    引数 説明
    &matView 作成する行列へのポインタ
    (0.0f,30.0f,0.0f) カメラの位置を表す3次元ベクトル
    (0.0f,0.0f,0.0f) カメラの注視点を表す3次元ベクトル
    (0.0f,0.0f,1.0f) 上方向を表す3次元ベクトル
  9. D3DXMatrixPerspectiveFovLH() 関数の説明です。
    引数 説明
    &matProj 作成する行列へのポインタ
    D3DX_PI/4 視野角
    1.0f アスペクト比
    0.0f これより近い(遠い)点は表示されない
    100.0f これより遠い(近い)点は表示されない
  10. オブジェクトを開放する関数です。
    取得したオブジェクトは必ず開放して下さい。
        void Cleanup(void)
        {   SAFE_RELEASE(g_pDEV);
            SAFE_RELEASE(g_pD3D);
        }
        

【演習】

  1. 回転速度を遅く(速く)して描画して下さい。
  2. View 座標は正四面体を正面やや下から見上げています。
    View 座標の方向を変えて「真上・真下・正面・左上・右下」を試してみて下さい。
  3. カメラの注視点は (0.0f, 0.0f, 0.0f) になっています。
    注視点を変えて試してみて下さい。
  4. 上方向を表す3次元ベクトルを変えて試してみて下さい。
    次の値に設定するのが最も馴染みやすいかも知れません。
    (0.0f, -1.0f, 0.0f)
  5. Light は FALSE に設定して下さい。
    ライトを使用していないので、この設定が無いと画面が真っ暗になります。
    コメントにして試してみて下さい。
    g_pDEV->SetRenderState(D3DRS_LIGHTING,FALSE);
  6. このプログラムでは、Zバッファは使わないので FALSE を設定して下さい。
    g_pDEV->SetRenderState(D3DRS_ZENABLE,D3DZB_FALSE);
  7. カリングの設定を NONE にすると「裏向きのポリゴン」が描画されて面白い画面になります。
    これも試してみて下さい。
    g_pDEV->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
  8. ウインドウのアスペクト比を変えると、モデルのアスペクト比も変わります。
    変わらないように設定する方法があるのですが挑戦してみて下さい。
    プログラミングの方法は Object のサイズと描画のサイズ を参照して下さい。

超初心者の方のために全ソースコードを掲載します。 (^_^;)
全ソースコード

超初心者のプログラム入門(DirectX9 game program)

超初心者のプログラム入門(DirectX10 game program)