概要

Direct X 9を用い、3D空間にX軸として赤線(0,0,0)-(500,0,0)、Y軸として緑線(0,0,0)-(0,500,0)、Z軸として青線(0,0,0)-(0,0,500)を描画します。
文字は、DrawText関数を用いて描画します。DrawText関数は、スクリーン座標で位置を指定するため、ワールド座標をスクリーン座標に変換する必要があります。D3DXVec3Project関数をもちいれば簡単に変換できます。キーボードによりカメラ位置、視点、視野角をカーソルキー等で変更できます。D3DXMatrixLookAtLH関数、D3DXMatrixPerspectiveFovLH関数の引数がウィンドウに文字で表示されます。

キー操作は下記のとおりです。

←:Y軸を中心に視点を左回り22.5度回転させる。→:Y軸を中心に視点を右回り22.5度回転させる。
↑:X軸を中心に視点を左回り22.5度回転させる。↓:Y軸を中心に視点を右回り22.5度回転させる。
PageUp:原点からのカメラ距離を100加算する。PageDown:原点からのカメラ距離を100減算する。
Shift + ←:Y軸を中心に注視点を左回り22.5度回転させる。Shift + →:Y軸を中心に注視点を右回り22.5度回転させる。
Shift + ↑:X軸を中心に注視点を左回り22.5度回転させる。Shift + ↓:Y軸を中心に注視点を右回り22.5度回転させる。
w:視野角を11.25度広げる。(ワイド)t:視野角を11.25度狭める。(ズーム)

D3DXMatrixLookAtLH関数、D3DXMatrixPerspectiveFovLH関数の引数を下記の計算式で計算されます。なおcosf,sinfの引数はラジアン単位です。

ex=er*cosf(exr);
ez=er*sinf(exr);
ey=er*sinf(eyr);

vx=vr*cosf(vxr);
vz=vr*sinf(vxr);
vy=vr*sinf(vyr);

 

ソースコードのダウンロード 3dview.zip

ソースコードの説明

WinMain

最初に実行される関数です。
最初にウィンドウを作成し、Init3DDev関数でDirect Xの初期化を行います。
その後通常のWinMain関数と同じ様にメッセージループを構成しますが、メッセージの取り出しにGetMessage関数ではなく、PeekMessage関数を使用し、メッセージがないときもループが回るようにします。メッセージがないときにDraw関数を呼び出し描画を行います。
プログラム終了前にInit3DDevで作成したデバイスを開放します。

WndProc

ウィンドウプロシージャーです。ウィンドウ関係のメッセージを処理します。通常WM_PAINTメッセージにより再描画を行いますが、スピードが要求される場合は、前期のとおり別の場所で処理しています。
キーボードの処理は、WM_KEYDOWNメッセージで処理しています。

Init3Devices

Direct Xの初期化を行います。

InitFont

D3DXCreateFont関数によりフォントオブジェクトを作成します。

Render

画面のクリア Clear

レンタリング開始 BeginScene

D3DXMatrixLookAtLH

ワールド座標をビュー座標に変換する。ワールド座標のどの方向からどの範囲を見るか(描画するか)を設定する。この関数は、左手座標(Xが右、Yが上、Zが奥行方向で奥 左手で手のひらを上側がにし人差し指をX方向に向けた時における親指のさしている方向がZ値が増える方向である。)においてワールド座標をビュー座標に変換する。SetTransform関数により設定。

D3DXMatrixPerspectiveFovLH

描画する遠近それぞれのZ値、画面の縦横比(アスペクト)、Y方向の視野角を指定する。SetTransform関数により設定。

DrawPrimitiveUP

線分を描画します。

D3DXVec3Project( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DVIEWPORT9 *pViewport, CONST D3DXMATRIX *pProjection, CONST D3DXMATRIX *pView, CONST D3DXMATRIX *pWorld );

ワールド座標をスクリーン座標に変換する。
pvに変換したいワールド座標
pViewportにウィンドウサイズを設定
pProjectionD3DXMatrixPerspectiveFovLHで使用したマトリックスを設定
pViewD3DXMatrixLookAtLHで使用したマトリックスを設定
pwordにワールド変換行列を設定します。本プログラムの場合、pvにワールド座標を設定しますので、D3DXMatrixIdentityで初期化された行列を設定します。

SetRect

文字の表示位置を設定します。

DrawText

文字を表示します。内部でGDIを使っていますので、遅いです。デバック用といったところでしょうか。

レンタリング終了 EndScene

バッファの内容を表示 Present

 

コンパイル方法

Visual C++ 2008
Microsoft DirectX SDK June 2010をインストールをあらかじめ行っておく
プロジェクトに下記のパスを追加(64bit Windowsで64bitの実行ファイルを作成する場合)
includeパスにC:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include
LibパスにC:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x64
を追加

