Microsoft Visual Studio .NET 2003
 Microsoft DirectX 9.0 SDK (December 2004)
 シェーダーモデル 2.0

■投影テクスチャーシャドー Prev  Top  Next
関連ページ:デプスバッファシャドー ブラーフィルター ブラーフィルターその2

2009/02/28: ぼかし方法を変更

無料レンタルサーバー

さて今回は投影テクスチャーシャドーをやります。 元ネタはまたまた「Game Watch」の記事の「バーチャファイター5」です。 バーチャ5ネタは今回で最後です。
投影テクスチャーシャドーは、AオブジェクトのシルエットをBオブジェクトに投影します。このシルエットが影となります。 なお、今回紹介する投影テクスチャーシャドーでは、影を落とすオブジェクトまでの距離の長さにより影のエッジのぼかし具合を調整するようにします。


投影テクスチャーシャドーです。手前に伸びている影がぼけています。トラの足元付近の影の輪郭はくっきりします。

次に処理フローです。
1.カメラ位置からシーンを眺めて投影テクスチャーシャドーマップを作成。
2.1で作成した投影テクスチャーシャドーマップをぼかす。
3.1と2で作成した投影テクスチャーシャドーマップをもとに影を適応。
1.カメラ位置からシーンを眺めて投影テクスチャーシャドーマップを作成。
2.1で作成した投影テクスチャーシャドーマップをぼかす。
3.2でぼかした投影テクスチャーシャドーマップをさらぼかす。
4.2と3で作成した投影テクスチャーシャドーマップをもとに影を適応。

みたいな感じです。
1はデプスバッファシャドーでやっていることとちょっと異なります。 今回はRGB成分に影の色を出力し、A成分にオブジェクトのZ値を出力します。
2はただのブラーなので省略。
3で投影テクスチャーシャドーマップを適応します。 投影テクスチャーシャドーマップのA成分に格納されているZ値とオブジェクトのZ値との距離の長さにより ぼけてないイメージとぼかしたイメージを線形合成します。距離が長くなるほどぼかすようにします。 線形合成する色はRGB成分です。この色を地面のマテリアル色に積算することで影を適応します。

1はデプスバッファシャドーでやっていることとちょっと異なります。 今回はRGB成分に影の色を出力し、A成分にオブジェクトのZ値を出力します。
2と3はただのブラーなので省略。
4で投影テクスチャーシャドーマップを適応します。 投影テクスチャーシャドーマップのA成分に格納されているZ値とオブジェクトのZ値との距離の長さにより ぼけてないイメージとぼかしたイメージを線形合成します。距離が長くなるほどぼかすようにします。 線形合成する色はRGB成分です。この色を地面のマテリアル色に積算することで影を適応します。

さてソースを見ていきます。

---Lambert20.fx---


float4   m_Color;  // RGBに出力する色
float4x4 m_WVOP;   // ワールド × ビュー × 正射影

struct VS_OUTPUT
{
   float4 Pos     : POSITION;
   float2 Depth   : TEXCOORD0;
};

VS_OUTPUT VS( float4 Pos     : POSITION,
              float4 Normal  : NORMAL,
              float2 Tex     : TEXCOORD0 )
{
   VS_OUTPUT Out;

   Out.Pos    = mul( Pos, m_WVOP );
   Out.Depth.xy = Out.Pos.zw;

   return Out;
}

float4 PS( VS_OUTPUT In ) : COLOR0
{  
   float4 Out;

   //RGBを出力
   Out.rgb = m_Color.rgb;

   //深度情報を格納する
   Out.a = In.Depth.x / In.Depth.y;

   return Out;
}

technique TShader
{
   pass P0
   {
      VertexShader = compile vs_1_1 VS();
      PixelShader  = compile ps_2_0 PS();
   }
}

RGB成分に影の色、A成分にZ値を出力します。

---Lambert.h---


