ホーム < ゲームつくろー! < 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();
   }
}