这篇文章将教你如何构建和使用effect。它将介绍effect一些强大有趣的细节。
.创建一个effect
下面是dx sdk例子 BasicHLSL Sample里的代码
ID3DXEffect* g_pEffect = NULL;
DWORD dwShaderFlags = 0;
//设置flag
dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
dwShaderFlags |= D3DXSHADER_NO_PRESHADER;
// Read the D3DX effect file
WCHAR str[MAX_PATH];
DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"BasicHLSL.fx" );
D3DXCreateEffectFromFile(
1 pd3dDevice,
2 str,
3 NULL, // CONST D3DXMACRO* pDefines,
4 NULL, // LPD3DXINCLUDE pInclude,
5 dwShaderFlags,
6 NULL, // EFFECT POOL
7 &g_pEffect,
8 NULL );
参数6:effect pool.如果多个effect使用指向同一内存的pool指针,那么这些effect将共用这些变量。
如果是NULL将不具有这功能。其他参数具体含义可以参考sdk。
. 渲染effect
使用effect渲染的一般步骤是:
Begin 设置当前的 techinque
BeginPass 设置当前的 pass
CommitChanges 在一个pass内改变effect状态,要在渲染代码前。
EndPass 结束pass
End 结束tuchinque
Effect渲染代码往往比相应的不用effect的代码简单,下面是一个例子:
// Apply the technique contained in the effect
g_pEffect->Begin(&cPasses, 0);
for (iPass = 0; iPass < cPasses; iPass++)
{
g_pEffect->BeginPass(iPass);
// Only call CommitChanges if any state changes have happened
// after BeginPass is called
g_pEffect->CommitChanges();
// Render the mesh with the applied technique
g_pMesh->DrawSubset(0);
g_pEffect->EndPass();
}
g_pEffect->End();
在渲染循环中可以有多个technique,一个technique中也可以有多个pass。
.effect 的语义(semantic)
Effect可以根据Semantics(语义)来找到相应的变量。一个变量最多有一个semantic,semantic以:型
式跟在变量后面:
float4x4 matWorldViewProj : WORLDVIEWPROJ;
effect接口可以这样根据semantic来得到变量句柄:
D3DHANDLE handle =
m_pEffect->GetParameterBySemantic(NULL, "WORLDVIEWPROJ");
effect也有其他的方法来查询变量。
.变量注释
我们可以对effect中的变量,pass 和 techinque加注释。
对变量很容易加上注释,而且这些信息可以读取和作他用。注释可以使任何数据类型,可以被动态添加
注释的声明便限制在<>,一个注释应包括:
1.类型
2.变量名
3.=号
4.数值
5. ;号
比如: texture Tex0 < string name = "tiger.bmp"; >;
<>内就是一个annotation,它仅仅是作为一个用户注释附加在变量后面。通过 GetAnnotation 或
GetAnnotationByName函数来得到。annotation也可以通过应用程序来添加,
但要注意以下几点:
Must be either numeric or strings.
Must always be initialized with a default value.
Can be associated with Techniques and Passes and top-level Effect Parameters.
Can be written to and read from with either ID3DXEffect or ID3DXEffectCompiler.
Can be added with ID3DXEffect.
Cannot be referenced inside the effect.
Cannot have sub-semantics or sub-annotations.
.共享Effect的变量
Effect parameters 是一些在effect中声明的非static 变量。这些包括全局变量和注释annotations。
Effect Parameters 可以在不同的effect中共用,但前提是声明这些parameters是加上"shared"关键字
,而且要相同的名字,类型和语义。effect pool 包含了共享的effect parameter。通过
D3DXCreateEffectPool 创建pool:
ID3DXEffectPool* g_pEffectPool = NULL; // Effect pool for sharing parameters
D3DXCreateEffectPool( &g_pEffectPool );
Effect中这些共享参数必须用同样的设备。当effect release 共享参数时,同时也在pool中删掉了这些
参数。如果没有必要共享参数,那么在创建effect时把pool参数设为NULL。
.编译Effect
在application中,当你调用 D3DXCreateEffect时就已经包含了对effect的编译。也可以用sdk提供的工
具进行离线编译。
fxc.exe Compiles HLSL shaders.
flink.exe Links HLSL fragments.
vsa.exe Compiles assembly vertex shaders.
psa.exe Compiles assembly pixel shaders.
这些工具在(SDK root)\Utilities\Bin\x86\目录下。
.通过预渲染来提高性能。
preshader 通过预计算常表达式来提高shader 效率。effect 编译器会自动的把它从shader主体抽出来
让cup预处理,这种情况跟把static 表达式从一个循环代码中提出来一样。preshader只在effect中起作
用。preshader能够减少每遍渲染所要的指令数和寄存器数,从而提高shader代码的执行效率。
可以把effect 编译器想象成一个两个编译器的集合:一个编译cpu类型,一个编译gpu类型。当作
preshader时,相当于把部分gpu计算让cpu来代劳。
.Parameter Blocks
parameter blocks可以保存effect的状态。
m_pEffect->SetTechnique( "RenderScene" );
m_pEffect->BeginParameterBlock();
D3DXVECTOR4 v4( Diffuse.r, Diffuse.g, Diffuse.b, Diffuse.a );
m_pEffect->SetVector( "g_vDiffuse", &v4 );
m_pEffect->SetFloat( "g_fReflectivity", fReflectivity );
m_pEffect->SetFloat( "g_fAnimSpeed", fAnimSpeed );
m_pEffect->SetFloat( "g_fSizeMul", fSize );
m_hParameters = m_pEffect->EndParameterBlock();
上面代码保存了4个parameter值,并返回一个句柄。可以调用api ApplyParameterBlock来得到保持的
状态。如下面代码:
CObj g_aObj[NUM_OBJS]; // Object instances
if( SUCCEEDED( pd3dDevice->BeginScene() ) )
{
// Set the shared parameters using the first mesh's effect.
// Render the mesh objects
for( int i = 0; i < NUM_OBJS; ++i )
{
ID3DXEffect *pEffect = g_aObj[i].m_pEffect;
// Apply the parameters
pEffect->ApplyParameterBlock( g_aObj[i].m_hParameters );
...
pEffect->Begin( &cPasses, 0 );
for( iPass = 0; iPass < cPasses; iPass++ )
{
...
}
pEffect->End();
}
pd3dDevice->EndScene();
}
以上代码直观上最大的有点是用一行代码设置N个parameters。