Microsoft Visual Studio .NET 2003
 Microsoft DirectX 9.0 SDK (December 2004)

■ボリュームテクスチャー Prev  Top  Next
関連ページ:ボリュームテクスチャーでDDSファイル作成

ずっとシェーダーネタやってたので今回は違うことをやってみます。ボリュームテクスチャーというのをやります。 ボリュームテクスチャーというくらいだから、厚み情報をテクスチャーに持っていて2Dスプライトをレンダリングすると3Dオブジェクトとしてレンダリングされるのかと思ってたんですが、違うみたいです。 2Dスプライトと複数のテクスチャーを使用してアニメーションをするときにキーフレーム以外の時間を、2枚のテクスチャーを線形合成して滑らかにアニメーションさせるために使用します。 炎や煙などののパーティクルのアニメーションに効力を発揮すると思います。


'A'から'H'のアルファベットを描いたテクスチャーを使用してアニメーションしています。上の画像は'C'から'D'に切り替わる途中のアニメーションです。'C'と'D'が線形合成されているのがわかると思います。

では実装方法を説明します。まず最初にボリュームテクスチャーを作成する必要がありますのでボリュームテクスチャーでDDSファイル作成を参照してください。

次にソースを解説します。つってもDirectXのサンプルソースをそのまんまぱくっただけなんですけどね(笑)。

---Main.cpp---


LPDIRECT3DDEVICE9 m_pd3dDevice = NULL;
D3DPRESENT_PARAMETERS m_d3dParameters;

D3DCAPS9 Caps;

//シーンのメッシュ
//DirectX SDK(December 2004) に添付されているDXUTMesh.cppファイルにあるヘルパークラス群
//CDXUTMesh* m_pMeshBack = NULL;

//パーティクルオブジェクト
struct VOLUMEVERTEX
{
    FLOAT      x, y, z;
    DWORD      color;
    FLOAT      tu, tv, tw;

    static const DWORD FVF;
};
//FVFの設定。テクセルにtu,tv,twの3つを使用するため、D3DFVF_TEXCOORDSIZE3(0)を指定する
const DWORD VOLUMEVERTEX::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0);

//2D矩形オブジェクトの座標
VOLUMEVERTEX g_vVertices[4] =
{
    { -1.0f, 1.0f, 0.0f, 0xffffffff, 0.0f, 0.0f, 0.0f },
    {  1.0f, 1.0f, 0.0f, 0xffffffff, 1.0f, 0.0f, 0.0f },
    { -1.0f,-1.0f, 0.0f, 0xffffffff, 0.0f, 1.0f, 0.0f },
    {  1.0f,-1.0f, 0.0f, 0xffffffff, 1.0f, 1.0f, 0.0f }
};

LPDIRECT3DVERTEXBUFFER9  m_pVB = NULL;

//ボリュームテクスチャー
LPDIRECT3DVOLUMETEXTURE9 m_pVolumeTexture = NULL;

//スクリーンの解像度
UINT nWidth = 1024;
UINT nHeight = 768;

//太陽の位置ベクトル
D3DXVECTOR4 LightPos = D3DXVECTOR4( 52.0f, 30.0f, -30.0f, 0.0f );

//平行光源の光の方向ベクトル
D3DXVECTOR4 LightDir;

//視点の位置ベクトル
D3DXVECTOR4 EyePos   = D3DXVECTOR4( 0.0f, 0.0f, 0.0f, 1.0f );

bool RenderOK = false;

