概述
Effect Framework 是一个工具代码的集合, 他提供了一个框架用于组织 shader 和渲染状态, 并由他们共同实现某种效果.
在 DX11 中使用 Effect 需要包含 d3dx11Effect.h 头文件, 并链接 D3DX11Effects.lib 和 D3DX11EffectsD.lib 库. 头文件在 DirectX SDK\Samples\C++\Effects11\Inc 目录下, 两个需要使用的库需要构建 Effects11 自行生成.
Effect Files
.fx 文件和 .cpp 文件 .h 文件一样, 也是普通的文本文件. 他存储着之前我们见到的 shader 程序.
在 fx 文件中多出来的是 technique 和 pass. 一个 effect 至少包含一个 technique, 而一个 technique 至少包含一个pass. 每个 pass 至少包含一个 VS, [GS, tessellation 可选], 一个 PS ( 可选但是一般都会有 ), [渲染状态 可选]. 每个 technique 至少有一个 pass 用来实现特定的渲染效果.
下面是一个完整的 Effect Files例子.
cbuffer cbPerObject
{
float4x4 gWorldViewProj;
};
struct VertexIn
{
float3 Pos : POSITION;
float4 Color : COLOR;
};
struct VertexOut
{
float4 PosH : SV_POSITION;
float4 Color : COLOR;
};
VertexOut VS(VertexIn vin)
{
VertexOut vout;
// Transform to homogeneous clip space.
vout.PosH = mul(float4(vin.Pos, 1.0f), gWorldViewProj);
// Just pass vertex color into the pixel shader.
vout.Color = vin.Color;
return vout;
}
float4 PS(VertexOut pin) : SV_Target
{
return pin.Color;
}
technique11 ColorTech
{
pass P0
{
SetVertexShader( CompileShader( vs_5_0, VS() ) );
SetPixelShader( CompileShader( ps_5_0, PS() ) );
}
}
对于渲染状态, 我们也可以在 fx 文件中直接设置, 当有一些特别需求的时候这个特性是方便的, 但是当状态很多时我们还是应该在应用层面上进行管理, 这样才便于对复杂的渲染状态进行切换.
下面是一个直接设置 Rasterizer State 的示例.
RasterizerState WireframeRS
{
FillMode = Wireframe;
CullMode = Back;
FrontCounterClockwise = false;
// Default values used for any properties we do not set.
};
technique11 ColorTech
{
pass P0
{
SetVertexShader( CompileShader( vs_5_0, VS() ) );
SetPixelShader( CompileShader( ps_5_0, PS() ) );
SetRasterizerState(WireframeRS);
}
}
Compiling Shaders
实现某种效果的第一步便是将之前 .fx 文件中存储的 shader 程序进行编译. 我们需要用到 DX11 中的 D3DX11CompileFromFile 方法.
HRESULT D3DX11CompileFromFile(
LPCTSTR pSrcFile,
CONST D3D10_SHADER_MACRO *pDefines,
LPD3D10INCLUDE pInclude,
LPCSTR pFunctionName,
LPCSTR pProfile,
UINT Flags1,
UINT Flags2,
ID3DX11ThreadPump *pPump,
ID3D10Blob **ppShader,
ID3D10Blob **ppErrorMsgs,
HRESULT *pHResult);
- pSrcFile: .fx 文件的名字.
- pFunctionName: 这是 shader 的入口函数名, 但是这只在单独编译 shader 时有用, 在使用 Effect 框架的时候传空就可以了, 这个入口函数已经在 technique 的 pass 中声明好了.
- pProfile: 指明 shader 的版本.
- Flags1: 表示我们将如何编译 shader, 他有若干个有效值, 我们会用到两个, D3D10_SHADER_DEBUG 和 D3D10_SHADER_SKIP_OPTIMIZATION ( 只在 debug 时有效 )
- ppShader: 函数返回的一个 ID3D10Blob 的指针, 他存储着编译出来的 shader.
- ppErrorMsgs: 函数返回的一个 ID3D10Blob 的指针, 他存储着编译时的报错信息.
- 其他的属性都是跟高级的用法, 我们在学习过程中不需要使用, 设置为默认值即可.
其中 ID3D10Blob 表示的其实就是一个内存块, 他只有 GetBufferPointer 和 GetBufferSize 两个方法用来操作这块内存.
创建 Effect
在编译完 shader 之后我们需要使用 D3DX11CreateEffectFromMemory 方法来创建 effect.
HRESULT D3DX11CreateEffectFromMemory(
void *pData,
SIZE_T DataLength,
UINT FXFlags,
ID3D11Device *pDevice,
ID3DX11Effect **ppEffect);
- pData: .fx 文件的内容.
- DataLength: .fx 文件的长度.
- FXFlags: 与之前编译 shader 时的 Flags2 对应.
- pDevice: 渲染设备指针.
- ppEffect: 返回的 Effect 指针.
示例
DWORD shaderFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
shaderFlags |= D3D10_SHADER_DEBUG;
shaderFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
#endif
ID3D10Blob* compiledShader = 0;
ID3D10Blob* compilationMsgs = 0;
HRESULT hr = D3DX11CompileFromFile(L"color.fx", 0,
0, 0, "fx_5_0", shaderFlags,
0, 0, &compiledShader, &compilationMsgs, 0);
// compilationMsgs can store errors or warnings.
if(compilationMsgs != 0)
{
MessageBoxA(0, (char*)compilationMsgs->GetBufferPointer(), 0, 0);
ReleaseCOM(compilationMsgs);
}
// Even if there are no compilationMsgs,
// check to make sure there were no other errors.
if(FAILED(hr))
{
DXTrace(__FILE__, (DWORD)__LINE__, hr,
L"D3DX11CompileFromFile", true);
}
ID3DX11Effect* mFX;
HR(D3DX11CreateEffectFromMemory(
compiledShader->GetBufferPointer(),
compiledShader->GetBufferSize(),
0, md3dDevice, &mFX));
// Done with compiled shader.
ReleaseCOM(compiledShader);