首先Effect最大好處的就是簡單,使得編寫Shader繪制的程序工作量大大下降。如果不用Effect框架,那么一個個Vertex Shader,Geometry Shader和Pixel Shader都要一遍遍的先用D3DX11CompileFromFile得到二進制代碼,再分別調CreateVertexShader, CreateGeometryShader和CreatePixelShader等創建ID3D11VertexShader,ID3D11GeometryShader,ID3D11PixelShader等。
相比之下使用Effect框架的話,在用D3DX11CompileFromFile得到二進制代碼后,只需要再調用D3DX11CreateEffectFromMemory得到ID3D11Effect接口便可。
第二個好處是Effect框架下,Directx中的變量和HLSL中的變量綁定邏輯比較清晰(也可以說簡單)。之前的日志說過在不用Effect框架下,HLSL中的變量和Directx應用中的變量的綁定是根據變量的類型和順序等來綁定的,這種隱式的綁定相當容易出錯。而且還要考慮Global Variable,Constant Buffer等各種類型。Constant Buffer可能是不同的寄存器中,它也有着一套它自己的變量的更新規則等等。這些都相當的麻煩。而在Effect框架中,一切綁定通過GetVariableByName(),這使得邏輯非常的清晰簡單。
當然還有可能有其他的好處壞處,留着在慢慢發掘。
下面記錄下如何使用Directx的Effect框架。
在Dirext11中,Effect已經被單獨划分出來了。要使用Effect框架,我們要引用相應的靜態庫。
首先要在Directx的SDk的安裝目錄下找到Samples/C++/Effect11,編譯目錄下的工程,在相應的Samples/C++/Effect11/Debug目錄下找到Effects11這個靜態庫,拷貝這個靜態庫到自己工程的工作目錄下,最后在自己項目中的Linker->Input->Additional Dependencies中輸入Effects11.lib,表示要引用這個靜態庫。這就OK了。
然后再代碼中加入#include <d3dx11effect.h>表示引用這個頭文件。
下面從代碼上說下如何在程序中使用Effect框架的幾個基本的步驟,這里我們只是繪制幾個簡單的箱子。
先看HLSL代碼:
//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
cbuffer WorldConstantBuffer: register( b0 )
{
matrix World;
matrix View;
matrix Projection;
}
//--------------------------------------------------------------------------------------
struct VS_OUTPUT
{
float4 Pos : SV_POSITION;
float4 Color : COLOR;
};
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VS( float4 Pos : POSITION, float4 Color : COLOR )
{
VS_OUTPUT output = (VS_OUTPUT)0;
output.Pos = mul( Pos, World );
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Color = Color;
return output;
}
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( VS_OUTPUT input ) : SV_Target
{
return input.Color;
}
technique11 BasicTech
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0,VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader(ps_4_0,PS() ) );
}
}
基本和非Effect框架一樣,只是多了個
technique11 BasicTech
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0,VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader(ps_4_0,PS() ) );
}
}
Technique把pass包裝起來,pass把渲染管道里面所有需要用到的vertex,geometry,pixel包裝起來。
看完HLSL代碼,再來看看在Direct11中如何使用它們。第一步是在Direct11中先用D3DX11CompileFromFile()編譯HLSL代碼,再用D3DX11CreateEffectFromMemory生成Effect的接口。
HRESULT Box::BuildFX()
{
HRESULT hr=S_OK;
ID3DBlob *pBblob=NULL;
ID3DBlob *pErrorBlob=NULL;
DWORD shaderFlags=D3DCOMPILE_ENABLE_STRICTNESS;
hr=D3DX11CompileFromFile(L"Cube.fx",NULL,NULL,NULL,"fx_5_0",shaderFlags,0,NULL,&pBblob,&pErrorBlob,NULL);
if(FAILED(hr))
{
……
}
D3DX11CreateEffectFromMemory(pBblob->GetBufferPointer(),pBblob->GetBufferSize(),0,m_pDevice,&m_pFX);
……
return hr;
}
值得注意的是D3DX11CompileFromFile()中的"fx_5_0",在Directx11中一定要用"fx_5_0",這個是不向下兼容的,不能用"fx_4_0"及之前版本。
在得到Effect框架接口后,我們就要綁定HLSL和Directx中的相關變量
HRESULT Box::BuildFX()
{
HRESULT hr=S_OK;
ID3DBlob *pBblob=NULL;
ID3DBlob *pErrorBlob=NULL;
DWORD shaderFlags=D3DCOMPILE_ENABLE_STRICTNESS;
hr=D3DX11CompileFromFile(L"Cube.fx",NULL,NULL,NULL,"fx_5_0",shaderFlags,0,NULL,&pBblob,&pErrorBlob,NULL);
if(FAILED(hr))
{
if(pErrorBlob->GetBufferPointer())
{
char* errorMes=(char*)pErrorBlob->GetBufferPointer();
//printf(errorMes);
std::cout<<errorMes<<std::endl;
return E_FAIL;
}
}
IFR(D3DX11CreateEffectFromMemory(pBblob->GetBufferPointer(),pBblob->GetBufferSize(),0,m_pDevice,&m_pFX));
m_pTech=m_pFX->GetTechniqueByName("BasicTech");
m_pWorldMatVar=m_pFX->GetVariableByName("World")->AsMatrix();
m_pViewMatVar=m_pFX->GetVariableByName("View")->AsMatrix();
m_pProjMatVar=m_pFX->GetVariableByName("Projection")->AsMatrix();
return hr;
}
上面幾個變量的類型分別是ID3DX11Effect *m_pFX;
ID3DX11EffectTechnique *m_pTech;
ID3DX11EffectMatrixVariable *m_pWorldMatVar;
ID3DX11EffectMatrixVariable *m_pViewMatVar;
ID3DX11EffectMatrixVariable *m_pProjMatVar;
最后我們要為頂點創建頂點的格式:ID3D11InputLayout。
HRESULT Box::BuildInputLayout()
{
HRESULT hr=S_OK;
// Define the input layout
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = ARRAYSIZE( layout );
// Create the input layout
D3DX11_PASS_DESC passDesc;
m_pTech->GetPassByIndex(0)->GetDesc(&passDesc);
IFR( m_pDevice->CreateInputLayout( layout,numElements,passDesc.pIAInputSignature,passDesc.IAInputSignatureSize, &m_pInputLayout ) );
// Set the input layout
m_pContext->IASetInputLayout( m_pInputLayout );
return hr;
}
初始化的工作完成后我們就可以渲染了。先是通過SetMatrix等函數設置HLSL中相應變量的值。然后再遍歷technique中所有的pass,通過Apply調用相應pass的shader狀態。
void Box::Render(const float *glMat,D3DXMATRIX* mView,D3DXMATRIX* mProj)
{
m_pWorldMatVar->SetMatrix(glMat);
m_pViewMatVar->SetMatrix( ((float*)mView) );
m_pProjMatVar->SetMatrix( ((float*)mProj) );
D3DX11_TECHNIQUE_DESC techDesc;
m_pTech->GetDesc(&techDesc);
for(int i=0;i<techDesc.Passes;i++)
{
ID3DX11EffectPass *pass=m_pTech->GetPassByIndex(i);
pass->Apply(0,m_pContext);
m_pContext->DrawIndexed( 36, 0, 0 ); // 36 vertices needed for 12 triangles in a triangle list
}
return;
}
這次主要總結下了Effect框架的相關使用問題和主要的步驟。由於Directx11的代碼很長,就不一一貼出來了。