int APIENTRY WinMain( HINSTANCE hInstance,
                      HINSTANCE /*hPrevInstance*/,
                      LPSTR     /*lpCmpLine*/,
                      INT       /*nCmdShow*/)
{  
   char* AppName = "Tutrial";

   MSG msg;
   ZeroMemory(&msg, sizeof(MSG));
   HWND hWnd = NULL;

   WNDCLASSEX wc;
   wc.cbSize      = sizeof(WNDCLASSEX);
   wc.style      = CS_VREDRAW | CS_HREDRAW;
   wc.lpfnWndProc   = (WNDPROC)WndProc;
   wc.cbClsExtra   = 0;
   wc.cbWndExtra   = sizeof(DWORD);
   wc.hCursor      = LoadCursor(NULL, IDC_ARROW);
   wc.hIcon      = NULL;
   wc.hIconSm      = NULL;
   wc.lpszMenuName    = NULL;
   wc.lpszClassName = AppName;
   wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
   wc.hInstance    = hInstance;
   ::RegisterClassEx(&wc);


   //****************************************************************
   //ここでウィンドウの作成処理
   //****************************************************************


   //****************************************************************
   //ここでDirect3Dの初期化を行う。
   //****************************************************************

   m_pd3dDevice->GetDeviceCaps(&Caps);

   //メッシュのロード
   //背景のロード
   //m_pMeshBack = new CDXUTMesh();
   //m_pMeshBack->Create( m_pd3dDevice, _T("res\\back.x") );
   //m_pMeshBack->SetFVF( m_pd3dDevice, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 );

   //頂点バッファを作成
   m_pd3dDevice->CreateVertexBuffer( 4*sizeof(VOLUMEVERTEX),
                                     D3DUSAGE_WRITEONLY,
                                     VOLUMEVERTEX::FVF,
                                     D3DPOOL_MANAGED, &m_pVB, NULL );
   //ロックして頂点の座標を設定する
   VOLUMEVERTEX* pVertices;
   m_pVB->Lock( 0, 0, (void**)&pVertices, 0 );
   memcpy( pVertices, g_vVertices, sizeof(VOLUMEVERTEX)*4 );
   m_pVB->Unlock();

   //ボリュームテクスチャーをロードする
   D3DXCreateVolumeTextureFromFile( m_pd3dDevice,
                                    _T("res\\VolumeTexture.dds"),  //ボリュームテクスチャーのファイル名
                                    &m_pVolumeTexture );
                                
   //平行光源の位置ベクトルから方向ベクトルを計算する
   LightDir = D3DXVECTOR4( -LightPos.x, -LightPos.y, -LightPos.z, 0.0f );
   D3DXVec3Normalize( (D3DXVECTOR3*)&LightDir, (D3DXVECTOR3*)&LightDir );

   RenderOK = true;
   
   //デバイス消失後にリストアする必要があるオブジェクトの初期化
   Restore();

   ::ShowWindow(hWnd, SW_SHOW);
   ::UpdateWindow(hWnd);

   do
   { 
      if( ::PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
      {
         ::TranslateMessage(&msg); 
         ::DispatchMessage(&msg); 
      }
      else
      {
         if( MainLoop(hWnd) == FALSE )
            ::DestroyWindow( hWnd );
      }
   }while( msg.message != WM_QUIT );

   ::UnregisterClass( AppName, hInstance );

   return msg.wParam;
}

//デバイスのリセット前に開放すべきオブジェクト
void Invalidate()
{
}

//デバイスのリセット後に初期化すべきオブジェクト
void Restore()
{
   //固定機能パイプラインライティングを設定する
   D3DLIGHT9 Light;
   ZeroMemory(&Light, sizeof(D3DLIGHT9));
   Light.Type = D3DLIGHT_DIRECTIONAL;
   Light.Direction = D3DXVECTOR3( LightDir.x, LightDir.y, LightDir.z );
   Light.Ambient   = D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f );
   Light.Diffuse   = D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f );
   Light.Specular  = D3DXCOLOR( 0.0f, 0.0f, 0.0f, 0.0f );
   m_pd3dDevice->SetLight(0, &Light);
   m_pd3dDevice->LightEnable(0, TRUE);

   D3DMATERIAL9 Material;
   ZeroMemory( &Material, sizeof( Material ) );
   Material.Diffuse.r = 1.0f;
   Material.Diffuse.g = 1.0f;
   Material.Diffuse.b = 1.0f;
   Material.Diffuse.a = 1.0f;
   m_pd3dDevice->SetMaterial( &Material );
}