class LAMBERT20
{
private:
   LPD3DXEFFECT m_pEffect;
   D3DXHANDLE m_pTechnique, m_pWVOP, m_pColor;
   LPDIRECT3DDEVICE9 m_pd3dDevice;

public:
   LAMBERT20( LPDIRECT3DDEVICE9 pd3dDevice );
   ~LAMBERT20();
   void Invalidate();
   void Restore();
   HRESULT Load();
   void Begin();
   void BeginPass();
   void SetColor( float Color );
   void SetColor( D3DXVECTOR4* pColor );
   void SetMatrix( D3DXMATRIX* pMatWVOP );
   void CommitChanges();
   void EndPass();
   void End();
   BOOL IsOK();
   LPD3DXEFFECT GetEffect(){ return m_pEffect; };
};

---Lambert.cpp---


LAMBERT20::LAMBERT20( LPDIRECT3DDEVICE9 pd3dDevice )
{
   m_pd3dDevice = pd3dDevice;
   m_pEffect = NULL;
}

LAMBERT20::~LAMBERT20()
{
   SafeRelease( m_pEffect );
}

void LAMBERT20::Invalidate()
{
   if( m_pEffect )
      m_pEffect->OnLostDevice();
}

void LAMBERT20::Restore()
{
   if( m_pEffect )
      m_pEffect->OnResetDevice();
}

HRESULT LAMBERT20::Load()
{
   D3DCAPS9 caps;

   m_pd3dDevice->GetDeviceCaps( &caps );
   if( caps.VertexShaderVersion >= D3DVS_VERSION( 1, 1 ) && caps.PixelShaderVersion >= D3DPS_VERSION( 2, 0 ) )
   {
      LPD3DXBUFFER pErr = NULL;
      HRESULT hr = D3DXCreateEffectFromFile( m_pd3dDevice, _T("Lambert20.fx"), NULL, NULL, 0, NULL, &m_pEffect, &pErr );
      if( SUCCEEDED( hr ) )
      {
         m_pTechnique = m_pEffect->GetTechniqueByName( "TShader" );
         m_pWVOP      = m_pEffect->GetParameterByName( NULL, "m_WVOP" );
         m_pColor     = m_pEffect->GetParameterByName( NULL, "m_Color" );
         m_pEffect->SetTechnique( m_pTechnique );   
      }

      else
      {
         return -1;
      }
   }

   else
   {
      return -2;
   }

   return S_OK;
}

void LAMBERT20::Begin()
{
   if(  m_pEffect )
   {
      m_pEffect->Begin( NULL, 0 );
   }
}

void LAMBERT20::BeginPass()
{
   if( m_pEffect )
   {
      m_pEffect->BeginPass( 0 );
   }
}

void LAMBERT20::SetColor( float Color )
{
   if( m_pEffect )
   {
      D3DXVECTOR4 A;
      A = D3DXVECTOR4( Color, Color, Color, Color );
      m_pEffect->SetVector( m_pColor, &A );
   }
}

void LAMBERT20::SetColor( D3DXVECTOR4* pColor )
{
   if( m_pEffect )
      m_pEffect->SetVector( m_pColor, pColor );
}

void LAMBERT20::SetMatrix( D3DXMATRIX* pMatWVOP )
{
   if( m_pEffect )
   {
      D3DXMATRIX m, m1, m2;

      m_pEffect->SetMatrix( m_pWVOP, pMatWVOP );
   }
}

void LAMBERT20::CommitChanges()
{
   if( m_pEffect )
      m_pEffect->CommitChanges();
}

void LAMBERT20::EndPass()
{
   if( m_pEffect )
   {
      m_pEffect->EndPass();
   }
}

void LAMBERT20::End()
{
   if( m_pEffect )
   {
      m_pEffect->End();
   }
}

BOOL LAMBERT20::IsOK()
{
   if( m_pEffect )
      return TRUE;

   return FALSE;
}

特に説明はいらないでしょう。

---ProjectedTextureShadow.fx---


float4x4 m_WVP;                 // ワールド × ビュー × 射影
float4x4 m_LWVP;                // ライト基準の行列変換
float4x4 m_LWVPT;               // ライト基準の行列変換をテクスチャー座標系に変換

float4   m_LightDir;            // 平行光源の方向ベクトル
float4   m_Ambient = 0.0f;      // 環境光
float    m_Scale;               // 距離の調整値

sampler tex0 : register(s0);    // オブジェクトのテクスチャー
sampler tex1 : register(s1);    // ぼかした投影テクスチャーシャドーマップ
sampler tex2 : register(s2);    // すごくぼかした投影テクスチャーシャドーマップ