ソースコード

//      カメラ座標のテスト Direct X 9

#define     szClassName      TEXT("3DView")

#include <windows.h>
#include <d3dx9.h>
#include <tchar.h>

#pragma once
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
#pragma comment(lib,"winmm.lib")

#define SAFE_RELEASE(p)         { if (p) { (p)->Release();  (p)=NULL; } }

typedef struct _D3DVERTEX{
        D3DXVECTOR3 position; // 頂点座標
    D3DXVECTOR3 normal;   // 法線方向
}   D3DVERTEX;

#define D3DFVF_VERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)


//頂点フォーマット

#define D3DFVF_VERTEXCOL  (D3DFVF_XYZ | D3DFVF_DIFFUSE) //トランスフォームされていない頂点の位置座標+ディフューズ カラー成分

typedef struct _D3DVERTEXCOR_ {
        float   x,y,z;  // トランスフォームされていない頂点の位置座標
    DWORD   color;      // ディフューズ カラー成分
}       D3DVERTEXCOR;

D3DVERTEXCOR    gauge_x[]={     {  0,0,0,0xffff0000 },{200,0,0,0xffff0000 } };

D3DVERTEXCOR    gauge_y[]={     {  0,0,0,0xff00ff00 },
                                                {  0,200,0,0xff00ff00 } };

D3DVERTEXCOR    gauge_z[]={     {  0,0,0,0xff0000ff },
                                                {  0,0,200,0xff0000ff } };

void SetupMatrices(float r,float g,float b);

//      視点位置

float exr=0;
float eyr=0;
float er=-2000;

//      視点位置

float vxr=0;
float vyr=0;
float vr=100;

float vzr=D3DX_PI/4;

int sx=0;       //      スクリーン座標
int sy=0;

D3DPRESENT_PARAMETERS   D3DPPWin;
LPDIRECT3DDEVICE9       pDEV=0;
LPDIRECT3D9             pD3D=0;
D3DMATERIAL9            Material;
D3DLIGHT9               Light;
LPD3DXFONT                              pFont = 0;


BOOL InitApp(HINSTANCE, WNDPROC, TCHAR*);
BOOL InitInstance(HINSTANCE, TCHAR*, int);
HWND hWnd;
HRESULT InitDevices(HWND hWnd); // デバイス/モード等の初期化
HRESULT InitGeometry(void);     // 頂点バッファの初期化
LRESULT WINAPI WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
void Render(void);// 描画処理
void SetupMatrices(float r=0.486f,      float g=0.433f, float b=0.185f);// 描画環境の設定
void geuge_draw(LPDIRECT3DDEVICE9 pDEV);
HRESULT InitFont(HWND hWnd);
void Cleanup(void);     // オブジェクトの開放

INT WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR lptsCmdLine, INT nCmdShow){
        MSG     msg;

    if (!hPrevInst) {
        if (!InitApp(hInst,WndProc, szClassName))    //    ウィンドウクラスの登録
            return FALSE;
    }
    if (!InitInstance(hInst, szClassName, nCmdShow)) {    // ウィンドウの作成
        return FALSE;
    }
        
        if (FAILED(InitDevices(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
                        Render();
    }
    Cleanup();
    return 0;
}

BOOL InitApp(HINSTANCE hInst,WNDPROC WndProc,TCHAR* Name){
    WNDCLASS wc;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = (TCHAR*)Name;
    return (RegisterClass(&wc));
}

BOOL InitInstance(HINSTANCE hInst, TCHAR* Name, int nCmdShow){
    hWnd = CreateWindow(Name,
            Name,
            WS_OVERLAPPEDWINDOW,
            0,
            0,
            639,
            479,
            NULL,
            NULL,
            hInst,
            NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}

// デバイス/モード等の初期化
HRESULT InitDevices(HWND hWnd){
        D3DDISPLAYMODE          dmode;
        HRESULT     hr;

    // デバイス/モード等の初期化
    if (pD3D==NULL)
                pD3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (pD3D==NULL)
                return E_FAIL;
    //現在のディスプレイモードを得る
    if (FAILED(pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&dmode)))
                return E_FAIL;

    // D3DDeviceオブジェクトの作成
    ZeroMemory(&D3DPPWin, sizeof(D3DPPWin));
    D3DPPWin.BackBufferFormat           = dmode.Format;
    D3DPPWin.BackBufferCount            = 1;
    D3DPPWin.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
    D3DPPWin.Windowed                   = TRUE;
    D3DPPWin.EnableAutoDepthStencil     = TRUE;
    D3DPPWin.AutoDepthStencilFormat     = D3DFMT_D16;
    D3DPPWin.Flags                      = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
        
        hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                D3DCREATE_HARDWARE_VERTEXPROCESSING, &D3DPPWin, &pDEV);
    if (FAILED(hr)){
                hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING, &D3DPPWin, &pDEV);
        if (FAILED(hr)){
                        hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &D3DPPWin, &pDEV);
            if (FAILED(hr)){
                                MessageBox(NULL,TEXT("Create Device Error"),TEXT("Surface Error"),MB_OK);
                                return E_FAIL;
                        }
        }
    }
        pDEV->SetRenderState( D3DRS_ZENABLE , TRUE );
        InitFont(hWnd);
    return S_OK;
}

