effects框架是一组工具代码,用来组织着色器程序和渲染状态,使它们共同协作实现一个具体的渲染效果。比如,你可能有不同的特效用于渲染水,云,金属物体和动态的角色。每一个effect都会包含至少一个顶点着色器,一个像素着色器和渲染阶段。
在11之前的版本中,effect属于D3D库,但在11中,它被分离了出来。源码在DirectX SDK\Samples\C++ \Effects11,要用的话自个儿编译一下就完事。
1.Effect(特效)文件
除了着色器和常量缓冲区之外,特效也包含了至少一个technique,而technique至少要包含一个通道(pass)。
1.technique11:一个technique由一个或者多个用于创建一个特定渲染technique的通道组成。对于每一个通道,几何体以不同的方式渲染,将每一个通道的结果通过某种方式结合起来以得到期望的结果。例如,一个地形渲染technique可能使用一个多通道的纹理technique。注意多通道technique开销一般很高,因为每个通道都会重新绘制几何体。然而,在某些必要的时候,还是要用到多通道technique。
2.通道(pass):一个通道由一个顶点着色器,一个可选的几何着色器,一个可选的曲面细分相关着色器,一个像素着色器和一个渲染状态组成。这些原件指示如何在当前通道中对几何体进行处理和着色。注意像素着色器也是可选的(这种情况很少见)。例如我们只想渲染深度缓冲,但不渲染后台缓冲,在这种情况下就不需要像素着色器。
technique也可以通过effect group来组织起来。如果你没有显示地定义一个,编译器会创建一个匿名的并容纳特效文件中所有的technique。
以下是一个effect文件的完整示例:
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() ) );
}
}
点和矢量的坐标规定和许多不同的空间相关(本地空间,世界空间,观察空间,投影空间)。当阅读代码时,很难搞清楚当前的坐标对应哪个空间。因此,我们经常用到以下后缀来说明空间,看下例:
float3 iPosL; // local space
float3 gEyePosW; // world space
float3 normalV; // view space
float4 posH; // homogeneous clip space
我们之前提到了通道由渲染状态组成。也就是说,状态块可以直接在特效文件里面创建。当特效需要特定的渲染状态来工作时,这一点很方便。相比之下,有些特效可能在不同的渲染状态设置下工作,在这种情况下,为了便于状态转换,我们更倾向于在应用层设置状态。以下代码展示了如何在特效文件中创建并设置一个光栅化状态块。
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);
}
}
语法和c++很像。。。
2.编译着色器
创建effect的第一步是编译定义在.fx文件里的着色器文件。可以用以下D3DX函数来完成这个过程。
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);
1.pFileName:我们想要编译的.fx文件的文件名。
2.pDefines:高级选项,暂时没用上,具体看文档。
3.pInclude:高级选项,暂时没用上,具体看文档。
4.pFunctionName:着色器函数名称入口点。仅在独立编译着色器程序的时候用到。当使用effect框架时,指定为空,因为此时着色器入口点由effect文件里定义的technique通道指定。
5.pProfile:字符串,指定使用着色器的版本。对于D3D11的特效,我们使用5.0版本的(“fx_5_0”)。