struct VS_OUTPUT
{
   float4 Pos       : POSITION;
   float4 Col       : COLOR0;
   float2 Tex       : TEXCOORD0;
   float4 LightUV   : TEXCOORD1;      //Zバッファのテクセル座標
   float2 Depth     : TEXCOORD2;    //カメラ基準の行列変換により計算した深度値
};

VS_OUTPUT VS( float4 Pos     : POSITION,
              float4 Normal  : NORMAL,
              float2 Tex     : TEXCOORD0 )
{
   VS_OUTPUT Out;
   
   Out.Pos    = mul( Pos, m_WVP );

   Out.Tex    = Tex;
   
   float3 L = -m_LightDir.xyz;
   float3 N = normalize( Normal.xyz );

   //ランバート拡散照明によるライティング
   Out.Col = max( m_Ambient, dot(N, L) );

   // Zバッファサーフェイスの深度情報を取得するためにテクセル座標をセット
   Out.LightUV = mul( Pos, m_LWVPT );
   
   //Z値をセット
   Out.Depth.xy = mul( Pos, m_LWVP ).zw;
   
   return Out;
}

float4 PS( VS_OUTPUT In ) : COLOR0
{
   float4 Out;

   //Zバッファサーフェイスから深度値を取得する
   float4 ZMap = tex2Dproj( tex2, In.LightUV );
   
   //影を発生させるオブジェクトと影が落ちるオブジェクトとの距離
   float l = min( 1.0f, distance( ZMap.a, In.Depth.x / In.Depth.y ) * m_Scale );
   
   //ライティングによる結果に投影テクスチャーシャドーマップを適応
   float4 Shadow = lerp( tex2Dproj( tex1, In.LightUV ), ZMap, l );
   
   //影を適応する
   Out = tex2D( tex0, In.Tex ) * In.Col * Shadow;
   
   return Out;
}

technique TShader
{
   pass P0
   {
      VertexShader = compile vs_1_1 VS();
      PixelShader  = compile ps_2_0 PS();
   }
}

投影テクスチャシャドーマップを適応します。

---DepthBufferShadow.h---


class PROJECTED_TEXTURE_SHADOW
{
private:
   LPD3DXEFFECT m_pEffect;
   D3DXHANDLE m_pTechnique, m_pWVP, m_pLWVP, m_pLWVPT, m_pLightDir, m_pAmbient, m_pScale;
   D3DXMATRIX m_matView, m_matProj;
   LPDIRECT3DDEVICE9 m_pd3dDevice;

public:
   PROJECTED_TEXTURE_SHADOW( LPDIRECT3DDEVICE9 pd3dDevice );
   ~PROJECTED_TEXTURE_SHADOW();
   void Invalidate();
   void Restore();
   HRESULT Load();
   void Begin();
   void BeginPass();
   void SetAmbient( float Ambient );
   void SetAmbient( D3DXVECTOR4* pAmbient );
   void SetMatrix( D3DXMATRIX* pMatWorld, D3DXMATRIX* pMatLight, D3DXVECTOR4* pLightDir );
   void SetScale( float Scale );
   void CommitChanges();
   void EndPass();
   void End();
   BOOL IsOK();
   LPD3DXEFFECT GetEffect(){ return m_pEffect; };
};

---DepthBufferShadow.cpp---

PROJECTED_TEXTURE_SHADOW::PROJECTED_TEXTURE_SHADOW( LPDIRECT3DDEVICE9 pd3dDevice )
{
   m_pd3dDevice = pd3dDevice;
   m_pEffect = NULL;
}

PROJECTED_TEXTURE_SHADOW::~PROJECTED_TEXTURE_SHADOW()
{
   SafeRelease( m_pEffect );
}

void PROJECTED_TEXTURE_SHADOW::Invalidate()
{
   if( m_pEffect )
      m_pEffect->OnLostDevice();
}

void PROJECTED_TEXTURE_SHADOW::Restore()
{
   if( m_pEffect )
      m_pEffect->OnResetDevice();
}

