Microsoft Visual Studio .NET 2003 Microsoft DirectX 9.0 SDK (December 2004) シェーダーモデル 2.0 |
■フォーカスアニメーションつき被写界深度 | Prev Top Next |
関連ページ:ブラーフィルターその2 被写界深度 |
|
今回は、被写界深度をやります。被写界深度は前にもやってますが、今回はこれにフォーカス位置によって被写界深度を動的に調整するアニメーション機能を追加します。 さらに前回は手前をはっきり、奥をぼけるようにしましたが、今回は手前もぼけるようにしました。
フォーカス位置はスクリーンの中心位置です。この位置のZ値は手前のトラとなるため、手前のトラがくっきりとし、背景や奥にいるトラはぼけています。
フォーカス位置は上と同じくスクリーンの中心位置です。この位置のZ値は背景となるため、手前にいるトラはぼけて、背景や奥にいるトラはくっきりとします。
これの被写界深度の調整がアニメーションします。動かしてみるとなかなか面白いです。
処理フローは前回の被写界深度とあまり変わらないので解説は省略します。
ということでソースをみていきます。
---DOF.fx---
float m_Z; //フォーカスが当たっているピクセルのZ値 float m_Bias; //Z値の調整値。値を大きくするとボケる領域が広くなる float m_BlurPower; //ボケの強さ float2 m_Texel; //テクセルのオフセット値 sampler tex0 : register(s0); //バックバッファのそのままのテクスチャー sampler tex1 : register(s1); //バックバッファをぼかしたテクスチャ sampler tex2 : register(s2); //バックバッファのZ値 struct VS_OUTPUT { float4 Pos : POSITION; float2 Tex : TEXCOORD0; }; VS_OUTPUT VS( float4 Pos : POSITION, float2 Tex : TEXCOORD0 ) { VS_OUTPUT Out; Out.Pos = Pos; Out.Tex = Tex; return Out; } float4 PS( VS_OUTPUT In ) : COLOR { //現在のピクセルのZ値情報を取得 float ZCenter = tex2D( tex2, In.Tex ).r; //現在のピクセルのZ値とフォーカス地点のピクセルのZ値の距離 //距離が遠くなるほどボケる //m_Biasを距離に積算し、ボケる領域の調整を行う float len = min( 1.0f, distance( m_Z, ZCenter ) * m_Bias ); //周囲のピクセル位置のZ値を取得(注意1) float Z1 = tex2D( tex2, In.Tex + float2( m_Texel.x, m_Texel.y ) ).r; float Z2 = tex2D( tex2, In.Tex + float2( m_Texel.x, -m_Texel.y ) ).r; float Z3 = tex2D( tex2, In.Tex + float2( -m_Texel.x, m_Texel.y ) ).r; float Z4 = tex2D( tex2, In.Tex + float2( -m_Texel.x, -m_Texel.y ) ).r; float len2; float l = 0.0f; //周囲のピクセル位置のZ値 < 現在のピクセル位置のZ値のときはぼかす必要がある if( Z1 < ZCenter ) { //現在のピクセル位置と周囲のピクセル位置のZ値の距離を計算 len2 = distance( Z1, ZCenter ); //周囲のピクセル位置のボケ > 現在のピクセル位置のボケ のときボケの強さを更新する if( len2 > len ) len = len2 * m_BlurPower; } //周囲のピクセル位置のZ値 < 現在のピクセル位置のZ値のときはぼかす必要がある if( Z2 < ZCenter ) { //現在のピクセル位置と周囲のピクセル位置のZ値の距離を計算 len2 = distance( Z2, ZCenter ); //周囲のピクセル位置のボケ > 現在のピクセル位置のボケ のときボケの強さを更新する if( len2 > len ) len = len2 * m_BlurPower; } //周囲のピクセル位置のZ値 < 現在のピクセル位置のZ値のときはぼかす必要がある if( Z3 < ZCenter ) { //現在のピクセル位置と周囲のピクセル位置のZ値の距離を計算 len2 = distance( Z3, ZCenter ); //周囲のピクセル位置のボケ > 現在のピクセル位置のボケ のときボケの強さを更新する if( len2 > len ) len = len2 * m_BlurPower; } //周囲のピクセル位置のZ値 < 現在のピクセル位置のZ値のときはぼかす必要がある if( Z4 < ZCenter ) { //現在のピクセル位置と周囲のピクセル位置のZ値の距離を計算 len2 = distance( Z4, ZCenter ); //周囲のピクセル位置のボケ > 現在のピクセル位置のボケ のときボケの強さを更新する if( len2 > len ) len = len2 * m_BlurPower; } //くっきりしたイメージとぼけたイメージを線形合成し出力 return lerp( tex2D( tex0, In.Tex ), tex2D( tex1, In.Tex ), len ); } technique TShader { //深度値を取得 pass P0 { VertexShader = compile vs_1_1 VS(); PixelShader = compile ps_2_0 PS(); } }
被写界深度のシェーダーです。ボケ量は現在のピクセル位置のZ値とフォーカス地点(ここではスクリーンの中心位置)のZ値の距離により計算します。 この距離を元にシーンのくっきりしたイメージとぼけたイメージを線形合成し、出力します。 距離が遠くなるほどボケるようになります。
(注意1) ここからなぞの処理を行っています。これはくっきりとしているオブジェクトよりボケているオブジェクトが手前にあるとき ボケているオブジェクトの輪郭がくっきりしてしまうので、輪郭付近がぼけるようにするための処理です。 m_BlurPower変数によりこの処理におけるボケ量を調整します。あまり大きな値を設定すると表示がおかしくなるので注意してください。 またm_Texel変数によりこの処理におけるボケる範囲を調整します。
左が調整なし、右が調整ありです。
画像が小さいのでわかりにくいですが、手前にいるトラの輪郭付近をみると左の画像は輪郭がはっきりしていますが、右の画像はぼけるようになっています。
---DepthOfField.h---
class DOF : public D3D2DSQUARE { private: LPD3DXEFFECT m_pEffect; D3DXHANDLE m_pTechnique, m_pZ, m_pBias, m_pTexel, m_pBlurPower; LPDIRECT3DDEVICE9 m_pd3dDevice; D3DPRESENT_PARAMETERS* m_pd3dParameters; public: DOF( LPDIRECT3DDEVICE9 pd3dDevice, D3DPRESENT_PARAMETERS* pd3dParameters ); ~DOF(); void Invalidate(); void Restore(); HRESULT Load(); void SetTexel( float TU, float TV ); void SetZ( float Z ); void SetBlurPower( float BlurPower ); void SetBias( float Bias ); void Render(); BOOL IsOK(); void CommitChanges(); LPD3DXEFFECT GetEffect(){ return m_pEffect; }; };
---DepthOfField.cpp---
DOF::DOF( LPDIRECT3DDEVICE9 pd3dDevice, D3DPRESENT_PARAMETERS* pd3dParameters ) : D3D2DSQUARE( pd3dDevice, pd3dParameters ) { m_pd3dDevice = pd3dDevice; m_pd3dParameters = pd3dParameters; m_pEffect = NULL; } DOF::~DOF() { //SafeReleaseは関数ではなくマクロ //#define SafeRelease(x) { if(x) { (x)->Release(); (x)=NULL; } } SafeRelease( m_pEffect ); } void DOF::Invalidate() { if( m_pEffect ) m_pEffect->OnLostDevice(); } void DOF::Restore() { if( m_pEffect ) m_pEffect->OnResetDevice(); } HRESULT DOF::Load() { D3DCAPS9 caps; HRESULT hr; m_pd3dDevice->GetDeviceCaps( &caps ); if( caps.VertexShaderVersion >= D3DVS_VERSION( 1, 1 ) && caps.PixelShaderVersion >= D3DPS_VERSION( 2, 0 ) ) { hr = D3D2DSQUARE::Load(); if( FAILED( hr ) ) return -1; //シェーダーの初期化 LPD3DXBUFFER pErr = NULL; hr = D3DXCreateEffectFromFile( m_pd3dDevice, _T("DOF.fx"), NULL, NULL, 0, NULL, &m_pEffect, &pErr ); if( FAILED( hr ) ) return -2; m_pTechnique = m_pEffect->GetTechniqueByName( "TShader" ); m_pZ = m_pEffect->GetParameterByName( NULL, "m_Z" ); m_pBias = m_pEffect->GetParameterByName( NULL, "m_Bias" ); m_pTexel = m_pEffect->GetParameterByName( NULL, "m_Texel" ); m_pBlurPower = m_pEffect->GetParameterByName( NULL, "m_BlurPower" ); m_pEffect->SetTechnique( m_pTechnique ); } else return -3; return S_OK; } void DOF::SetZ( float Z ) { if( m_pEffect ) m_pEffect->SetFloat( m_pZ, Z ); } void DOF::SetBias( float Bias ) { if( m_pEffect ) m_pEffect->SetFloat( m_pBias, Bias ); } void DOF::SetTexel( float TU, float TV ) { if( m_pEffect ) { float Texel[2]; Texel[0] = TU; Texel[1] = TV; m_pEffect->SetValue( m_pTexel, Texel, sizeof( float ) * 2 ); } } void DOF::SetBlurPower( float BlurPower ) { if( m_pEffect ) m_pEffect->SetFloat( m_pBlurPower, BlurPower ); } void DOF::Render() { if( m_pEffect ) { m_pEffect->Begin( NULL, 0 ); m_pEffect->BeginPass( 0 ); D3D2DSQUARE::Render(); //2Dスプライトのレンダリング m_pEffect->EndPass(); m_pEffect->End(); } } void DOF::CommitChanges() { if( m_pEffect ) m_pEffect->CommitChanges(); } BOOL DOF::IsOK() { if( m_pEffect ) return TRUE; return FALSE; }
被写界深度クラスです。D3D2DSQUAREクラスを継承しています。これはスクリーン全体を覆う、2Dスプライトオブジェクトです。 詳細は表面化散乱(Subsurface Scattering)を参照してください。
---Main.cpp---
LPDIRECT3D9 m_pdirect3d9 = NULL; LPDIRECT3DDEVICE9 m_pd3dDevice = NULL; D3DPRESENT_PARAMETERS m_d3dParameters; D3DCAPS9 Caps; //シーンのメッシュ //DirectX SDK(December 2004) に添付されているDXUTMesh.cppファイルにあるヘルパークラス群 CDXUTMesh* m_pMeshBack = NULL; CDXUTMesh* m_pMeshSun = NULL; CDXUTMesh* m_pMeshTiger = NULL; //Z値も取得できるランバート拡散照明クラスの宣言 //LAMBERT10はボリュームライトで使用しているのでそちらのページを参照 LAMBERT10* m_pLambert = NULL; //ブラーフィルターオブジェクトの宣言 BLURFILTER2* m_pBlurFilter = NULL; //被写界深度オブジェクトの宣言 DOF* m_pDOF = NULL; //色情報 LPDIRECT3DTEXTURE9 m_pColorTexture; LPDIRECT3DSURFACE9 m_pColorSurface; //Z値情報 LPDIRECT3DTEXTURE9 m_pZMapTexture; LPDIRECT3DSURFACE9 m_pZMapSurface; //Z値情報サーフェイスの色情報を参照するために一時的にコピーするためのサーフェイス LPDIRECT3DTEXTURE9 m_pLockableTexture = NULL; LPDIRECT3DSURFACE9 m_pLockableSurface = NULL; //縮小サーフェイス LPDIRECT3DTEXTURE9 m_pHalfTexture = NULL; LPDIRECT3DSURFACE9 m_pHalfSurface = NULL; //ブラー適応マップ LPDIRECT3DTEXTURE9 m_pBlurFilterTexture[2]; LPDIRECT3DSURFACE9 m_pBlurFilterSurface[2]; //スクリーンの解像度 UINT nWidth = 1024; UINT nHeight = 768; D3DXVECTOR2 BlurSurfaceSize = D3DXVECTOR2( (float)nWidth / 2, (float)nHeight / 2 ); //太陽の位置ベクトル D3DXVECTOR4 LightPos = D3DXVECTOR4( 100.0f, 100.0f, -300.0f, 1.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_pMeshSun = new CDXUTMesh(); m_pMeshSun->Create( m_pd3dDevice, _T("res\\sun.x") ); m_pMeshSun->SetFVF( m_pd3dDevice, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 ); //トラ m_pMeshTiger = new CDXUTMesh(); m_pMeshTiger->Create( m_pd3dDevice, _T("res\\tiger.x") ); m_pMeshTiger->SetFVF( m_pd3dDevice, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 ); //Z値も取得できるランバート拡散照明クラスの初期化 m_pLambert = new LAMBERT10( m_pd3dDevice ); m_pLambert->Load(); //ブラーフィルタークラスの初期化 m_pBlurFilter = new BLURFILTER2( m_pd3dDevice, (UINT)BlurSurfaceSize.x, (UINT)BlurSurfaceSize.y ); m_pBlurFilter->Load(); //被写界深度クラスの初期化 m_pDOF = new DOF( m_pd3dDevice, &m_d3dParameters ); m_pDOF->Load(); m_pd3dDevice->CreateTexture( nWidth, nHeight, 1, 0, D3DFMT_G16R16F, D3DPOOL_SYSTEMMEM, &m_pLockableTexture, NULL ); m_pLockableTexture->GetSurfaceLevel( 0, &m_pLockableSurface ); //平行光源の位置ベクトルから方向ベクトルを計算する 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() { m_pLambert->Invalidate(); m_pBlurFilter->Invalidate(); m_pDOF->Invalidate(); SafeRelease( m_pColorSurface ); SafeRelease( m_pColorTexture ); SafeRelease( m_pZMapSurface ); SafeRelease( m_pZMapTexture ); SafeRelease( m_pHalfSurface ); SafeRelease( m_pHalfTexture ); for( int i=0; i<2; i++ ) { SafeRelease( m_pBlurFilterSurface[i] ); SafeRelease( m_pBlurFilterTexture[i] ); } } //デバイスのリセット後に初期化すべきオブジェクト void Restore() { m_pLambert->Restore(); m_pBlurFilter->Restore(); m_pDOF->Restore(); D3DSURFACE_DESC desc; m_pLockableTexture->GetLevelDesc( 0, &desc ); m_pd3dDevice->CreateTexture( nWidth, nHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pColorTexture, NULL ); m_pColorTexture->GetSurfaceLevel( 0, &m_pColorSurface ); //深度値情報を格納するサーフェイス m_pd3dDevice->CreateTexture( desc.Width, desc.Height, 1, D3DUSAGE_RENDERTARGET, desc.Format, D3DPOOL_DEFAULT, &m_pZMapTexture, NULL ); m_pZMapTexture->GetSurfaceLevel( 0, &m_pZMapSurface ); //縮小サーフェイス m_pd3dDevice->CreateTexture( (UINT)BlurSurfaceSize.x, (UINT)BlurSurfaceSize.y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pHalfTexture, NULL ); m_pHalfTexture->GetSurfaceLevel( 0, &m_pHalfSurface ); //ブラーフィルター適応後サーフェイス for( int i=0; i<2; i++ ) { m_pd3dDevice->CreateTexture( (UINT)BlurSurfaceSize.x, (UINT)BlurSurfaceSize.y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pBlurFilterTexture[i], NULL ); m_pBlurFilterTexture[i]->GetSurfaceLevel( 0, &m_pBlurFilterSurface[i] ); } //固定機能パイプラインライティングを設定する 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 { static float Focus = 0.0f; D3DXMATRIX matPProj, matView, matWorld, matTiger, matScaling, matTranslation; m_pd3dDevice->BeginScene(); //遠近射影座標変換 //クリップ面はアプリケーションごとに調整すること float zf = 700.0f; D3DXMatrixPerspectiveFovLH( &matPProj, D3DX_PI/4.0f, 4.0f / 3.0f, 30.0f, zf ); m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matPProj ); //ビュー座標変換 D3DXMatrixIdentity( &matView ); m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); //トラのワールド行列作成 D3DXMatrixScaling( &matScaling, 20.0f, 20.0f, 20.0f ); D3DXMatrixTranslation( &matTranslation, 0.0f, 30.0f, 100.0 ); matTiger = matScaling * matTranslation; 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->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_POINT ); m_pd3dDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_POINT ); m_pd3dDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_NONE ); m_pd3dDevice->SetSamplerState( 2, D3DSAMP_MINFILTER, D3DTEXF_POINT ); m_pd3dDevice->SetSamplerState( 2, D3DSAMP_MAGFILTER, D3DTEXF_POINT ); m_pd3dDevice->SetSamplerState( 2, D3DSAMP_MIPFILTER, D3DTEXF_NONE ); LPDIRECT3DSURFACE9 OldSurface = NULL; //被写界深度とブラーフィルターが使用可能か if( m_pDOF->IsOK() && m_pBlurFilter->IsOK() ) { m_pd3dDevice->GetRenderTarget( 0, &OldSurface ); m_pd3dDevice->SetRenderTarget( 0, m_pColorSurface ); m_pd3dDevice->SetRenderTarget( 1, m_pZMapSurface ); } m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFFFFFFFF, 1.0f, 0L ); //**************************************************************** //ステップ1 : シーンのレンダリングおよびZ値を取得 //**************************************************************** //背景レンダリング m_pLambert->Begin(); D3DXMatrixIdentity( &matWorld ); m_pLambert->SetMatrix( &matWorld, &LightDir, zf ); m_pd3dDevice->SetTexture( 0, m_pMeshBack->m_pTextures[0] ); m_pLambert->SetAmbient( 0.1f ); m_pLambert->BeginPass(); m_pMeshBack->m_pLocalMesh->DrawSubset( 0 ); m_pLambert->EndPass(); //トラ・トラ・トラ D3DXMatrixScaling( &matScaling, 20.0f, 20.0f, 20.0f ); D3DXMatrixTranslation( &matTranslation, 0.0f, 30.0f, 100.0 ); matTiger = matScaling * matTranslation; m_pLambert->SetMatrix( &matWorld, &LightDir, zf ); m_pLambert->SetAmbient( 0.1f ); m_pd3dDevice->SetTexture( 0, m_pMeshTiger->m_pTextures[0] ); m_pLambert->BeginPass(); m_pMeshTiger->m_pLocalMesh->DrawSubset( 0 ); m_pLambert->EndPass(); D3DXMatrixTranslation( &matTranslation, -50.0f, 50.0f, 150.0 ); matTiger = matScaling * matTranslation; m_pLambert->SetMatrix( &matWorld, &LightDir, zf ); m_pLambert->BeginPass(); m_pMeshTiger->m_pLocalMesh->DrawSubset( 0 ); m_pLambert->EndPass(); D3DXMatrixTranslation( &matTranslation, -80.0f, 40.0f, 130.0 ); matTiger = matScaling * matTranslation; m_pLambert->SetMatrix( &matWorld, &LightDir, zf ); m_pLambert->BeginPass(); m_pMeshTiger->m_pLocalMesh->DrawSubset( 0 ); m_pLambert->EndPass(); m_pLambert->End(); //Z値マップにZ値を出力しないようにする m_pd3dDevice->SetRenderTarget( 1, NULL ); //空レンダリング //ライト無効 m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); //深度バッファ書込み禁止 m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE ); m_pd3dDevice->SetTexture( 0, m_pMeshBack->m_pTextures[1] ); D3DXMatrixIdentity( &matWorld ); m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); m_pMeshBack->m_pLocalMesh->DrawSubset( 1 ); //太陽レンダリング //αブレンドを有効にする 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 ); D3DXMatrixTranslation( &matTranslation, LightPos.x, LightPos.y, LightPos.z ); //ビルボードマトリックスを取得(注意2) matWorld = GetBillBoardMatrix( m_pd3dDevice, &matTranslation ); m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); m_pd3dDevice->SetTexture( 0, m_pMeshSun->m_pTextures[0] ); m_pMeshSun->m_pLocalMesh->DrawSubset( 0 ); m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ); float Z = 0.0f; //被写界深度とブラーフィルターが使用可能か if( m_pDOF->IsOK() && m_pBlurFilter->IsOK() ) { //**************************************************************** //ステップ2 : シーンのレンダリングイメージをブラー処理する //**************************************************************** m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE ); //縮小サーフェイスを使用してブラー処理を行うため、コピーする m_pd3dDevice->EndScene(); //IDirect3DDevice9::StretchRect()で設定するテクスチャ フィルタ モードは //RADEON 9600などD3DTEXF_LINEARが対応していない場合があるのでD3DTEXF_NONEにしておく // m_pd3dDevice->StretchRect( m_pColorSurface, NULL, m_pHalfSurface, NULL, D3DTEXF_LINEAR ); m_pd3dDevice->StretchRect( m_pColorSurface, NULL, m_pHalfSurface, NULL, D3DTEXF_NONE ); m_pd3dDevice->BeginScene(); //縮小サーフェイスを使用するためビューポートを変更する D3DVIEWPORT9 NewViewport, OldViewport; m_pd3dDevice->GetViewport( &OldViewport ); CopyMemory( &NewViewport, &OldViewport, sizeof( D3DVIEWPORT9 ) ); NewViewport.Height = (DWORD)BlurSurfaceSize.x; NewViewport.Width = (DWORD)BlurSurfaceSize.y; m_pd3dDevice->SetViewport( &NewViewport ); //ブラー処理の際、0.0F, 1.0F を超えてテクセル位置を参照することがあるので、巻き戻ししないようにするため CLAMP に設定する m_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP ); m_pd3dDevice->SetTexture( 0, m_pHalfTexture ); //ボケ率を調整できるようにブラーを繰り返し処理できるようにしておいた。がループ回数を大きくすると、FPSが極端に低下するので注意。 //縮小サーフェイスのサイズを小さくした方が効果的。 for( int i=0; i<1; i++ ) { m_pd3dDevice->SetRenderTarget( 0, m_pBlurFilterSurface[i%2] ); m_pBlurFilter->Render(0); m_pd3dDevice->SetRenderTarget( 0, m_pBlurFilterSurface[1-i%2] ); m_pd3dDevice->SetTexture( 0, m_pBlurFilterTexture[i%2] ); m_pBlurFilter->Render(1); m_pd3dDevice->SetTexture( 0, m_pBlurFilterTexture[1-i%2] ); } m_pd3dDevice->SetViewport( &OldViewport ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP ); m_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP ); m_pd3dDevice->SetRenderTarget( 0, OldSurface ); SafeRelease( OldSurface ); //**************************************************************** //ステップ3 : スクリーンの中心位置のピクセルのZ値を取得する //**************************************************************** //レンダーターゲットサーフェイスを直接ロックできないのでロックできるサーフェイスにいったんコピーする m_pd3dDevice->GetRenderTargetData( m_pZMapSurface, m_pLockableSurface ); D3DLOCKED_RECT rc; //参照するためにロックする hr = m_pLockableTexture->LockRect( 0, &rc, NULL, 0 ); WORD Color = 0; if( SUCCEEDED( hr ) ) { //色情報のポインタを取得 LPDWORD pTextureBits = (LPDWORD)rc.pBits; //スクリーンの中心位置のZ値を取得する Color = (WORD)(pTextureBits[nWidth * (nHeight/2)-nWidth/2]&0xFFFF); //アンロックする m_pLockableTexture->UnlockRect( 0 ); //浮動小数に変換する Z = (float)ConvS10E5Format( Color ); //フォーカスアニメーション if( Focus > Z ) Focus-=0.02f; else if( Focus < Z ) Focus+=0.02f; } //**************************************************************** //ステップ4 : 被写界深度を適応する //**************************************************************** m_pd3dDevice->SetTexture( 0, m_pColorTexture ); m_pd3dDevice->SetTexture( 1, m_pBlurFilterTexture[1] ); m_pd3dDevice->SetTexture( 2, m_pZMapTexture ); m_pDOF->SetZ( Focus ); m_pDOF->SetBias( 3.0f ); m_pDOF->SetTexel( 10.0f / (float)nWidth, 10.0f / (float)nHeight ); m_pDOF->SetBlurPower( 1.5f ); m_pDOF->Render(); m_pd3dDevice->SetTexture( 1, NULL ); m_pd3dDevice->SetTexture( 2, NULL ); m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE ); } 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; }
以上です。
(注意2) ビルボードを参照してください。
BLURFILTER2クラスですが、今回修正しました。前に参照したことがある方は申し訳ないけどブラーフィルターその2を参照して修正してください。 まあこのサイトは、こんな感じで以前公開したソースを予告なく修正することがあります。気づいてる人いるかもしれないけど。 所詮サンプルなんでこの辺はご了承ください。
今回の被写界深度ですが、ひとつ問題があります。ボケているオブジェクトにくっきりしているオブジェクトが重なっているとき、くっきりしているオブジェクト がボケに影響するため、くっきりしているオブジェクトの輪郭付近が正しくボケません。 まあこの辺の問題は、ほとんどのゲームで発生するので(というより対応してるゲームあるのか?いやその前に対応できるものなのか?)いいじゃんって気もするが(適当だな)。
あとConvS10E5Format()関数は瞳孔シミュレーションで解説してます。そちらを参照してください。