//メッセージループからコールされる関数
BOOL MainLoop( HWND HWnd )
{
   HRESULT hr;
   
   //レンダリング不可能
   if( RenderOK == false )
   {
      hr = m_pd3dDevice->TestCooperativeLevel();
      switch( hr )
      {
      //デバイスは消失しているがReset可能
      case D3DERR_DEVICENOTRESET:

         //開放
         Invalidate();

         //デバイスをリセットする
         hr = m_pd3dDevice->Reset( &m_d3dParameters );
         
         switch( hr )
         {
         //デバイスロスト
         case D3DERR_DEVICELOST:
            break;

         //内部ドライバーエラー
         case D3DERR_DRIVERINTERNALERROR:
            return FALSE;
            break;

         //メソッドの呼び出しが無効です
         case D3DERR_INVALIDCALL:
            return FALSE;
            break;

         case S_OK:

            //初期化
            Restore();

            RenderOK = true;
         }
         break;
      }
   }

   //レンダリング可能
   else
   {
      D3DXMATRIX matProj, matView, matWorld;

      m_pd3dDevice->BeginScene();

      //射影座標変換
      D3DXMatrixPerspectiveFovLH( &matProj,
                                  D3DX_PI/4.0f,
                                  4.0f / 3.0f,
                                  1.0f,
                                  1000.0f
                                );
      m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

      //ビュー座標変換
      D3DXMatrixIdentity( &matView );
      m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

      //初期化
      m_pd3dDevice->Clear( 0L,
                           NULL,
                           D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                           0x0, 
                           1.0f,
                           0L );

      m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
      m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
      m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );

      //ライティング無効
      m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
      
      //両面レンダリング
      m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
      
      //αブレンドを有効
      m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
      
      //加算合成
      m_pd3dDevice->SetRenderState( D3DRS_BLENDOP,   D3DBLENDOP_ADD );      
      m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
      m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

      m_pd3dDevice->SetTexture( 0, m_pVolumeTexture );

      //テクスチャーのレンダリングするフレームの更新
      static float fFrame = 0.0f;
      fFrame += 0.001f;
      if( fFrame > 1.0f )
         fFrame -= 1.0f;

      //twの値を変更することでフレームを更新する
      VOLUMEVERTEX* pVertices = NULL;
      m_pVB->Lock( 0, 0, (void**)&pVertices, 0 );
      for( int i=0; i<4; i++ )
         pVertices[i].tw = fFrame;
      m_pVB->Unlock();

      D3DXMATRIX matScaling, matTranslation;
      D3DXMatrixScaling( &matScaling, 10.0f, 10.0f, 1.0f );
      D3DXMatrixTranslation( &matTranslation, 0.0f, 10.0f, 100.0f );
      matWorld = matScaling * matTranslation;
      m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

      m_pd3dDevice->SetFVF( VOLUMEVERTEX::FVF );
      m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(VOLUMEVERTEX) );
      
      //2D矩形をレンダリング
      m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2);

      //レンダーステートを戻す
      m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
      m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
      m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

      
      m_pd3dDevice->EndScene();
         
      hr = m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
      
      //デバイスロストのチェック
      switch( hr )
      {
      //デバイスロスト
      case D3DERR_DEVICELOST:
         RenderOK = false;
         break;

      //内部ドライバーエラー
      case D3DERR_DRIVERINTERNALERROR:
         return FALSE;
         break;

      //メソッドの呼び出しが無効です
      case D3DERR_INVALIDCALL:
         return FALSE;
         break;
      }
   }
      
   return TRUE;
}

以上です。簡単でした。

Prev  Top  Next
inserted by FC2 system