HRESULT PROJECTED_TEXTURE_SHADOW::Load()
{
   D3DCAPS9 caps;

   m_pd3dDevice->GetDeviceCaps( &caps );
   if( caps.VertexShaderVersion >= D3DVS_VERSION( 1, 1 ) && caps.PixelShaderVersion >= D3DPS_VERSION( 2, 0 ) )
   {
      LPD3DXBUFFER pErr = NULL;
      HRESULT hr = D3DXCreateEffectFromFile( m_pd3dDevice, _T("ProjectedTextureShadow.fx"), NULL, NULL, 0, NULL, &m_pEffect, &pErr );
      if( SUCCEEDED( hr ) )
      {
         m_pTechnique   = m_pEffect->GetTechniqueByName( "TShader" );
         m_pWVP         = m_pEffect->GetParameterByName( NULL, "m_WVP" );
         m_pLWVP        = m_pEffect->GetParameterByName( NULL, "m_LWVP" );
         m_pLWVPT       = m_pEffect->GetParameterByName( NULL, "m_LWVPT" );
         m_pLightDir    = m_pEffect->GetParameterByName( NULL, "m_LightDir" );
         m_pAmbient     = m_pEffect->GetParameterByName( NULL, "m_Ambient" );
         m_pScale       = m_pEffect->GetParameterByName( NULL, "m_Scale" );

         m_pEffect->SetTechnique( m_pTechnique );   
      }

      else
      {
         return -1;
      }
   }

   else
   {
      return -2;
   }

   return S_OK;
}

void PROJECTED_TEXTURE_SHADOW::Begin()
{
   if(  m_pEffect )
   {
      m_pd3dDevice->GetTransform( D3DTS_VIEW, &m_matView );
      m_pd3dDevice->GetTransform( D3DTS_PROJECTION, &m_matProj );
      m_pEffect->Begin( NULL, 0 );
   }
}

void PROJECTED_TEXTURE_SHADOW::BeginPass()
{
   if( m_pEffect )
   {
      m_pEffect->BeginPass( 0 );
   }
}

void PROJECTED_TEXTURE_SHADOW::SetAmbient( float Ambient )
{
   if( m_pEffect )
   {
      D3DXVECTOR4 A;
      A = D3DXVECTOR4( Ambient, Ambient, Ambient, 1.0f );
      m_pEffect->SetVector( m_pAmbient, &A );
   }

   else
   {
      D3DMATERIAL9 old_material;
      m_pd3dDevice->GetMaterial( &old_material );
      old_material.Ambient.r = Ambient;
      old_material.Ambient.g = Ambient;
      old_material.Ambient.b = Ambient;
      old_material.Ambient.a = 1.0f;
      m_pd3dDevice->SetMaterial( &old_material );
   }
}

void PROJECTED_TEXTURE_SHADOW::SetAmbient( D3DXVECTOR4* pAmbient )
{
   if( m_pEffect )
      m_pEffect->SetVector( m_pAmbient, pAmbient );

   else
   {
      D3DMATERIAL9 old_material;
      m_pd3dDevice->GetMaterial( &old_material );
      old_material.Ambient.r = pAmbient->x;
      old_material.Ambient.g = pAmbient->y;
      old_material.Ambient.b = pAmbient->z;
      old_material.Ambient.a = pAmbient->w;
      m_pd3dDevice->SetMaterial( &old_material );
   }
}

//ローカル座標系
void PROJECTED_TEXTURE_SHADOW::SetMatrix( D3DXMATRIX* pMatWorld, D3DXMATRIX* pMatLight, D3DXVECTOR4* pLightDir )
{
   if( m_pEffect )
   {
      D3DXMATRIX m, ms, mt;
      D3DXVECTOR4 LightDir;
      D3DXVECTOR4 v;

      //カメラ基準の行列変換マトリックスをセットする
      m = (*pMatWorld) * m_matView * m_matProj;
      m_pEffect->SetMatrix( m_pWVP, &m );

      LightDir = *pLightDir;
      D3DXMatrixInverse( &m, NULL, pMatWorld );
      D3DXVec4Transform( &v, &LightDir, &m );
      D3DXVec4Normalize( &v, &v );
      m_pEffect->SetVector( m_pLightDir, &v );

      //ライト基準の行列変換マトリックスをセットする
      m = (*pMatWorld) * (*pMatLight);
      m_pEffect->SetMatrix( m_pLWVP, &m );

      //テクスチャー座標系へ変換
      D3DXMatrixScaling( &ms, 0.5f, -0.5f, 1.0f );
      D3DXMatrixTranslation( &mt, 0.5f, 0.5f, 0.0f );
      m = m * ms * mt;
      m_pEffect->SetMatrix( m_pLWVPT, &m );
   }

   else
      m_pd3dDevice->SetTransform( D3DTS_WORLD, pMatWorld );
}