LRESULT WINAPI WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){
    switch(msg){
                case WM_SIZE:
                        sx = LOWORD(lParam);  // クライアント領域の幅 
                        sy = HIWORD(lParam); // クライアント領域の高さ 
                        break;

                case WM_KEYDOWN:
                   if(GetKeyState(VK_LSHIFT) < 0){
                                switch(wParam){
                                        case VK_LEFT:
                                                vxr-=D3DX_PI/8;
                                                break;
                                        case VK_RIGHT:
                                                vxr+=D3DX_PI/8;
                                                break;
                                        case VK_UP:
                                                vyr+=D3DX_PI/8;
                                                break;
                                        case VK_DOWN:
                                                vyr-=D3DX_PI/8;
                                                break;
                                        case VK_PRIOR://Page Up
                                                vr+=100;
                                                break;
                                        case VK_NEXT://Page Down
                                                vr-=100;
                                                break;
                                }
                   }else{
                                switch(wParam){
                                        case VK_ESCAPE:
                                        case VK_F12:
                                                PostMessage(hWnd,WM_CLOSE,0,0);
                                                return 0L;
                                        case VK_F1:     //      リセット
                                                exr=0;
                                                eyr=0;
                                                er=-2000;
                                                vxr=0;
                                                vyr=0;
                                                vr=100;
                                                vzr=D3DX_PI/4;

                                                break;
                                        case VK_LEFT:
                                                exr-=D3DX_PI/8;
                                                break;
                                        case VK_RIGHT:
                                                exr+=D3DX_PI/8;
                                                break;
                                        case VK_UP:
                                                eyr+=D3DX_PI/8;
                                                break;
                                        case VK_DOWN:
                                                eyr-=D3DX_PI/8;
                                                break;
                                        case VK_PRIOR://Page Up
                                                er+=100;
                                                break;
                                        case VK_NEXT://Page Down
                                                er-=100;
                                                break;
                                        case 'W':
                                                vzr+=D3DX_PI/16;
                                                break;
                                        case 'T':
                                                vzr-=D3DX_PI/16;
                                                break;
                                }
                        }
            break;
           case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hWnd,msg,wParam,lParam);
}

