这个例子是关于着色器参数的,涉及两方面。首先是ParameterBlock,通过参数块将几个参数绑定在一起,用一个句柄设定一组参数,简化参数设定。第二个是ParameterSharing,在创建效果框架时传递EffectPool参数,拥有同一个Pool的效果框架会共享Pool的参数,同时会同步进行更新。具体可以参考SDK Documentation。
下面开始阅读代码。
1全局变量和定义数据
struct MESHLISTDATA
{
WCHAR wszName[MAX_PATH];
WCHAR wszFile[MAX_PATH];
DWORD dwNumMat; // Number of materials. To be filled in when loading this mesh.
} g_MeshListData[] =
{
{ L"Car", L"car2.x", 0 },
{ L"Banded Earth", L"sphereband.x", 0 },
{ L"Dwarf", L"dwarf\\DwarfWithEffectInstance.x", 0 },
{ L"Virus", L"cytovirus.x", 0 },
{ L"Car", L"car2.x", 0 },
{ L"Banded Earth", L"sphereband.x", 0 },
{ L"Dwarf", L"dwarf\\DwarfWithEffectInstance.x", 0 },
};
首先看到的是一个将要载入的网格的列表,第三个参数载入时设定。
//--------------------------------------------------------------------------------------
struct CMeshMaterial
{
ID3DXEffect* m_pEffect;
D3DXHANDLE m_hParam;
IDirect3DTexture9* m_pTexture;
public:
//省略部分代码
};
接下来是一个材质类,便于管理材质。
//--------------------------------------------------------------------------------------
class CEffectMesh
{
WCHAR m_wszMeshFile[MAX_PATH];
ID3DXMesh* m_pMesh;
CMeshMaterial* m_pMaterials;
DWORD m_dwNumMaterials;
public:
//省略部分代码
void Render( IDirect3DDevice9* pd3dDevice )
{
HRESULT hr;
for( UINT i = 0; i < m_dwNumMaterials; ++i )
{
CMeshMaterial* pMat = &m_pMaterials[i];
V( pMat->m_pEffect->ApplyParameterBlock( pMat->m_hParam ) );
UINT cPasses;
V( pMat->m_pEffect->Begin( &cPasses, 0 ) );
for( UINT p = 0; p < cPasses; ++p )
{
V( pMat->m_pEffect->BeginPass( p ) );
V( m_pMesh->DrawSubset( i ) );
V( pMat->m_pEffect->EndPass() );
}
V( pMat->m_pEffect->End() );
}
}
};
接下来是一个网格类,可以看到该类带有一个材质数组,渲染函数中依次调用材质效果框架渲染材质,当然,用到了ParameterBlock。
ID3DXEffectPool* g_pEffectPool = NULL; // Effect pool for sharing parameters
CGrowableArray <CEffectMesh> g_Meshes; // List of meshes being rendered
CGrowableArray <D3DXMATRIXA16> g_amWorld; // World transform for the meshes
这是三个值得注意的全局变量,一个是效果框架池,另外两个用来渲染网格。
HRESULT CEffectMesh::Create( LPCWSTR wszFileName, IDirect3DDevice9* pd3dDevice )
{
HRESULT hr;
WCHAR str[MAX_PATH];
WCHAR wszMeshPath[MAX_PATH];
ID3DXBuffer* pAdjacency;
ID3DXBuffer* pMaterials;
ID3DXBuffer* pEffectInstance;
DWORD dwNumMaterials;
// Save the mesh filename
wcscpy_s( m_wszMeshFile, MAX_PATH, wszFileName );
//省略,载入网格
bool bHasNormals = ( m_pMesh->GetFVF() & D3DFVF_NORMAL ) != 0;
ID3DXMesh* pCloneMesh;
V_RETURN( m_pMesh->CloneMesh( m_pMesh->GetOptions(), MESHVERTEX::Decl, pd3dDevice, &pCloneMesh ) );
m_pMesh->Release();
m_pMesh = pCloneMesh;//这里将载入的网格顶点格式改为需要的格式
//省略
// Ensure the mesh has correct normals.
if( !bHasNormals )
D3DXComputeNormals( m_pMesh, ( DWORD* )pAdjacency->GetBufferPointer() );
// Allocate material array
m_pMaterials = new CMeshMaterial[dwNumMaterials];
if( !m_pMaterials )
return E_OUTOFMEMORY;
D3DXMATERIAL* pXMats = ( D3DXMATERIAL* )pMaterials->GetBufferPointer();
D3DXEFFECTINSTANCE* pEI = ( D3DXEFFECTINSTANCE* )pEffectInstance->GetBufferPointer();
for( UINT i = 0; i < dwNumMaterials; ++i )
{
// Obtain the effect
hr = S_OK;
// Try the mesh's directory
wcscpy_s( str, MAX_PATH, wszMeshPath );
MultiByteToWideChar( CP_ACP, 0, pEI[i].pEffectFilename, -1, str + lstrlenW( str ), MAX_PATH );
if( pEI[i].pEffectFilename == NULL )
hr = E_FAIL;
WCHAR wszFxName[MAX_PATH];
MultiByteToWideChar( CP_ACP, 0, pEI[i].pEffectFilename, -1, wszFxName, MAX_PATH );
if( SUCCEEDED( hr ) )
{
WCHAR szTmp[MAX_PATH];
wcscpy_s( szTmp, MAX_PATH, L"SharedFx\\" );
wcscat_s( szTmp, MAX_PATH, wszFxName );
hr = DXUTFindDXSDKMediaFileCch( str, MAX_PATH, szTmp );
if( FAILED( hr ) )
{
// Search the SDK paths
hr = DXUTFindDXSDKMediaFileCch( str, MAX_PATH, wszFxName );
}
}
if( SUCCEEDED( hr ) )
DXUTGetGlobalResourceCache().CreateEffectFromFile( pd3dDevice, str, NULL, NULL, g_dwShaderFlags,
g_pEffectPool,
&m_pMaterials[i].m_pEffect, NULL );
if( !m_pMaterials[i].m_pEffect )
{
// No valid effect for this material. Use the default.
m_pMaterials[i].m_pEffect = g_pEffect;
m_pMaterials[i].m_pEffect->AddRef();
}
// Set the technique this material should use
D3DXHANDLE hTech;
m_pMaterials[i].m_pEffect->FindNextValidTechnique( NULL, &hTech );
m_pMaterials[i].m_pEffect->SetTechnique( hTech );
// Create a parameter block to include all parameters for the effect.
m_pMaterials[i].m_pEffect->BeginParameterBlock();
for( UINT param = 0; param < pEI[i].NumDefaults; ++param )
{
D3DXHANDLE hHandle = m_pMaterials[i].m_pEffect->GetParameterByName( NULL,
pEI[i].pDefaults[param].pParamName );
D3DXPARAMETER_DESC desc;
if( hHandle != NULL )
{
m_pMaterials[i].m_pEffect->GetParameterDesc( hHandle, &desc );
if( desc.Type == D3DXPT_BOOL ||
desc.Type == D3DXPT_INT ||
desc.Type == D3DXPT_FLOAT ||
desc.Type == D3DXPT_STRING )
{
m_pMaterials[i].m_pEffect->SetValue( pEI[i].pDefaults[param].pParamName,
pEI[i].pDefaults[param].pValue,
pEI[i].pDefaults[param].NumBytes );
}
}
}
// Obtain the texture
hr = S_OK;
if( pXMats[i].pTextureFilename )
{
// Try the mesh's directory first.
wcscpy_s( str, MAX_PATH, wszMeshPath );
MultiByteToWideChar( CP_ACP, 0, pXMats[i].pTextureFilename, -1, str + lstrlenW( str ), MAX_PATH );
// If the texture file is not in the same directory as the mesh, search the SDK paths.
if( INVALID_FILE_ATTRIBUTES == ::GetFileAttributesW( str ) )
{
WCHAR wszTexName[MAX_PATH];
// Search the SDK paths
MultiByteToWideChar( CP_ACP, 0, pXMats[i].pTextureFilename, -1, wszTexName, MAX_PATH );
hr = DXUTFindDXSDKMediaFileCch( str, MAX_PATH, wszTexName );
}
if( SUCCEEDED( hr ) )
DXUTGetGlobalResourceCache().CreateTextureFromFile( pd3dDevice, str, &m_pMaterials[i].m_pTexture );
}
if( !m_pMaterials[i].m_pTexture )
{
// No texture or texture fails to load. Use the default texture.
m_pMaterials[i].m_pTexture = g_pDefaultTex;
m_pMaterials[i].m_pTexture->AddRef();
}
// Include the texture in the parameter block if the effect requires one.
D3DXHANDLE hTexture = m_pMaterials[i].m_pEffect->GetParameterByName( NULL, "g_txScene" );
if( hTexture )
m_pMaterials[i].m_pEffect->SetTexture( hTexture, m_pMaterials[i].m_pTexture );
// Include the environment map texture in the parameter block if the effect requires one.
hTexture = m_pMaterials[i].m_pEffect->GetParameterByName( NULL, "g_txEnvMap" );
if( hTexture )
m_pMaterials[i].m_pEffect->SetTexture( hTexture, g_pEnvMapTex );
// Save the parameter block
m_pMaterials[i].m_hParam = m_pMaterials[i].m_pEffect->EndParameterBlock();
}
SAFE_RELEASE( pAdjacency );
SAFE_RELEASE( pMaterials );
SAFE_RELEASE( pEffectInstance );
m_dwNumMaterials = dwNumMaterials;
return S_OK;
}
最后看看CEffectMesh的Create函数。可以看到在载入纹理时同时创建ParameterBlock。下一节将阅读剩下的代码。