void PROJECTED_TEXTURE_SHADOW::SetScale( float Scale )
{
   if( m_pEffect )
      m_pEffect->SetFloat( m_pScale, Scale );
}

void PROJECTED_TEXTURE_SHADOW::CommitChanges()
{
   if( m_pEffect )
      m_pEffect->CommitChanges();
}

void PROJECTED_TEXTURE_SHADOW::EndPass()
{
   if( m_pEffect )
   {
      m_pEffect->EndPass();
   }
}

void PROJECTED_TEXTURE_SHADOW::End()
{
   if( m_pEffect )
   {
      m_pEffect->End();
   }
}

BOOL PROJECTED_TEXTURE_SHADOW::IsOK()
{
   if( m_pEffect )
      return TRUE;

   return FALSE;
}

投影テクスチャシャドーマップを適応します。

---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_pMeshTiger = NULL;

//2Dオブジェクト(表面化散乱(Subsurface Scattering) ページ参照)
D3D2DSQUARE* m_pSquObj = NULL;

//ランバート拡散照明クラスの宣言
LAMBERT1* m_pLambert1 = NULL;

//シーンのZ値を取得しつつモデルのシルエットを出力するクラスの宣言
LAMBERT20* m_pLambert20 = NULL;

//投影テクスチャーシャドークラスの宣言
PROJECTED_TEXTURE_SHADOW* m_pProjectedTextureShadow = NULL;

//ブラーフィルタークラスの宣言
BLURFILTER1* m_pBlurFilter1 = NULL;

//さらにぼかすブラーフィルタークラスの初期化
BLURFILTER2* m_pBlurFilter2 = NULL;

//シーンのZ値を格納するサーフェイス
LPDIRECT3DTEXTURE9    m_pZBufferTexture = NULL;
LPDIRECT3DSURFACE9    m_pZBufferSurface = NULL;

//Zバッファ
LPDIRECT3DSURFACE9    m_pZBuffer        = NULL;

//ぼかした影イメージ
LPDIRECT3DTEXTURE9    m_pBlurTexture[3] = { NULL, NULL, NULL };
LPDIRECT3DSURFACE9    m_pBlurSurface[3] = { NULL, NULL, NULL };

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

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

