如何使用effect
这篇文章将教你如何构建和使用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。