// 描画処理
void Render(void){
        D3DXMATRIX matWorld,matw;
    D3DXMATRIX matView;
    D3DXMATRIX matProj;
    D3DXMATRIX matScreen;
    pDEV->Clear(0,NULL,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,0),1.0f,0);
    pDEV->BeginScene();
                         D3DVIEWPORT9 vp;
                         vp.X  = 0;
                         vp.Y  = 0;
                         vp.Width = D3DPPWin.BackBufferWidth;
                         vp.Height =D3DPPWin.BackBufferHeight;
                         vp.MinZ  = 0.0f;
                         vp.MaxZ  = 1.0f;
        pDEV->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);            //反時計回りの面を消去
    pDEV->SetRenderState(D3DRS_LIGHTING,FALSE);              //ライティングしない
    pDEV->SetRenderState(D3DRS_ZENABLE,D3DZB_FALSE);         //Zバッファ使わない

        D3DXMatrixIdentity(&matView);
    D3DXMatrixIdentity(&matProj);

        float ex=er*cosf(exr);
        float ez=er*sinf(exr);
        float ey=er*sinf(eyr);

        float vx=vr*cosf(vxr);
        float vz=vr*sinf(vxr);
        float vy=vr*sinf(vyr);

        D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3( ex, ey,ez ),
                &D3DXVECTOR3(vx,vy,vz ), &D3DXVECTOR3( 0.0f, 1.0f, 0.0f));

    pDEV->SetTransform(D3DTS_VIEW, &matView);
    D3DXMatrixPerspectiveFovLH(&matProj, vzr, float(sx)/float(sy), ez, vz);
        pDEV->SetTransform(D3DTS_PROJECTION, &matProj);
        
        geuge_draw(pDEV);

        D3DXVECTOR3 wvec,vec,wvec2,vec2;
        D3DXMATRIX matW;
    D3DXMatrixIdentity( &matW);

        wvec.x=gauge_x[1].x;
        wvec.y=gauge_x[1].y;
        wvec.z=gauge_x[1].z;
        D3DXVec3Project(&vec,   &wvec, &vp, &matProj, &matView, &matW );

        TCHAR buf[256];
        RECT rect;
        SetRect(&rect ,int(vec.x)  , int(vec.y) , 300 , 260 );//描画位置
        pFont->DrawText(NULL, TEXT("X=200"), -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 255, 0, 0));

        wvec.x=gauge_y[1].x;
        wvec.y=gauge_y[1].y;
        wvec.z=gauge_y[1].z;
        D3DXVec3Project(&vec,   &wvec, &vp, &matProj, &matView, &matW );

        SetRect(&rect ,int(vec.x)  , int(vec.y) , 300 , 260 );//描画位置
        pFont->DrawText(NULL, TEXT("Y=200"), -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 0,255, 0));

        wvec.x=gauge_z[1].x;
        wvec.y=gauge_z[1].y;
        wvec.z=gauge_z[1].z;
        D3DXVec3Project(&vec,   &wvec, &vp, &matProj, &matView, &matW );

        SetRect(&rect,0,0,300,260);
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("D3DXMatrixLookAtLH(,&D3DXVECTOR3((%6.0f,%6.0f,%6.0f),\n  &D3DXVECTOR3((%6.0f,%6.0f,%6.0f),&D3DXVECTOR3(0.0f,1.0f,0.0f)"),ex,ey,ez,vx,vy,vz);
         pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 255, 0, 0));

        SetRect(&rect,0,40,300,260);
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("D3DXMatrixPerspectiveFovLH(,%3.4f,%2.4f,%6.0f,%6.0f)"),vzr, float(sx)/float(sy), ez, vz);
        pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 255, 0, 0));

        SetRect(&rect ,int(vec.x)  , int(vec.y) , 300 , 260 );//描画位置
        pFont->DrawText(NULL, TEXT("Z=200"), -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 0, 0, 255));

        SetRect(&rect,0,220,300,260);
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("exθ=%6.0f,eyθ=%6.0f,er=%6.0f"),exr/D3DX_PI*180.0f,eyr/D3DX_PI*180.0f,er);
        pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 255, 0, 0));
        SetRect(&rect,0,240,300,260);
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("vxrθ=%f,vyrθ=%f,vr=%6.0f"),vxr/D3DX_PI*180.0f,vyr/D3DX_PI*180.0f,vr);
        pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 0, 255, 0));

        SetRect(&rect,0,260,300,260);//描画位置
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("←:exrθ-=22.5度,→:exrθ+=22.5度,↑:eyrθ+=22.5度,↓:eyrθ-=22.5度"));
        pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 0, 255, 0));

        SetRect(&rect,0,280,300,260);//描画位置
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("PageUp:er+=100,PageNext:er-=100"));
        pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 0, 255, 0));

        SetRect(&rect,0,300,300,260);//描画位置
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("Shift+ ←:Vxrθ-=22.5度,→:Vxrθ+=22.5度,\nShift+ ↑:vyrθ+=22.5度,↓:vyrθ-=22.5度"));
        pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 0, 255, 0));

        SetRect(&rect,0,340,300,260);//描画位置
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("Shift+ PageUp:Vr+=100,PageNext:vr-=100"));
        pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 0, 255, 0));

        SetRect(&rect,0,360,300,260);//描画位置
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),TEXT("W:vzrθ+=11.25度 T:vzrθ-=11.25度"));
        pFont->DrawText(NULL, buf, -1, &rect, DT_LEFT | DT_NOCLIP, D3DCOLOR_ARGB(255, 0, 255, 0));

        pDEV->EndScene();
        pDEV->Present(NULL,NULL,NULL,NULL);
}

// オブジェクトの開放
void Cleanup(void){
    SAFE_RELEASE(pFont);
    SAFE_RELEASE(pDEV);
    SAFE_RELEASE(pD3D);
}

void geuge_draw(LPDIRECT3DDEVICE9 pDEV){
        pDEV->SetFVF(D3DFVF_VERTEXCOL);
        pDEV->DrawPrimitiveUP(D3DPT_LINELIST,1,gauge_x,sizeof(D3DVERTEXCOR));
        pDEV->DrawPrimitiveUP(D3DPT_LINELIST,1,gauge_y,sizeof(D3DVERTEXCOR));
        pDEV->DrawPrimitiveUP(D3DPT_LINELIST,1,gauge_z,sizeof(D3DVERTEXCOR));
}

//フォントの初期化
HRESULT InitFont(HWND hWnd){
        HRESULT hr = D3DXCreateFont( pDEV, 16, 0, FW_HEAVY, 1, false, DEFAULT_CHARSET, OUT_TT_ONLY_PRECIS,
                ANTIALIASED_QUALITY, FF_DONTCARE, TEXT("MS P明朝"), &pFont );
        if FAILED(hr)
                return(E_FAIL);
        return S_OK;
}