D3DXVECTOR4 LightEye = D3DXVECTOR4( 0.0f, 0.0f, -1.0f, 1.0f );
D3DXVECTOR4 LightUp = D3DXVECTOR4( 0.0f, 1.0f, 0.0f, 1.0f );
D3DXVECTOR4 LightAt = 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_pLambert1 = new LAMBERT1( m_pd3dDevice );
   m_pLambert1->Load();

   //シーンのZ値を取得しつつモデルのシルエットを出力するクラス
   m_pLambert20 = new LAMBERT20( m_pd3dDevice );
   m_pLambert20->Load();
   
   //投影テクスチャーシャドークラスの初期化
   m_pProjectedTextureShadow = new PROJECTED_TEXTURE_SHADOW( m_pd3dDevice );
   m_pProjectedTextureShadow->Load();

   //ブラーフィルタークラスの初期化
   m_pBlurFilter1 = new BLURFILTER1( m_pd3dDevice, 512, 512 );
   m_pBlurFilter1->Load();

   //さらにぼかすブラーフィルタークラスの初期化
   m_pBlurFilter2 = new BLURFILTER2( m_pd3dDevice, 512, 512 );
   m_pBlurFilter2->Load();


   //背景
   m_pMeshBack = new CDXUTMesh();
   m_pMeshBack->Create( m_pd3dDevice, _T("res\\01.x") );
   m_pMeshBack->SetFVF( m_pd3dDevice, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | 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 );

   //2Dオブジェクトのロード
   m_pSquObj = new D3D2DSQUARE( m_pd3dDevice, &m_d3dParameters );
   m_pSquObj->Load();

   //平行光源関連の位置ベクトルを回転
   D3DXMATRIX LightMatrix, mx, my;
   D3DXMatrixRotationX( &mx, D3DXToRadian( 30.0f ) );
   D3DXMatrixRotationY( &my, D3DXToRadian( 40.0f ) );
   LightMatrix = mx * my;

   D3DXVec3TransformCoord( (D3DXVECTOR3*)&LightEye, (D3DXVECTOR3*)&LightEye, &LightMatrix );
   D3DXVec3TransformCoord( (D3DXVECTOR3*)&LightUp, (D3DXVECTOR3*)&LightUp, &LightMatrix );
   D3DXVec3TransformCoord( (D3DXVECTOR3*)&LightAt, (D3DXVECTOR3*)&LightAt, &LightMatrix );

   //平行光源の方向ベクトル
   LightDir = LightAt - LightEye;

   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_pLambert1->Invalidate();
   m_pLambert20->Invalidate();
   m_pProjectedTextureShadow->Invalidate();
   m_pBlurFilter->Invalidate();
   m_pBlurFilter2->Invalidate();

   SafeRelease( m_pZBufferTexture );
   SafeRelease( m_pZBufferSurface );

   SafeRelease( m_pZBuffer );

   for( int i=0; i<3; i++ )
   {
      SafeRelease( m_pBlurTexture[i] );
      SafeRelease( m_pBlurSurface[i] );
   }
}

