ホーム < ゲームつくろー! < DirectX技術編 < 深度バッファシャドウの根っこ:Z値テクスチャ作成クラスを作る:サンプルプログラム
その45 深度バッファシャドウの根っこ:Z値テクスチャ作成クラスを作る:サンプルプログラム
DirectX技術編その45「深度バッファシャドウの根っこ:Z値テクスチャ作成クラスを作る」で説明した内容を踏まえたサンプルプログラムです。実行すると、整列したキューブを回りながら撮影したZ値テクスチャが動画として描画されます。
サンプルスクリーンショット。Z値テクスチャは画面にぴったり合うように描画しています。
実際はDirectX技術編その43のサンプルプログラムと変わりませんが、Z値テクスチャの作成をCZTexCreatorクラスに委譲しているため、ソースがかなり簡略化されています。このプログラムのメインはCZTexCreatorクラスの使い方を学ぶ事です。また、エフェクトファイルをリソースとして提供する仕組みが整っているので、リリース時には立方体のXファイルだけで動きます。
○ コンパイル方法
最下に挙げたサンプルプログラムをコンパイルするには、次のファイルをパスの通ったフォルダに置きます。
P ・ ZTexCreator.h Z値テクスチャ作成クラス宣言 P ・ ZTexCreator.cpp Z値テクスチャ作成クラス実装 P ・ ZTexCreator.rc Z値テクスチャ作成クラス用のリソースファイル ・ ZTexCreator.fx Z値プロットシェーダプログラム P ・ EffectResource.h エフェクトリソースヘッダー ・ Cube2.x 立方体Xファイル P ・ comptr.h COMポインタクラス P ・ main.cpp メイン部分
Xファイルとエフェクトファイル以外をプロジェクトに追加すればコンパイルできます(追加するファイルを「P」で示しています)。コンパイルされると、シェーダプログラムが実行ファイルに埋め込まれます。上記必要ファイルはこちらからダウンロードできます。実行形式も入っておりますので、まずは実行してみて下さい。
○ プログラムで遊ぶポイント
Z値テクスチャの作成部分はクラスが行ってくれますので、描画部分だけに集中できます。ただし、描画は1つのサブメッシュ、1つのIDirect3DDevice9::DrawPrimitiveメソッドの呼び出しごとに行う必要があります。Numという変数で立方体の数を変えられますので、色々と値を変更してみると楽しいと思います。
Z値テクスチャの大きさはメイン部のTexSizeという変数で決められています。ここを大きくしたり小さくしたりすると、Z値テクスチャの解像度が変わります。どのテクスチャも画面いっぱいいっぱいに描画されますので、その違いを体感できます。
射影変換行列(Proj)の最近距離と最遠距離を色々と替えてみると、Z値計算の性質が良くわかると思います。遠くにするほどグレースケールの色変化が少なくなります。これはZ値算出精度が劣化していることを意味します。近くにすると精度は上がりますが、今度は遠くのオブジェクトが切れてしまいます。この調節がなかなか難しいところです。
以下はメイン部のプログラムです。メインは上からデバッグしていくと、何をしているか良くわかるように1つのソースで完結させています。今回のポイントになりそうなところは太文字で示されています。
(もしうまく動かない、プログラムへの組み込み方がわからない等ございましたら掲示板にてご連絡下さい)
/// Z値テクスチャ作成サンプルプログラム #pragma comment(lib, "dxguid.lib") #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") #include <windows.h> #include <tchar.h> #include <d3d9.h> #include <d3dx9.h> #include "Comptr.h" #include <crtdbg.h> #include "ZTexCreator.h" using namespace IKD; _TCHAR gName[100] = _T("Z値テクスチャ作成サンプルプログラム"); // ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam){ switch( mes ) { case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, mes, wParam, lParam); } int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); // アプリケーションの初期化 MSG msg; HWND hWnd; WNDCLASSEX wcex ={sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, NULL, NULL, (HBRUSH)(COLOR_WINDOW+1), NULL, (_TCHAR*)gName, NULL}; if(!RegisterClassEx(&wcex)) return 0; int ClientSize = 600; DWORD WndStyle = WS_OVERLAPPEDWINDOW & ~(WS_MAXIMIZEBOX | WS_SIZEBOX); RECT WndRect={0, 0, ClientSize, ClientSize}; AdjustWindowRect( &WndRect, WndStyle, false ); if(!(hWnd = CreateWindow( gName, gName, WndStyle, CW_USEDEFAULT, 0, WndRect.right-WndRect.left, WndRect.bottom-WndRect.top, NULL, NULL, hInstance, NULL))) return 0; // Direct3Dの初期化 LPDIRECT3D9 g_pD3D; LPDIRECT3DDEVICE9 g_pD3DDev; if( !(g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )) ) return 0; Com_ptr<IDirect3D9> spD3D(g_pD3D); D3DPRESENT_PARAMETERS d3dpp = {0,0,D3DFMT_UNKNOWN,0,D3DMULTISAMPLE_NONE,0, D3DSWAPEFFECT_DISCARD,NULL,TRUE,TRUE,D3DFMT_D16,0,D3DPRESENT_RATE_DEFAULT,D3DPRESENT_INTERVAL_DEFAULT}; if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDev ) ) ) if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDev ) ) ) return 0; Com_ptr<IDirect3DDevice9> cpDev( g_pD3DDev ); ///////////////////////////////////////////////////// // Z値テクスチャ生成オブジェクトの生成と初期化 ///////////// UINT TexSize = 1024; // テクスチャサイズ CZTexCreator ZTexCreator; if(!ZTexCreator.Init( cpDev, TexSize, TexSize, D3DFMT_D16 )) // 多分無理ですがD16で作成を試み if(!ZTexCreator.Init( cpDev, TexSize, TexSize, D3DFMT_A8R8G8B8 )) // 通常はこのテクスチャが作成できます if(!ZTexCreator.Init( cpDev, TexSize, TexSize, D3DFMT_R5G6B5 )) // 万が一は16bitに落とします return 0; Com_ptr<IDirect3DTexture9> cpZBufTex; // Z値テクスチャ ZTexCreator.GetZTex( cpZBufTex ); // Xファイルオブジェクト読み込み Com_ptr<ID3DXBuffer> cpMatBuf; Com_ptr<ID3DXMesh> cpMesh; DWORD dwMatNum; if(FAILED(D3DXLoadMeshFromX( _T("Cube2.x"), D3DXMESH_MANAGED, cpDev.GetPtr(), NULL, cpMatBuf.ToCreator(), NULL, &dwMatNum, cpMesh.ToCreator() ))) return 0; // Z値テクスチャ描画用のスプライト生成 Com_ptr<ID3DXSprite> cpSprite; D3DXCreateSprite( cpDev.GetPtr(), cpSprite.ToCreator() ); D3DXMATRIX View, Proj; // ビュー変換・射影変換 // 射影変換行列を設定 D3DXMatrixPerspectiveFovLH( &Proj, D3DXToRadian(45), 640.0f/480.0f, 50.0f, 1000.0f); // ウィンドウ表示 ShowWindow( hWnd, SW_SHOW ); float Ratio = (float)ClientSize / TexSize; // 画面に対する比率を計算 float f=0.0f; DWORD i; do { if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) { DispatchMessage(&msg); } else { f+=0.1f; int x, z; int CubeNum = 30; // カメラの視点を変化 D3DXMatrixLookAtLH( &View, &D3DXVECTOR3(250*sin(f/20),80,250*cos(f/20)), &D3DXVECTOR3(40,0,150), &D3DXVECTOR3(0,1,0) ); g_pD3DDev->BeginScene(); /////////////////////////////////////// //■パス1 : Z値テクスチャに描画 ZTexCreator.Begin(); ZTexCreator.SetProjMatrix( &Proj ); ZTexCreator.SetViewMatrix( &View ); for(z=0; z<CubeNum; z++) { for(x=0; x<CubeNum; x++) { D3DXMATRIX mat, RotY, RotZ; D3DXMatrixIdentity( &mat ); D3DXMatrixRotationY( &RotY, D3DXToRadian( f ) ); D3DXMatrixRotationZ( &RotZ, D3DXToRadian( f*2.353f ) ); mat *= RotY * RotZ; mat._41 = x*15.0f; mat._43 = z*15.0f; ZTexCreator.SetWorldMatrix( &mat ); for(i=0; i<dwMatNum; i++) { ZTexCreator.BeginPass(); ZTexCreator.SetParamToEffect(); // 描画の直前に呼び出す必要があります cpMesh->DrawSubset(i); // メッシュ描画 ZTexCreator.EndPass(); } } } ZTexCreator.End(); ////////////////////////////////// //■パス2 : Z値テクスチャを描画 cpDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(255,255,255,255), 1.0f, 0 ); D3DXMATRIX ScaleMat; D3DXMatrixIdentity( &ScaleMat ); D3DXMatrixScaling( &ScaleMat, Ratio, Ratio, 1.0f ); cpSprite->SetTransform( &ScaleMat ); cpSprite->Begin(0); cpSprite->Draw(cpZBufTex.GetPtr(), NULL, NULL, NULL, 0xffffffff ); cpSprite->End(); g_pD3DDev->EndScene(); g_pD3DDev->Present( NULL, NULL, NULL, NULL ); } }while(msg.message != WM_QUIT); return 0; }