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; }
以上です。簡単でした。