DirectX で地球を回してみるテスト
○○ で地球を回してみるシリーズ。
昨日は OpenGL で地球を回してみましたが、今回は、DirectX で同様のことを実現したいと思います。
0. 事前準備
以下のコンパイラとライブラリを用意します。
※ Windows 8.1 の SDK に含まれる DirectX のライブラリはストアアプリ用の為、デスクトップ用に DirectX の開発をするには「DirectX SDK(June 2010)」をインストールする必要があるようです。
1. Visual C++ 2013 を起動する
2. 新しいプロジェクトを作成する
新しいプロジェクト名は「earth」としておきます。
3. プロジェクトのプロパティでインクルードファイルとライブラリのパスを追加する
4. ソースを修正する
ウィザードで生成された上記の「earth.cpp」を以下のコードに置き換えます。
#include "stdafx.h" #include "earth.h" #define R 50.0f // 球半径 #define VIEW_R 150.0f // カメラ位置 #include <windows.h> #include <d3dx9.h> #include <tchar.h> #include <mmsystem.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; } } #define FVF_TLVERTEX (D3DFVF_XYZ | D3DFVF_TEX1) LPDIRECT3D9 g_pD3D = NULL; LPDIRECT3DDEVICE9 g_pDev = NULL; LPD3DXMESH g_pMeshApp = NULL; LPD3DXMESH g_pMeshApp2 = NULL; LPDIRECT3DTEXTURE9 g_pTextures = NULL; DWORD start; // プログラム開始時のシステム時刻 HRESULT InitD3D(HWND hWnd); HRESULT CreateSphere(); void SetupMatrices(); void Render(); void Cleanup(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // 頂点フォーマットの定義 struct TLVERTEX{ float x, y, z; float tu, tv; }; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int) { LPCTSTR lpszClassName = _T("helloWindow"); LPCTSTR lpszWindowName = _T("Hello, DirectX World!"); WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_CLASSDC; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0L; wc.cbWndExtra = 0L; wc.hInstance = hInst; wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_APPLICATION)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = lpszClassName; wc.hIconSm = LoadIcon(hInst, MAKEINTRESOURCE(IDI_APPLICATION)); RegisterClassEx(&wc); HWND hWnd = CreateWindow( lpszClassName, lpszWindowName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 450, 450, NULL, NULL, hInst, NULL); InitD3D(hWnd); CreateSphere(); start = timeGetTime(); ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } void convert(float x, float y, float z, float r, float& u, float& v) { float q; float q2; q = atan2(z, x); u = q / (2.0f * 3.1415f); q2 = asin(y / r); v = (1.0f - q2 / (3.1415f / 2.0f)) / 2.0f; if (u>1.0) u = 1.0; } // デバイス/モード等の初期化 HRESULT InitD3D(HWND hWnd) { HRESULT hr; g_pD3D = Direct3DCreate9(D3D_SDK_VERSION); if (g_pD3D == NULL) { return E_FAIL; } // D3DDeviceオブジェクトの作成 D3DPRESENT_PARAMETERS d3dpp; d3dpp.BackBufferWidth = 0; d3dpp.BackBufferHeight = 0; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; d3dpp.BackBufferCount = 0; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.MultiSampleQuality = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = NULL; d3dpp.Windowed = TRUE; d3dpp.EnableAutoDepthStencil = 0; d3dpp.AutoDepthStencilFormat = D3DFMT_UNKNOWN; d3dpp.Flags = 0; d3dpp.FullScreen_RefreshRateInHz = 0; d3dpp.PresentationInterval = 0; hr = g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pDev); if (FAILED(hr)) { return E_FAIL; } return S_OK; } HRESULT CreateSphere() { HRESULT hr; // 球メッシュの生成 D3DXCreateSphere(g_pDev, R, 32, 32, &g_pMeshApp, NULL); g_pMeshApp->CloneMeshFVF(NULL, FVF_TLVERTEX, g_pDev, &g_pMeshApp2); LPDIRECT3DVERTEXBUFFER9 pVB = NULL; TLVERTEX* pVer = NULL; g_pMeshApp2->GetVertexBuffer(&pVB); pVB->Lock(0, 0, (VOID**)&pVer, 0); DWORD n; for (n = 0; n < g_pMeshApp2->GetNumVertices(); n++){ float u, v; convert(pVer[n].x, pVer[n].y, pVer[n].z, R, u, v); pVer[n].tu = u; pVer[n].tv = v; } pVB->Unlock(); hr = D3DXCreateTextureFromFile(g_pDev, _T("Texture.jpg"), &g_pTextures); if (FAILED(hr)){ MessageBox(NULL, _T("Texture Load Error"), _T("Error"), MB_OK); return E_FAIL; } return S_OK; } // 描画環境の設定 void SetupMatrices() { D3DXMATRIX matView; D3DXMATRIX matProj; D3DXVECTOR3 ViewForm; float rt = (float(timeGetTime() - start) / 10.0f); // システムタイムより回転角を算出する ViewForm.x = (float)(VIEW_R*cos(rt / 180 * 3.14)); ViewForm.y = 0.0f; ViewForm.z = (float)(VIEW_R*sin(rt / 180 * 3.14)); D3DXMatrixLookAtLH(&matView, &ViewForm, &D3DXVECTOR3(0.0f, 0.0f, 0.0f), &D3DXVECTOR3(0.0f, 1.0f, 0.0f)); g_pDev->SetTransform(D3DTS_VIEW, &matView); D3DXMatrixPerspectiveFovLH(&matProj, D3DXToRadian(45.0f), 1.0f, 100, 1000); g_pDev->SetTransform(D3DTS_PROJECTION, &matProj); g_pDev->SetRenderState(D3DRS_WRAP0, D3DWRAPCOORD_0); g_pDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); // 反時計回りの面を消去 g_pDev->SetRenderState(D3DRS_LIGHTING, FALSE); // ライティングしない g_pDev->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); // Zバッファ使わない } // 描画処理 void Render() { g_pDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); if (SUCCEEDED(g_pDev->BeginScene())){ g_pDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); SetupMatrices(); g_pDev->SetFVF(FVF_TLVERTEX); g_pDev->SetTexture(0, g_pTextures); g_pMeshApp2->DrawSubset(0); g_pDev->EndScene(); } g_pDev->Present(NULL, NULL, NULL, NULL); } // オブジェクトの開放 void Cleanup() { SAFE_RELEASE(g_pTextures); SAFE_RELEASE(g_pMeshApp); SAFE_RELEASE(g_pMeshApp2); SAFE_RELEASE(g_pDev); } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg){ case WM_CLOSE: case WM_DESTROY: Cleanup(); PostQuitMessage(0); return 0L; case WM_PAINT: Render(); return 0; } return DefWindowProc(hWnd, msg, wParam, lParam); }
上記、コードは、
■ 球にテクスチャーを張り視点を回転させる(DirectX9 32/64bit)
http://yamatyuu.net/computer/program/directx9/Sphere_Texture/index.html
のものを、一部、修正したものになります。
5. コンパイルする
[F5] キーでコンパイルします。
「正常終了」が出れば、とりあえず、コンパイル完了です。
6. 出力先フォルダにテクスチャファイルをコピーする
テクスチャに使用する画像ファイルは、「Texture.jpg」としてください。
7. 実行する
地球が回れば、成功です。
参考
■ 球にテクスチャーを張り視点を回転させる(DirectX9 32/64bit)
http://yamatyuu.net/computer/program/directx9/Sphere_Texture/index.html
■ CX's Hello, World! » Hello, DirectX(C++) World!
http://cx20.main.jp/blog/hello/2012/07/23/hello-directx-cpp-world/
■ 2013年にDirectXでゲームを作りたいときに知っておくべきこと - arveltの技術メモ