ホーム < ゲームつくろー! < DirectX技術編 < Z値をテクスチャに書き込む鉄板な方法:サンプルプログラム
その43 Z値をテクスチャに書き込む鉄板な方法:サンプルプログラム
DirectX技術編その43「Z値をテクスチャに書き込む鉄板な方法」で説明した内容を踏まえたサンプルプログラムです。実行すると、整列したキューブが回転して表示され、通常描画の上にZ値テクスチャがワイプしてきます。
サンプルスクリーンショット。Z値テクスチャは画面にぴったり合うように描画しています。
このサンプルではZ値を書き込むテクスチャとZ値用深度バッファを用意し、レンダリング時にそれらを切り替える事でZ値の描画をテクスチャに動的に行っています。Z値の計算には非常に簡単なシェーダプログラムが使用されています。
以下のプログラムは、ZValuePlot.fx(Z値書き込みエフェクトファイル)、comptr.h(COMポインタクラス)及びCube2.x(キューブのXファイル)をパスの通ったフォルダに置き、空のプロジェクトにコピペすることで完全に動きます。必要なファイルはこちらからダウンロードできます(DXGSmp_No43.lzh)。上からデバッグしていくと、何をしているか良くわかるように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> using namespace IKD; _TCHAR gName[100] = _T("Z値テクスチャ作成サンプルプログラム"); // サーフェイスの幅高取得関数 bool GetSurfaceWH( IDirect3DSurface9 *pSurf, UINT &uiWidth, UINT &uiHeight ) { if( !pSurf ) return false; D3DSURFACE_DESC SufDesc; pSurf->GetDesc( &SufDesc ); uiWidth = SufDesc.Width; uiHeight = SufDesc.Height; return true; } // ウィンドウプロシージャ 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; DWORD WndStyle = WS_OVERLAPPEDWINDOW & ~(WS_MAXIMIZEBOX | WS_SIZEBOX); RECT WndRect={0, 0, 800, 600}; 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値テクスチャとZ値用深度バッファサーフェイスの作成 ///// // デバイスの深度バッファの幅と高さを取得 UINT uiDevZBufWidth; // デバイスバッファ幅 UINT uiDevZBufHeight; // デバイスバッファ高 IDirect3DSurface9 *pTmpSf; cpDev->GetDepthStencilSurface(&pTmpSf); GetSurfaceWH( pTmpSf, uiDevZBufWidth, uiDevZBufHeight ); pTmpSf->Release(); // Z値テクスチャを作成 IDirect3DTexture9 *pZBufTex; if(FAILED( D3DXCreateTexture( cpDev.GetPtr(), uiDevZBufWidth, uiDevZBufHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pZBufTex ))) return 0; Com_ptr<IDirect3DTexture9> cpZBufTex(pZBufTex); // Z値テクスチャの幅高を保持 UINT uiZTexWidth; UINT uiZTexHeight; // Z値テクスチャサーフェイスを保持 IDirect3DSurface9 *pZTexSurf; cpZBufTex->GetSurfaceLevel( 0, &pZTexSurf ); GetSurfaceWH( pZTexSurf, uiZTexWidth, uiZTexHeight ); // 深度バッファサーフェイスの作成 Com_ptr<IDirect3DSurface9> cpZTexZBuf; cpDev->CreateDepthStencilSurface( uiZTexWidth, uiZTexHeight, D3DFMT_D16, D3DMULTISAMPLE_NONE, 0, FALSE, cpZTexZBuf.ToCreator(), NULL); // Z値をレンダリングするエフェクトの読み込み Com_ptr<ID3DXEffect> cpEffect; if(FAILED( D3DXCreateEffectFromFile( cpDev.GetPtr(), _T("ZValuePlot.fx"), NULL, NULL, 0, NULL, cpEffect.ToCreator(), NULL) )) return 0; // Xファイルオブジェクト読み込み Com_ptr<ID3DXBuffer> cpMatBuf; Com_ptr<ID3DXMesh> cpMesh; DWORD dwMatNum; D3DXMATERIAL *pMatAry; D3DXLoadMeshFromX( _T("Cube2.x"), D3DXMESH_MANAGED, cpDev.GetPtr(), NULL, cpMatBuf.ToCreator(), NULL, &dwMatNum, cpMesh.ToCreator() ); pMatAry = (D3DXMATERIAL*)cpMatBuf->GetBufferPointer(); // Z値テクスチャ描画用のスプライト生成 Com_ptr<ID3DXSprite> cpSprite; D3DXCreateSprite( cpDev.GetPtr(), cpSprite.ToCreator() ); float W_Scale = (float)uiDevZBufWidth / uiZTexWidth; // 貼り付けるZ値テクスチャを画面サイズにする幅高スケール値 float H_Scale = (float)uiDevZBufHeight / uiZTexHeight; // 描画デバイスサーフェイス群を取得保持 Com_ptr<IDirect3DSurface9> pDeviceSurf; Com_ptr<IDirect3DSurface9> pDeviceZBufSurf; g_pD3DDev->GetRenderTarget(0, pDeviceSurf.ToCreator()); g_pD3DDev->GetDepthStencilSurface(pDeviceZBufSurf.ToCreator()); // ライト D3DLIGHT9 light; ZeroMemory(&light, sizeof(D3DLIGHT9) ); light.Direction = D3DXVECTOR3(1, -1, 1); light.Type = D3DLIGHT_DIRECTIONAL; light.Diffuse.a = 1.0f; light.Diffuse.r = 0.7f; light.Diffuse.g = 0.7f; light.Diffuse.b = 0.7f; g_pD3DDev->SetLight( 0, &light ); g_pD3DDev->LightEnable(0, TRUE); // ライトを照らす D3DXMATRIX View, Proj; // ビュー変換・射影変換 D3DXMatrixPerspectiveFovLH( &Proj, D3DXToRadian(45), 640.0f/480.0f, 50.0f, 1000.0f); // ウィンドウ表示 ShowWindow( hWnd, SW_SHOW ); 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 Num = 30; UINT numPass; // カメラの視点を変化 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値テクスチャに描画 cpEffect->SetTechnique( "ZValuePlotTec" ); g_pD3DDev->SetRenderTarget(0, pZTexSurf); g_pD3DDev->SetDepthStencilSurface( cpZTexZBuf.GetPtr() ); g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(255,255,255,255), 1.0f, 0 ); for(z=0; z<Num; z++) { for(x=0; x<Num; x++) { cpEffect->Begin( &numPass, 0 ); cpEffect->BeginPass(0); 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; mat = mat * View * Proj; cpEffect->SetMatrix( "matWorldViewProj", &mat ); // ワールドビュー射影変換行列設定 for(i=0; i<dwMatNum; i++) cpMesh->DrawSubset(i); // メッシュ描画 cpEffect->EndPass(); } } cpEffect->End(); ////////////////////////////////////////// // ■パス2 : 通常描画+スプライト描画 g_pD3DDev->SetVertexShader(NULL); // 固定機能パイプラインに処理を戻す g_pD3DDev->SetPixelShader(NULL); g_pD3DDev->SetRenderTarget(0, pDeviceSurf.GetPtr()); g_pD3DDev->SetDepthStencilSurface( pDeviceZBufSurf.GetPtr() ); g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(10,150,190), 1.0f, 0 ); for(z=0; z<Num; z++) { for(x=0; x<Num; 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; mat = mat * View * Proj; g_pD3DDev->SetTransform( D3DTS_WORLD, &mat ); for(i=0; i<dwMatNum; i++) { g_pD3DDev->SetMaterial( &pMatAry[i].MatD3D ); cpMesh->DrawSubset(i); } } } // Z値テクスチャ適用のスプライト描画 D3DXMATRIX SpMat; D3DXMatrixScaling( &SpMat, W_Scale, H_Scale, 1.0f ); cpSprite->SetTransform( &SpMat ); RECT SpRect; SetRect( &SpRect, 0, 0, (int)((1+sin(f/20.0f))*uiZTexWidth/2.0f), uiZTexHeight ); cpSprite->Begin(0); cpSprite->Draw(cpZBufTex.GetPtr(), &SpRect, NULL, NULL, 0xffffffff ); cpSprite->End(); g_pD3DDev->EndScene(); g_pD3DDev->Present( NULL, NULL, NULL, NULL ); } }while(msg.message != WM_QUIT); pZTexSurf->Release(); return 0; }
○ Z値レンダリングシェーダプログラム
Z値をレンダリングするシェーダプログラムです。ピクセルシェーダ2.0を使用しておりますので、対応していないビデオカードでは動作しませんので注意して下さい。
float4x4 matWorldViewProj; // ワールドビュー射影変換行列 struct VS_OUTPUT { float4 Pos : POSITION; // 射影変換座標 float4 ShadowMapTex : TEXCOORD0; // Zバッファテクスチャ }; // 頂点シェーダ VS_OUTPUT ZBufferCalc_VS( float4 Pos : POSITION ) { VS_OUTPUT Out = (VS_OUTPUT)0; // 普通にワールドビュー射影行列をする Out.Pos = mul( Pos, matWorldViewProj ); // テクスチャ座標を頂点に合わせる Out.ShadowMapTex = Out.Pos; return Out; } // ピクセルシェーダ float4 ZBufferPlot_PS( float4 ShadowMapTex : TEXCOORD0 ) : COLOR { // Z値算出 return ShadowMapTex.z / ShadowMapTex.w; } technique ZValuePlotTec { pass P0 { VertexShader = compile vs_2_0 ZBufferCalc_VS(); PixelShader = compile ps_2_0 ZBufferPlot_PS(); } }