//デバイスのリセット後に初期化すべきオブジェクト
void Restore()
{
   DWORD SurfaceWidth  = nWidth  * 2;
   DWORD SurfaceHeight = nHeight * 2;

   m_pLambert1->Restore();
   m_pLambert20->Restore();
   m_pProjectedTextureShadow->Restore();
   m_pBlurFilter->Restore();
   m_pBlurFilter2->Restore();

   //レンダーターゲットサーフェイスとZバッファの縦横のサイズは一致させる
   D3DXCreateTexture( m_pd3dDevice,
                      SurfaceWidth,
                      SurfaceHeight,
                      1,
                      D3DUSAGE_RENDERTARGET,
                      D3DFMT_A8R8G8B8,
                      D3DPOOL_DEFAULT,
                      &m_pZBufferTexture );
   m_pZBufferTexture->GetSurfaceLevel( 0, &m_pZBufferSurface );

   m_pd3dDevice->CreateDepthStencilSurface( SurfaceWidth,
                                            SurfaceHeight,
                                            D3DFMT_D16,
                                            m_d3dParameters.MultiSampleType,
                                            m_d3dParameters.MultiSampleQuality,
                                            TRUE,
                                            &m_pZBuffer,
                                            NULL );

   D3DXCreateTexture( m_pd3dDevice,
                        512,
                        512,
                        1,
                         D3DUSAGE_RENDERTARGET,
                        D3DFMT_A8R8G8B8,
                        D3DPOOL_DEFAULT,
                          &m_pBlurTexture[0] );
   m_pBlurTexture[0]->GetSurfaceLevel( 0, &m_pBlurSurface[0] );

   for( int i=1; i<3; i++ )
   {
      D3DXCreateTexture( m_pd3dDevice,
                           128,
                           128,
                           1,
                            D3DUSAGE_RENDERTARGET,
                           D3DFMT_A8R8G8B8,
                           D3DPOOL_DEFAULT,
                             &m_pBlurTexture[i] );
      m_pBlurTexture[i]->GetSurfaceLevel( 0, &m_pBlurSurface[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
   {
      D3DXMATRIX matSM, matProj, matView, matScale, matViewLight, matProjLight, matWorld, m;
      D3DXVECTOR3 LEye;

      m_pd3dDevice->BeginScene();

      //カメラ位置からの射影座標変換
      D3DXMatrixPerspectiveFovLH( &matProj,
                                  D3DX_PI/4.0f,
                                  (float)nWidth / (float)nHeight,
                                  50.0f, 1000.0f );
      m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

      //****************************************************************   
      // STEP1:ライト位置からシーンを眺めて深度マップを作成
      //****************************************************************   

      //ライト位置からのビュー座標系
      //ライトの視点を適当に伸ばす
      LEye = D3DXVECTOR3( LightEye.x, LightEye.y, LightEye.z ) * 400.0f;
      D3DXMatrixLookAtLH( &matViewLight, &LEye, (D3DXVECTOR3*)&LightAt, (D3DXVECTOR3*)&LightUp );

      //ライト位置からの射影行列はゆがませないようにするため左手座標系正射影行列を作成する
      D3DXMatrixOrthoLH( &matProjLight, 
                         (float)nWidth / 2.5f, (float)nHeight / 2.5f,
                         250.0f, 500.0f );

      //カメラのビュー行列 * カメラの射影行列
      matSM = matViewLight * matProjLight;

      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_LINEAR );
      m_pd3dDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
      m_pd3dDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_NONE );

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

      LPDIRECT3DSURFACE9 OldSurface = NULL, OldZMap = NULL;
      m_pd3dDevice->GetRenderTarget( 0, &OldSurface );
      m_pd3dDevice->SetRenderTarget( 0, m_pZBufferSurface );

      m_pd3dDevice->GetDepthStencilSurface( &OldZMap );
      m_pd3dDevice->SetDepthStencilSurface( m_pZBuffer );

      m_pd3dDevice->Clear( 0L,
                           NULL,
                           D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                           0xFFFFFFFF,    //白ですべて塗りつぶし 
                           1.0f,
                           0L
                         );

      //とら
      m_pLambert20->Begin();
      m_pLambert20->SetColor( 0.1f );
      D3DXMATRIX matTeaPot[2], matScaling, matTranslation;
      D3DXMatrixScaling( &matScaling, 50.0f, 50.0f, 50.0f );
      D3DXMatrixTranslation( &matTranslation, -40.0f, 90.0f, 35.0 );
      matTeaPot[0] = matScaling * matTranslation;
      m = matTeaPot[0] * matSM;
      m_pLambert20->SetMatrix( &m );
      m_pLambert20->BeginPass();
      m_pMeshTiger->m_pLocalMesh->DrawSubset( 0 );
      m_pLambert20->EndPass();

      D3DXMatrixTranslation( &matTranslation, -40.0f, 160.0f, 35.0 );
      matTeaPot[1] = matScaling * matTranslation;
      m = matTeaPot[1] * matSM;
      m_pLambert20->SetMatrix( &m );
      m_pLambert20->BeginPass();
      m_pMeshTiger->m_pLocalMesh->DrawSubset( 0 );
      m_pLambert20->EndPass();
      m_pLambert20->End();

      m_pd3dDevice->SetDepthStencilSurface( OldZMap );
      SafeRelease( OldZMap );


      //****************************************************************   
      // STEP2:影イメージをぼかす
      //****************************************************************   

      m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );

      m_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
      m_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );

      //縮小サーフェイスを使用するのでビューポートを変更する
      D3DSURFACE_DESC pDesc;

      D3DVIEWPORT9 OldViewport, NewViewport;
      m_pd3dDevice->GetViewport( &OldViewport );
      CopyMemory( &NewViewport, &OldViewport, sizeof( D3DVIEWPORT9 ) );
      
      m_pBlurSurface[0]->GetDesc( &pDesc );
      NewViewport.Width  = pDesc.Width;
      NewViewport.Height = pDesc.Height;
      m_pd3dDevice->SetViewport( &NewViewport );

      m_pd3dDevice->SetTexture( 0, m_pZBufferTexture );
      m_pd3dDevice->SetRenderTarget( 0, m_pBlurSurface[0] );

      //2Dポリゴンの大きさをサーフェイスにあわせる
      m_pBlurFilter1->Resize( pDesc.Width, pDesc.Height );
      m_pBlurFilter1->Render();

      m_pBlurSurface[2]->GetDesc( &pDesc );
      NewViewport.Width  = pDesc.Width;
      NewViewport.Height = pDesc.Height;
      m_pd3dDevice->SetViewport( &NewViewport );

      m_pd3dDevice->SetTexture( 0, m_pBlurTexture[0] );

      //2Dポリゴンの大きさをサーフェイスにあわせる
      //Resize()は継承したD3D2DSQUAREクラスのメンバ関数
      //表面化散乱(Subsurface Scattering)を参照
      m_pBlurFilter2->Resize( pDesc.Width, pDesc.Height );

      //適当に繰り返しぼかす
      for( int i=0; i<3; i++ )
      {
         m_pd3dDevice->SetRenderTarget( 0, m_pBlurSurface[1] );
         m_pBlurFilter2->Render(0);

         m_pd3dDevice->SetTexture( 0, m_pBlurTexture[1] );
         m_pd3dDevice->SetRenderTarget( 0, m_pBlurSurface[2] );
         m_pBlurFilter2->Render(1);

         m_pd3dDevice->SetTexture( 0, m_pBlurTexture[2] );
      }

      m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );

      m_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP );
      m_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP );

      m_pd3dDevice->SetViewport( &OldViewport );


      //****************************************************************   
      // STEP3:影イメージをレンダリング
      //****************************************************************   

      m_pd3dDevice->SetRenderTarget( 0, OldSurface );
      SafeRelease( OldSurface );

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

      m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
      m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

      //地面
      m_pProjectedTextureShadow->Begin();
      D3DXMatrixIdentity( &matWorld );
      m_pProjectedTextureShadow->SetMatrix( &matWorld, &matSM, &LightDir );
      m_pProjectedTextureShadow->SetAmbient( 0.1f );
      m_pProjectedTextureShadow->SetScale( 1.0f );
      m_pd3dDevice->SetTexture( 0, m_pMeshBack->m_pTextures[0] );
      m_pd3dDevice->SetTexture( 1, m_pBlurTexture[0] );
      m_pd3dDevice->SetTexture( 2, m_pBlurTexture[2] );
      m_pProjectedTextureShadow->BeginPass();
      m_pMeshBack->m_pLocalMesh->DrawSubset( 0 );
      m_pProjectedTextureShadow->EndPass();
      m_pProjectedTextureShadow->End();

      m_pd3dDevice->SetTexture( 1, NULL );
      m_pd3dDevice->SetTexture( 2, NULL );

      //空
      m_pLambert1->Begin();
      D3DXMatrixIdentity( &matWorld );
      m_pLambert1->SetMatrix( &matWorld, &LightDir );
      m_pLambert1->SetAmbient( 1.0f );
      m_pd3dDevice->SetTexture( 0, m_pMeshBack->m_pTextures[1] );
      m_pLambert1->BeginPass(0);
      m_pMeshBack->m_pLocalMesh->DrawSubset( 1 );
      m_pLambert1->EndPass();

      //とら
      m_pLambert1->SetAmbient( 0.15f );
      m_pLambert1->SetMatrix( &matTeaPot[0], &LightDir );
      m_pd3dDevice->SetTexture( 0, m_pMeshTiger->m_pTextures[0] );
      m_pLambert1->BeginPass();
      m_pMeshTiger->m_pLocalMesh->DrawSubset( 0 );
      m_pLambert1->EndPass();

      m_pLambert1->SetMatrix( &matTeaPot[1], &LightDir );
      m_pLambert1->BeginPass();
      m_pMeshTiger->m_pLocalMesh->DrawSubset( 0 );
      m_pLambert1->EndPass();
      m_pLambert1->End();
      
      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;
}

以上です。

さて、バーチャ5とはレンダリング結果が微妙に違いますな。バーチャ5でどうやってるのかわからんけど。 あと投影テクスチャーシャドーの場合、セルフシャドーは適応できません。したがってティーポットに シャドーは落とせないのでご注意を。バーチャ5ではキャラ自身に落ちるセルフシャドーと背景に落ちる影とで別々の影生成アルゴリズムを使用してるようですが。
前よりマシになりました。とりあえずこんなもんでいいと思いますが、ぼかし方法はもっと効率化した方がよいと思われ。 ちなみにバーチャ5では投影テクスチャーシャドーマップをミップマップ化してます。 この方がきれいにはなりますが、負荷は高くなります。まあそのへんは自己判断で。 あとは投影距離が遠くなるほど影がぼけるようにするのと同時に、影の濃度が薄くなるような処理を追加すれば完璧ですか?

Prev  Top  Next
inserted by FC2 system