Microsoft Visual Studio .NET 2003 Microsoft DirectX 9.0 SDK (December 2004) シェーダーモデル 2.0 |
■Z値補正バンプマッピング | Prev Top Next |
関連ページ:バンプマッピング アルファチャンネルつきDDSファイル作成 |
|
今回はZ値補正バンプマッピングをやります。元ネタは「Game Watch」の記事です。バンプマッピングはメッシュの表面の微細な凸凹を表現しますが、頂点単位で凸凹にするのではなくそう見えるように明暗をつけているだけなので Zバッファに書き込むZ値に凸凹は考慮されません。今回はこの凸凹の高さ情報を考慮して、Zバッファに書き込むことをやります。
左側が通常のバンプマッピング、右側がZ値補正バンプマッピングです。地面がバンプマッピングにより凸凹になっています。左側は、水面と地面の境界線に地面の凸凹が考慮されていませんが、右側は考慮されているため複雑な形状になっています。
アルゴリズムを簡単に説明するとこんな感じになります。
1.バンプマッピングを適応する地形をレンダリングします。このとき2枚のマルチレンダーターゲットサーフェイスを使用し、1枚目のサーフェイスにシーンのレンダリング結果、2枚目に補正したZ値を出力します。
2.バックバッファにシーンのレンダリング結果をコピーする。
3.1で作成したZ値を格納したサーフェイスを参照し、水面をクリッピングしつつレンダリングする。
Z値補正はこんな感じに処理します。高さ情報を法線マップ作成時に使用した高さマップを参照して取得します。
赤いところの高さが補正値となります。正確には青いところを参照すべきなんですが、そこまで厳密にはやってません。フェイクです。
---ZBumpMap.fx---
float4x4 m_WVP; //ワールド × ビュー × 遠近射影行列 float4 m_LightDir; //ローカル座標系上でのライトベクトル float4 m_EyePos; //ローカル座標系上での視点ベクトル float4 m_Ambient = 1.0f; //環境光 float m_Specular = 0.0f; //ハイライトの範囲 float m_SpecularPower = 0.0f; //ハイライトの強度 float m_Height = 0.1f; //高さの補正値の調整量 float m_ZF; //遠近射影行列の最遠近距離 sampler tex0 : register(s0); //オブジェクトのテクスチャー sampler tex1 : register(s1); //法線マップ struct VS_OUTPUT { float4 Pos : POSITION; float2 Tex : TEXCOORD0; float3 Eye : TEXCOORD1; float3 Light : TEXCOORD2; float4 PosWVP : TEXCOORD3; }; VS_OUTPUT VS( float4 Pos : POSITION, float4 Tangent : TANGENT0, float4 Binormal : BINORMAL0, float4 Normal : NORMAL, float2 Tex : TEXCOORD0 ) { VS_OUTPUT Out; Out.Pos = mul( Pos, m_WVP ); Out.Tex = Tex; //視線ベクトルを計算 float3 Eye = normalize( m_EyePos.xyz - Pos.xyz ); //視線ベクトルを頂点座標系に変換する Out.Eye.x = dot( Eye, Tangent ); Out.Eye.y = dot( Eye, Binormal ); Out.Eye.z = dot( Eye, Normal ); Out.Eye = normalize( Out.Eye ); //オブジェクトの頂点座標 -> ライトの位置ベクトル に変換する float3 Light = -m_LightDir.xyz; //ライトベクトルを頂点座標系に変換する Out.Light.x = dot( Light, Tangent ); Out.Light.y = dot( Light, Binormal ); Out.Light.z = dot( Light, Normal ); Out.Light = normalize( Out.Light ); Out.PosWVP = Out.Pos; return Out; } struct PS_OUTPUT { float4 Col0 : COLOR0; //シーンの色情報 float4 Col1 : COLOR1; //シーンのZ値 }; PS_OUTPUT PS( VS_OUTPUT In ) { PS_OUTPUT Out; //法線マップを参照し、法線を取得する //法線マップは -1.0f 〜 1.0f の値を 0.0f 〜 1.0f の範囲に保存してあるので変換する float3 Normal = 2.0f * tex2D( tex1, In.Tex ).xyz - 1.0f; //フォンシェーディングによるスペキュラーの色を計算する //頂点 -> ライト位置ベクトル + 頂点 -> 視点ベクトル float3 H = normalize( In.Light + In.Eye ); //スペキュラーカラーを計算する float S = pow( max( 0.0f, dot( Normal, H ) ), m_Specular ) * m_SpecularPower; //シーンの色情報をセットする Out.Col0 = tex2D( tex0, In.Tex ) * max( m_Ambient, dot( Normal, In.Light ) ) + S; Out.Col1 = In.PosWVP.z / m_ZF - tex2D( tex1, In.Tex ).a * m_Height; return Out; } technique TShader { pass P0 { VertexShader = compile vs_1_1 VS(); PixelShader = compile ps_2_0 PS(); } }
バンプマッピングがベースとなっていて、Z値を出力するように変更されています。 Z値は行列変換した頂点のZ値を遠近射影行列の最遠近距離で割り算しています。このへんはメモ書きを参照してください。 高さ情報は法線マップのアルファチャンネルから取得しています。法線マップはDDSファイルです。DDSファイルの作成はアルファチャンネルつきDDSファイル作成のページを参照してください。
---ZBumpMap.h---
class ZBUMPMAP { private: LPD3DXEFFECT m_pEffect; D3DXHANDLE m_pTechnique, m_pWVP, m_pLightDir, m_pEyePos, m_pAmbient, m_pSpecular, m_pSpecularPower, m_pHeight, m_pZF; D3DXMATRIX m_matView, m_matProj; LPDIRECT3DDEVICE9 m_pd3dDevice; public: ZBUMPMAP( LPDIRECT3DDEVICE9 pd3dDevice ); ~ZBUMPMAP(); HRESULT Load(); void Invalidate(); void Restore(); void Begin(); void BeginPass(); void SetAmbient( float Ambient ); void SetAmbient( D3DXVECTOR4* pAmbient ); void SetSpecular( float Specular ); void SetSpecularPower( float SpecularPower ); void SetHeight( float Height ); void SetMatrix( D3DXMATRIX* pMatWorld, D3DXVECTOR4* pCameraPos, D3DXVECTOR4* pLightDir, float pZF ); void EndPass(); void End(); void CommitChanges(); BOOL IsOK(); LPD3DXEFFECT GetEffect(){ return m_pEffect; }; };
---ZBumpMap.cpp---
ZBUMPMAP::ZBUMPMAP( LPDIRECT3DDEVICE9 pd3dDevice ) { m_pd3dDevice = pd3dDevice; m_pEffect = NULL; } ZBUMPMAP::~ZBUMPMAP() { Invalidate(); //SafeReleaseは関数ではなくマクロ //#define SafeRelease(x) { if(x) { (x)->Release(); (x)=NULL; } } SafeRelease( m_pEffect ); } //デバイスがロストしたときにコールする関数 void ZBUMPMAP::Invalidate() { if( m_pEffect ) m_pEffect->OnLostDevice(); } //デバイスがリストアしたときにコールする関数 void ZBUMPMAP::Restore() { if( m_pEffect ) m_pEffect->OnResetDevice(); } HRESULT ZBUMPMAP::Load() { //ハードウェアがサポートするバーテックスシェーダーとピクセルシェーダーのバージョンをチェックする 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("ZBumpMap.fx"), NULL, NULL, 0, NULL, &m_pEffect, &pErr ); if( SUCCEEDED( hr ) ) { //D3DXHANDLE変数の設定 m_pTechnique = m_pEffect->GetTechniqueByName( "TShader" ); m_pWVP = m_pEffect->GetParameterByName( NULL, "m_WVP" ); m_pLightDir = m_pEffect->GetParameterByName( NULL, "m_LightDir" ); m_pEyePos = m_pEffect->GetParameterByName( NULL, "m_EyePos" ); m_pAmbient = m_pEffect->GetParameterByName( NULL, "m_Ambient" ); m_pSpecularPower = m_pEffect->GetParameterByName( NULL, "m_SpecularPower" ); m_pSpecular = m_pEffect->GetParameterByName( NULL, "m_Specular" ); m_pHeight = m_pEffect->GetParameterByName( NULL, "m_Height" ); m_pZF = m_pEffect->GetParameterByName( NULL, "m_ZF" ); m_pEffect->SetTechnique( m_pTechnique ); } else return -1; } else return -2; return S_OK; } void ZBUMPMAP::Begin() { if( m_pEffect ) { m_pd3dDevice->GetTransform( D3DTS_VIEW, &m_matView ); m_pd3dDevice->GetTransform( D3DTS_PROJECTION, &m_matProj ); m_pEffect->Begin( NULL, 0 ); } } void ZBUMPMAP::BeginPass() { if( m_pEffect ) m_pEffect->BeginPass( 0 ); } void ZBUMPMAP::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 ZBUMPMAP::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 ZBUMPMAP::SetSpecular( float Specular ) { if( m_pEffect ) m_pEffect->SetFloat( m_pSpecular, Specular ); } void ZBUMPMAP::SetSpecularPower( float SpecularPower ) { if( m_pEffect ) m_pEffect->SetFloat( m_pSpecularPower, SpecularPower ); } void ZBUMPMAP::SetHeight( float Height ) { if( m_pEffect ) m_pEffect->SetFloat( m_pHeight, Height ); } void ZBUMPMAP::SetMatrix( D3DXMATRIX* pMatWorld, D3DXVECTOR4* pCameraPos, D3DXVECTOR4* pLightDir, float pZF ) { if( m_pEffect ) { D3DXMATRIX m, m1; D3DXVECTOR4 LightDir; D3DXVECTOR4 v; //左手座標系遠近射影行列を使用したワールド座標系 m = (*pMatWorld) * m_matView * m_matProj; m_pEffect->SetMatrix( m_pWVP, &m ); //ローカル座標系上でのカメラ位置ベクトル m1 = (*pMatWorld) * m_matView; D3DXMatrixInverse( &m1, NULL, &m1 ); D3DXVec4Transform( &v, pCameraPos, &m1 ); m_pEffect->SetVector( m_pEyePos, &v ); //ローカル座標系での平行光源の方向ベクトル LightDir = *pLightDir; D3DXMatrixInverse( &m1, NULL, pMatWorld ); D3DXVec4Transform( &v, &LightDir, &m1 ); D3DXVec3Normalize( (D3DXVECTOR3*)&v, (D3DXVECTOR3*)&v ); m_pEffect->SetVector( m_pLightDir, &v ); //遠近射影行列の最遠近距離 m_pEffect->SetFloat( m_pZF, pZF ); } else m_pd3dDevice->SetTransform( D3DTS_WORLD, pMatWorld ); } void ZBUMPMAP::CommitChanges() { if( m_pEffect ) m_pEffect->CommitChanges(); } void ZBUMPMAP::EndPass() { if( m_pEffect ) m_pEffect->EndPass(); } void ZBUMPMAP::End() { if( m_pEffect ) m_pEffect->End(); } BOOL ZBUMPMAP::IsOK() { if( m_pEffect ) return TRUE; return FALSE; }
Z値補正バンプマッピングのクラスです。
---Lambert16.fx---
float4x4 m_WVPP; //ワールド座標系 × ビュー座標系 × 遠近射影座標系 float4x4 m_WVPPT; //ワールド座標系 × ビュー座標系 × 遠近射影座標系 × テクスチャー座標系 float4 m_LightDir; //平行光源の方向ベクトル float4 m_Ambient = 0.0f; //環境光 float m_ZF; //遠近射影行列の最遠近距離 sampler tex0 : register(s0); //シーンの深度マップ sampler tex1 : register(s1); //水テクスチャー struct VS_OUTPUT { float4 Pos : POSITION; float4 Col : COLOR0; float2 Tex : TEXCOORD0; float4 PosWVP : TEXCOORD1; float4 ScreenPos : TEXCOORD2; }; VS_OUTPUT VS( float4 Pos : POSITION, float4 Normal : NORMAL, float2 Tex : TEXCOORD0 ) { VS_OUTPUT Out; Out.Pos = mul( Pos, m_WVPP ); Out.Tex = Tex; Out.Col = max( m_Ambient, dot( Normal.xyz, -m_LightDir.xyz ) ); Out.PosWVP = Out.Pos; Out.ScreenPos = mul( Pos, m_WVPPT ); return Out; } float4 PS( VS_OUTPUT In ) : COLOR0 { //Z値補正深度マップより、Z値が奥のときは出力しない clip( tex2Dproj( tex0, In.ScreenPos ).r - In.PosWVP.z / m_ZF ); //水のマテリアル float4 Out; Out = tex2D( tex1, In.Tex ); Out.rgb *= In.Col; return Out; } technique TShader { pass P0 { VertexShader = compile vs_1_1 VS(); PixelShader = compile ps_2_0 PS(); } }
地形の補正された深度マップを参照し水をクリッピング処理します。水は半透明なので線形合成します。
---Lambert.h---
class LAMBERT16 { private: LPD3DXEFFECT m_pEffect; D3DXHANDLE m_pTechnique, m_pWVPP, m_pWVPPT, m_pLightDir, m_pAmbient, m_pZF; D3DXMATRIX m_matView, m_matProj; LPDIRECT3DDEVICE9 m_pd3dDevice; public: LAMBERT16( LPDIRECT3DDEVICE9 pd3dDevice ); ~LAMBERT16(); void Invalidate(); void Restore(); HRESULT Load(); void Begin(); void BeginPass(); void SetAmbient( float Ambient ); void SetAmbient( D3DXVECTOR4* pAmbient ); void SetMatrix( D3DXMATRIX* pMatWorld, D3DXVECTOR4* pLightDir, float pZF ); void CommitChanges(); void EndPass(); void End(); BOOL IsOK(); LPD3DXEFFECT GetEffect(){ return m_pEffect; }; };
---Lambert.cpp---
LAMBERT16::LAMBERT16( LPDIRECT3DDEVICE9 pd3dDevice ) { m_pd3dDevice = pd3dDevice; m_pEffect = NULL; } LAMBERT16::~LAMBERT16() { Invalidate(); //SafeReleaseは関数ではなくマクロ //#define SafeRelease(x) { if(x) { (x)->Release(); (x)=NULL; } } SafeRelease( m_pEffect ); } //デバイスがロストしたときにコールする関数 void LAMBERT16::Invalidate() { if( m_pEffect ) m_pEffect->OnLostDevice(); } //デバイスがリストアしたときにコールする関数 void LAMBERT16::Restore() { if( m_pEffect ) m_pEffect->OnResetDevice(); } HRESULT LAMBERT16::Load() { //ハードウェアがサポートするバーテックスシェーダーとピクセルシェーダーのバージョンをチェックする 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("Lambert16.fx"), NULL, NULL, 0, NULL, &m_pEffect, &pErr ); if( SUCCEEDED( hr ) ) { //D3DXHANDLE変数の設定 m_pTechnique = m_pEffect->GetTechniqueByName( "TShader" ); m_pWVPP = m_pEffect->GetParameterByName( NULL, "m_WVPP" ); m_pWVPPT = m_pEffect->GetParameterByName( NULL, "m_WVPPT" ); m_pLightDir = m_pEffect->GetParameterByName( NULL, "m_LightDir" ); m_pAmbient = m_pEffect->GetParameterByName( NULL, "m_Ambient" ); m_pZF = m_pEffect->GetParameterByName( NULL, "m_ZF" ); m_pEffect->SetTechnique( m_pTechnique ); } else return -1; } else return -2; return S_OK; } void LAMBERT16::Begin() { if( m_pEffect ) { m_pd3dDevice->GetTransform( D3DTS_VIEW, &m_matView ); m_pd3dDevice->GetTransform( D3DTS_PROJECTION, &m_matProj ); m_pEffect->Begin( NULL, 0 ); } } void LAMBERT16::BeginPass() { if( m_pEffect ) m_pEffect->BeginPass( 0 ); } void LAMBERT16::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 LAMBERT16::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 LAMBERT16::SetMatrix( D3DXMATRIX* pMatWorld, D3DXVECTOR4* pLightDir, float pZF ) { if( m_pEffect ) { D3DXMATRIX m, m1, m2; D3DXVECTOR4 LightDir; D3DXVECTOR4 v; //パースつき射影変換マトリックスで行列変換 m = (*pMatWorld) * m_matView * m_matProj; m_pEffect->SetMatrix( m_pWVPP, &m ); //行列変換マトリックスをテクスチャー座標系へ変換 D3DXMatrixScaling( &m1, 0.5f, -0.5f, 1.0f ); D3DXMatrixTranslation( &m2, 0.5f, 0.5f, 0.0f ); m = m * m1 * m2; m_pEffect->SetMatrix( m_pWVPPT, &m ); //Light LightDir = *pLightDir; D3DXMatrixInverse( &m1, NULL, pMatWorld ); D3DXVec4Transform( &v, &LightDir, &m1 ); D3DXVec4Normalize( &v, &v ); m_pEffect->SetVector( m_pLightDir, &v ); //遠近射影行列の最遠近距離 m_pEffect->SetFloat( m_pZF, pZF ); } else m_pd3dDevice->SetTransform( D3DTS_WORLD, pMatWorld ); } void LAMBERT16::CommitChanges() { if( m_pEffect ) m_pEffect->CommitChanges(); } void LAMBERT16::EndPass() { if( m_pEffect ) m_pEffect->EndPass(); } void LAMBERT16::End() { if( m_pEffect ) m_pEffect->End(); } BOOL LAMBERT16::IsOK() { if( m_pEffect ) return TRUE; return FALSE; }
水をレンダリングするシェーダークラスです。
---Main.cpp---
LPDIRECT3DDEVICE9 m_pd3dDevice = NULL; D3DPRESENT_PARAMETERS m_d3dParameters; D3DCAPS9 Caps; //シーンのメッシュ //DirectX SDK(December 2004) に添付されているDXUTMesh.cppファイルにあるヘルパークラス群 CDXUTMesh* m_pMeshBack = NULL; CDXUTMesh* m_pMeshWater = NULL; //2Dオブジェクト(表面化散乱(Subsurface Scattering) ページ参照) D3D2DSQUARE* m_pSquObj = NULL; //Z値によるクリッピングをシェーダー内で処理するクラスオブジェクトの宣言 LAMBERT16* m_pLambert16 = NULL; //Z値補正バンプマップクラスオブジェクトの宣言 ZBUMPMAP* m_pZBumpMap = NULL; //シーンの色情報を格納するサーフェイス LPDIRECT3DTEXTURE9 m_pColorTexture = NULL; LPDIRECT3DSURFACE9 m_pColorSurface = NULL; //シーンのZ値を格納するサーフェイス LPDIRECT3DTEXTURE9 m_pZBufferTexture = NULL; LPDIRECT3DSURFACE9 m_pZBufferSurface = NULL; //水の色情報を格納するサーフェイス LPDIRECT3DTEXTURE9 m_pWaterTexture = NULL; LPDIRECT3DSURFACE9 m_pWaterSurface = NULL; //水のZ値を格納するサーフェイス LPDIRECT3DTEXTURE9 m_pZBufferWaterTexture = NULL; LPDIRECT3DSURFACE9 m_pZBufferWaterSurface = NULL; //法線マップテクスチャー LPDIRECT3DTEXTURE9 m_pNormalTexture = 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); //シェーダー関連のクラスの初期化 //Z値補正バンプマップの初期化 m_pZBumpMap = new ZBUMPMAP( m_pd3dDevice ); m_pZBumpMap->Load(); //水をレンダリングするランバート拡散照明の初期化 m_pLambert16 = new LAMBERT16( m_pd3dDevice ); m_pLambert16->Load(); //メッシュのロード //背景のロード m_pMeshBack = new CDXUTMesh(); m_pMeshBack->Create( m_pd3dDevice, _T("res\\back.x") ); D3DVERTEXELEMENT9 decl[] = { {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0}, {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0}, {0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0}, {0, 48, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, D3DDECL_END() }; m_pMeshBack->SetVertexDecl( m_pd3dDevice, decl ); //水メッシュのロード m_pMeshWater = new CDXUTMesh(); m_pMeshWater->Create( m_pd3dDevice, _T("res\\water.x") ); m_pMeshWater->SetFVF( m_pd3dDevice, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 ); //法線マップをファイルからロード D3DXCreateTextureFromFileEx( m_pd3dDevice, _T("res\\normal.dds"), //法線マップテクスチャーのファイル名 D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0x0, NULL, NULL, &m_pNormalTexture ); //平行光源の位置ベクトルから方向ベクトルを計算する 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_pZBumpMap->Invalidate(); m_pLambert16->Invalidate(); SafeRelease( m_pColorTexture ); SafeRelease( m_pColorSurface ); SafeRelease( m_pZBufferTexture ); SafeRelease( m_pZBufferSurface ); SafeRelease( m_pWaterTexture ); SafeRelease( m_pWaterSurface ); SafeRelease( m_pZBufferWaterTexture ); SafeRelease( m_pZBufferWaterSurface ); } //デバイスのリセット後に初期化すべきオブジェクト void Restore() { m_pZBumpMap->Restore(); m_pLambert16->Restore(); D3DXCreateTexture( m_pd3dDevice, nWidth, nHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pColorTexture ); m_pColorTexture->GetSurfaceLevel( 0, &m_pColorSurface ); D3DXCreateTexture( m_pd3dDevice, nWidth, nHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pWaterTexture ); m_pWaterTexture->GetSurfaceLevel( 0, &m_pWaterSurface ); D3DXCreateTexture( m_pd3dDevice, nWidth, nHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &m_pZBufferTexture ); m_pZBufferTexture->GetSurfaceLevel( 0, &m_pZBufferSurface ); D3DXCreateTexture( m_pd3dDevice, nWidth, nHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &m_pZBufferWaterTexture ); m_pZBufferWaterTexture->GetSurfaceLevel( 0, &m_pZBufferWaterSurface ); //固定機能パイプラインライティングを設定する 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(); //射影座標変換 float zf = 1000.0f; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4.0f, 4.0f / 3.0f, 1.0f, zf ); m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); //ビュー座標変換 D3DXMatrixIdentity( &matView ); m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); //**************************************************************** // (STEP1) シーンをレンダリングし、カラー情報と補正したZ値情報を取得する //**************************************************************** LPDIRECT3DSURFACE9 OldSurface = NULL; m_pd3dDevice->GetRenderTarget( 0, &OldSurface ); m_pd3dDevice->SetRenderTarget( 0, m_pColorSurface ); m_pd3dDevice->SetRenderTarget( 1, m_pZBufferSurface ); //セットしたレンダーターゲットを初期化 m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFFFFFFFF, 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->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); m_pd3dDevice->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_NONE ); m_pZBumpMap->Begin(); //ステージ0に地面にテクスチャーを設定する m_pd3dDevice->SetTexture( 0, m_pMeshBack->m_pTextures[0] ); //ステージ1に法線マップを設定する m_pd3dDevice->SetTexture( 1, m_pNormalTexture ); //マテリアルを設定する m_pZBumpMap->SetAmbient( 0.1f ); m_pZBumpMap->SetSpecular( 80.0f ); m_pZBumpMap->SetSpecularPower( 0.35f ); //高さの調整値を設定する m_pZBumpMap->SetHeight( 0.003f ); D3DXMatrixIdentity( &matWorld ); m_pZBumpMap->SetMatrix( &matWorld, &EyePos, &LightDir, zf ); m_pZBumpMap->BeginPass(); m_pMeshBack->m_pLocalMesh->DrawSubset( 0 ); m_pZBumpMap->EndPass(); m_pZBumpMap->End(); m_pd3dDevice->SetRenderTarget( 1, NULL ); m_pd3dDevice->SetTexture( 1, NULL ); //**************************************************************** // (STEP2) バックバッファにコピーする //**************************************************************** m_pd3dDevice->SetRenderTarget( 0, OldSurface ); SafeRelease( OldSurface ); m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE ); m_pd3dDevice->SetTexture( 0, m_pColorTexture ); m_pSquObj->Render(); //**************************************************************** // (STEP3) 水をレンダリングする //**************************************************************** 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_INVSRCALPHA ); m_pd3dDevice->SetTexture( 0, m_pZBufferTexture ); m_pd3dDevice->SetTexture( 1, m_pMeshWater->m_pTextures[0] ); m_pLambert16->Begin(); m_pLambert16->SetAmbient( 0.0f ); D3DXMatrixIdentity( &matWorld ); m_pLambert16->SetMatrix( &matWorld, &LightDir, zf ); m_pLambert16->BeginPass(); m_pMeshWater->m_pLocalMesh->DrawSubset( 0 ); m_pLambert16->EndPass(); m_pLambert16->End(); m_pd3dDevice->SetRenderTarget( 1, NULL ); m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ); 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; }
以上です。
水と地面との境界ですが、水面が浅くなるほど透明度を上げるようにして滑らかになるようにした方がいいと思います。 実は。そんなわけで今回のネタはあんまり意味ないかも。