山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他d3dxcreateboxを使用して立体を描画する(DirectX 9 32/64bit)
概要
Direct X 9を用い、今までは、立方体を描画するために、プログラマが立方体を三角形に分解して描画していましたが、Direct X 9には直方体を描画するメッシュがあります。このメッシュは1つの面を二個の三角形で表し、全部で頂点が36個、12個の三角形に分解しております。このメッシュが優れているのは、各面ごとに法線が設定されております。このプログラムでは、立方体を原点に固定し、視点を回転させています。なおライトを使用しております。
ソースコードのダウンロード d3dxcreatebox.zip
ソースコードの説明
_tWinMain
最初に実行される関数です。関数名に_tがついているのはUNICODEおよびマルチバイトコードの両方でコンパイルできるようにするためです。
最初にウィンドウを作成し、InitDevices関数でDirect Xの初期化を行います。
その後通常の_tWinMain関数と同じ様にメッセージループを構成しますが、メッセージの取り出しにGetMessage関数ではなく、PeekMessage関数を使用し、メッセージがないときもループが回るようにします。メッセージがないときにDraw関数を呼び出し描画を行います。
Init3Devices
Direct Xの初期化を行います。初期化は、できるだけハードウェアの能力を生かせるように、
①ハードウェアによる頂点処理・ハードウェアによるラスタ処理
②ソフトウェアによる頂点処理・ハードウェアによるラスタ処理
③ソフトウェアによる頂点処理・ソフトウェアによるラスタ処理
の順番でデバイスの作成を試みます。
d3dxcreatebox
立方体のメッシュを作成します。
通常インデックスバッファを用いる場合、頂点座標を各面で共有できますが、法線方向を3面が共有している頂点で正確に表現することは、簡単には共有できません。この関数は1つの面を二個の三角形で表し、全部で頂点が36個、12個の三角形に分解しております。この関数が優れているのは、各面ごとに法線が設定されているので、法線を共有することがありません。したがって、頂点付近の処理がきれいに描画されます。
SetupMatrices
Draw関数から呼び出されます。
D3DXMatrixRotationX,D3DXMatrixRotationZ,D3DXMatrixMultiply,SetTransform
X軸及びZ軸を中心に立体を回転させます。
D3DMatrixLookAtLH
D3DMatrixPerspectiveFovLH
ワールド座標をビュー座標に変換する。ワールド座標のどの方向からどの範囲を見るか(描画するか)を設定する。この関数は、左手座標(Xが右、Yが上、Zが奥行方向で奥 左手で手のひらを上側がにし人差し指をX方向に向けた時における親指のさしている方向がZ値が増える方向である。)においてワールド座標をビュー座標に変換する。
描画する遠近それぞれのZ値、画面の縦横比(アスペクト)、Y方向の視野角を指定する。
その他に、光源と球とトーラスのマテリアルを設定します。
Draw
画面のクリア Clear
レンタリング開始 BeginScene
メッシュの描画 DrawSubset
レンタリング終了 EndScene
バッファの内容を表示 Present
Cleanup
Init3Devicesで作成したデバイスと頂点バッファを開放します。
WndProc
ウィンドウプロシージャーです。ウィンドウ関係のメッセージを処理します。通常WM_PAINTメッセージにより再描画を行いますが、スピードが要求される場合は、前期のとおり別の場所で処理しています。
コンパイル方法
Visual C++ 2008
Microsoft DirectX SDK June 2010をインストールをあらかじめ行っておく
ツールメニュよりオプションを選ぶ。
オプションダイアログボックスのツリービューよりプロジェクトおよびソリューション・VC++ディレクトリを選択
ディレクトリを表示するプロジェクトよりインクルードファイルおよびライブラリファイルを選択してパスを設定する。
インクルードファイル C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include
ライブラリファイル C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x64
ソースコード
// Direct X 9 Sample Program
// 立方体を固定し、ビュー座標(視点)側をY軸中心に回転させる
// ライトつき
// 32bit/64bit マルチバイト/Unicode対応 Visual C++ 2008
#define szClassName TEXT("D3DXCreateBox")
#include <windows.h>
#include <d3dx9.h>
#include <tchar.h>
#include <Stdio.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; } }
HWND hWnd;
LPDIRECT3D9 pD3D;
LPDIRECT3DDEVICE9 pDev;
D3DPRESENT_PARAMETERS D3DPPWin;
D3DLIGHT9 Light;
D3DMATERIAL9 Material;
LPD3DXMESH pMeshApp = NULL;
typedef struct _D3DVERTEX{
D3DXVECTOR3 position; // 頂点座標
D3DXVECTOR3 normal; // 法線方向
} D3DVERTEX;
HRESULT InitDevices(void);
void SetupMatrices(void);
void Draw(void);
void Cleanup(void);
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE, TCHAR*, int){
MSG msg;
WNDCLASSEX wc = {
sizeof(WNDCLASSEX),CS_CLASSDC,WndProc,0L,0L,hInst,
NULL,NULL,NULL,NULL,szClassName,NULL
};
if (RegisterClassEx(&wc)==0)
return FALSE;
hWnd = CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInst,NULL);
if (FAILED(InitDevices()))
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(szClassName,wc.hInstance);
return 0;
}
//デバイス/モード等の初期化
HRESULT InitDevices(void){
pD3D= NULL;
pDev= NULL;
HRESULT hr;
D3DDISPLAYMODE dmode;
// デバイス/モード等の初期化
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;
}
}
}
//直方体メッシュの生成
D3DXCreateBox(pDev,50,50,50,&pMeshApp,NULL);
return S_OK;
}
//描画環境の設定
void SetupMatrices(void){
D3DXMATRIX matView;
D3DXMATRIX matProj;
D3DXVECTOR3 ViewForm; // 目の位置を保存
float rt=(float(timeGetTime())/10.0f); // システムタイムより回転角を算出する
ViewForm.x= (float)(200*cos(rt/180*3.14));
ViewForm.y= 100;
ViewForm.z= (float)(200*sin(rt/180*3.14));
//View 座標の設定 ViewFormが目の位置 &D3DXVECTOR3(0.0f,0.0f,0.0f)が目の向く方向 すなわちy軸から距離200位置から原点を見ることになる
D3DXMatrixLookAtLH(&matView,&ViewForm,
&D3DXVECTOR3(0.0f,0.0f,0.0f),&D3DXVECTOR3(0.0f,1.0f,0.0f));
pDev->SetTransform(D3DTS_VIEW,&matView);
//透視変換の設定 Y方向の視野角45度 Z座標 100~1000の範囲を射影する
D3DXMatrixPerspectiveFovLH(&matProj,D3DXToRadian(45.0f),1.0f,100,1000); // 視野に基づいて、左手座標系パースペクティブ射影行列を作成する
pDev->SetTransform(D3DTS_PROJECTION,&matProj);
pDev->SetRenderState(D3DRS_AMBIENT,0x00808080);// アンビエント反射 環境光:光が当たらない部分へ間接的に当たる光の強さ
float r=0.486f;
float g=0.433f;
float b=0.185f;
float rr,gg,bb;
rr= (float)200/255.0f;
gg= (float)200/255.0f;
bb= (float)200/255.0f;
// //光源の作成
ZeroMemory(&Light,sizeof(D3DLIGHT9));
Light.Type = D3DLIGHT_DIRECTIONAL; //ライトはディレクショナル光源である。これは無限大の距離でポイント ライトを使うことと同じである。
Light.Diffuse.r= rr; // ライトが放射するディフューズ色
Light.Diffuse.g= gg;
Light.Diffuse.b= bb;
Light.Specular.r= rr; // ライトが放射するスペキュラ色
Light.Specular.g= gg;
Light.Specular.b= bb;
Light.Ambient.r= rr/2.0f; // ライトが放射するアンビエント色
Light.Ambient.g= gg/2.0f;
Light.Ambient.b= bb/2.0f;
D3DXVec3Normalize((D3DXVECTOR3*)&Light.Direction,&D3DXVECTOR3(1.0f,-1.5f,0.7f)); //3D ベクトルの正規化したベクトルを返す
pDev->SetLight(0,&Light);
pDev->LightEnable(0,TRUE);
ZeroMemory(&Material,sizeof(D3DMATERIAL9));
Material.Diffuse.r= r;
Material.Diffuse.g= g;
Material.Diffuse.b= b;
Material.Ambient.r= r/2;
Material.Ambient.g= g/2;
Material.Ambient.b= b/2;
Material.Specular.r= r/10;
Material.Specular.g= g/10;
Material.Specular.b= b/10;
Material.Emissive.r= r/10;
Material.Emissive.g= g/10;
Material.Emissive.b= b/10;
Material.Power= 0;
pDev->SetMaterial(&Material);
}
//描画処理
void Draw(void){
//黒で塗りつぶして消去
pDev->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,0),1.0f,0);
if (SUCCEEDED(pDev->BeginScene())){
//描画環境の設定
SetupMatrices();
pMeshApp->DrawSubset(0);
pDev->EndScene();
}
pDev->Present(NULL,NULL,NULL,NULL);
}
//オブジェクトの開放
void Cleanup(void){
SAFE_RELEASE(pMeshApp);
SAFE_RELEASE(pDev);
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){
switch(msg){
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
return 0L;
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}