DirectX11 Effect特效文件

Effect特效文件

1. 什么是Effect特效文件?

Direct3D 中,有个 Effect 文件的概念。将所创建的着色器与这些文件捆绑在一起就是所谓的一个效果(特效)。 大多数时候,你只是结合顶点和像素着色器来创建某一行为,这叫做技术(technique)。一种技术定义了一个渲染效果,而 Effect文件就是包含很多渲染技术的文件。
例如:

technique11 ColorInversion
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_5_0, VS_Main() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_5_0, PS_Main() ) );
    }
}

每个效果文件都包含一个特定的渲染功能集。 每个效果,都可以在你绘制场景中的物体时应用,来指示物体绘制的外观和怎样绘制。 例如,你可以创建一个效果专用于贴图对象,或者创建一个效果用于产生明亮的光照或模糊的光照。 对于同一个效果也可以有不同的版本, 根据机器性能不同,将某些效果用于低端机器,而另一些用于高端机器。 在使用效果时,它们有着十分广泛的用途。
当然可以在 Direct3D 11 中一直单独的使用着色器,但是你会发现当将这些单独的着色器放在一起作为一个Effect使用时,会变得十分有用。 一个效果是以一种特别的方式将所需的着色器简单的放在一起打包来渲染对象。 效果作为一个单独的对象载入,并且只包含那些必须用到的着色器。通过在你的场景中改变效果文件,你就能够很容易改变 Direct3D 的渲染方式。 效果定义在效果文件之中,以文本格式从磁盘中载入,编译和执行。
效果文件和我们之前使用的着色器文件不同之处在于,Effect文件不仅仅包含着色器,还有诸如渲染状态,混合状态等内容。

2. Effect特效文件的组成(布局)

Effect效果文件由几个不同的部分组成:

  • 外部变量(External Variables)——这些变量的数据来自于程序的调用。例如:

    Texture2D colorMap : register( t0 );
    SamplerState colorSampler : register( s0 );
    cbuffer cbChangesEveryFrame : register( b0 )
    {
        matrix worldMatrix;
    };
    cbuffer cbNeverChanges : register( b1 )
    {
        matrix viewMatrix;
    };
    cbuffer cbChangeOnResize : register( b2 )
    {
        matrix projMatrix;
    };
  • 输入结构(Input structures)——定义着色器之间传递信息的结构。例如:

    struct VS_Input
    {
        float4 pos  : POSITION;
        float2 tex0 : TEXCOORD0;
    };
    struct PS_Input
    {
        float4 pos  : SV_POSITION;
        float2 tex0 : TEXCOORD0;
    };
  • 着色器(Shaders)——着色器代码(一个效果文件可以包含多个着色器)。例如:

    PS_Input VS_Main( VS_Input vertex )
    {
        PS_Input vsOut = ( PS_Input )0;
        vsOut.pos = mul( vertex.pos, worldMatrix );
        vsOut.pos = mul( vsOut.pos, viewMatrix );
        vsOut.pos = mul( vsOut.pos, projMatrix );
        vsOut.tex0 = vertex.tex0;
        return vsOut;
    }
    float4 PS_Main( PS_Input frag ) : SV_TARGET
    {
        return 1.0f - colorMap.Sample( colorSampler, frag.tex0 );
    }
  • 技术块(Technique blocks)——效果中定义的着色器的使用过程。几何着色器,外壳和域着色器都是可选的,可以设置为NULL。例如:

    technique11 ColorInversion
    {
        pass P0
        {
            SetVertexShader( CompileShader( vs_5_0, VS_Main() ) );
            SetGeometryShader( NULL );
            SetPixelShader( CompileShader( ps_5_0, PS_Main() ) );
        }
    }

3. 如何载入Effect特效文件?

Effect 通常使用函数 D3DX11CreateEffectFromMemory 从一段缓存中载入。因为该函数载入效果是来自于一段缓存,所以你需要使用 std::ifstream 读取 Effect 文件,将文件内容传递给载入函数。 该函数原型如下:

HRESULT D3DX11CreateEffectFromMemory(
void* pData,
SIZE_T DataLength,
UINT FXFlags,
ID3D11Device* pDevice,
ID3DX11Effect** ppEffect
);

上述函数的第一个参数是 Effect 文件的数据(HLSL 的 Effect 源代码),随后的参数是所包含源代码的字节数,编译标识, Direct3D 11 设备,和将持有 Effect 对象的 ID3DX11Effect 类型指针。例如:

    ID3DBlob* buffer = 0;
    bool compileResult = CompileD3DShader( "ColorInversion.fx", 0, "fx_5_0", &buffer );
    if( compileResult == false )
    {
        DXTRACE_MSG( "Error compiling the effect shader!" );
        return false;
    }
    HRESULT d3dResult;
    d3dResult = D3DX11CreateEffectFromMemory( buffer->GetBufferPointer( ),
        buffer->GetBufferSize( ), 0, d3dDevice_, &effect_ );
    if( FAILED( d3dResult ) )
    {
        DXTRACE_MSG( "Error creating the effect shader!" );
        if( buffer )
            buffer->Release( );
        return false;
    }

3. 如何从 Effect 中创建技术对象?

载入 Effect 文件后,为了使用其中定义的技术,可以获得对其中的技术的访问。 技术存储在一个ID3DX11EffectTechnique 对象中,在后面的顶点布局的定义或渲染时使用。

    ID3DX11EffectTechnique* colorInvTechnique;
    colorInvTechnique = effect_->GetTechniqueByName( "ColorInversion" );

4. pass过程是什么?该如何使用?

因为可以创建简单或复杂的渲染技术,这些技术通过其中定义的 pass 过程来表现其效果。每个 pass 过程更新或改变渲染状态和应用于场景的着色器。 因为并不是所有的效果都想通过单一的 pass 过程来表现,所以技术块中允许我们定义多个 pass 过程。在 HLSL Effect 文件中创建每个技术需要使用关键字 pass,并且关键字后面跟随一个 pass 级别。 Pass 级别是字母P 后跟随 pass 过程的序号表示。 例如:

technique11 Render
{
    pass P0
    {
        // pass shader definitions
    }
    pass P1
    {
        // pass shader definitions
    }
}

有一些后处理(post-processing)效果,例如域的深度,就需要多个 pass 过程处理。 需要注意的是,使用多个 pass 过程将会导致物体被绘制多次,有时渲染就会变慢。
每个 pass 过程的主要工作就是设置着色器。 因为着色器可以用于不同的 pass 过程,所以必须通过函数SetVertexShader,SetGeometryShader, SetPixelShader 等,来指定具体使用的着色器。例如:

technique11 ColorInversion
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_5_0, VS_Main() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_5_0, PS_Main() ) );
    }
}

假如你现在有一个技术对象,并且当你绘制物体时准备使用它。 使用该技术要求遍历有效的 pass 过程,并且调用你的绘制函数。在使用 pass 过程中的着色器绘制之前,技术需要在硬件中进行准备来进行绘制。 函数 Apply 用于设置当前的技术以及它的所有渲染状态和数据。例子如下:

    ID3DX11EffectTechnique* colorInvTechnique;
    colorInvTechnique = effect_->GetTechniqueByName( "ColorInversion" );
    D3DX11_TECHNIQUE_DESC techDesc;
    colorInvTechnique->GetDesc( &techDesc );
    for( unsigned int p = 0; p < techDesc.Passes; p++ )
    {
        ID3DX11EffectPass* pass = colorInvTechnique->GetPassByIndex( p );
        if( pass != 0 )
        {
            pass->Apply( 0, d3dContext_ );
            d3dContext_->DrawIndexed( 36, 0, 0 );
        }
    }

5. 如何使Effect文件与C++代码交互?

当我们创建输入布局时,必须使用一个 Effect 文件中的顶点着色器来指定具体的输入布局。为了这样做,首先我们获得指向一项技术的指针, 该 technique(技术)有我们希望用于输入布局的具体的顶点着色器,而该指针运行我们访问 technique 的 passes 过程。 因为每个过程能够使用不同的顶点着色器,所以我们必须获得指向用于输入布局的 pass 过程指针。 通过该 pass 过程,我们可以调用函数 GetVertexShaderDesc 来获得用于该 pass 过程的顶点着色器的描述对象,随后调用该对象的函数 GetShaderDesc 就可以取得顶点着色器的字节码和字节大小。 通过取得的顶点着色器字节码和字节大小就可以创建输入布局。例如:

    ID3DX11EffectTechnique* colorInvTechnique;
    colorInvTechnique = effect_->GetTechniqueByName( "ColorInversion" );
    ID3DX11EffectPass* effectPass = colorInvTechnique->GetPassByIndex( 0 );
    D3DX11_PASS_SHADER_DESC passDesc;
    D3DX11_EFFECT_SHADER_DESC shaderDesc;
    effectPass->GetVertexShaderDesc( &passDesc );
    passDesc.pShaderVariable->GetShaderDesc( passDesc.ShaderIndex, &shaderDesc );
    d3dResult = d3dDevice_->CreateInputLayout( solidColorLayout, totalLayoutElements,
        shaderDesc.pBytecode, shaderDesc.BytecodeLength, &inputLayout_ );

在函数 Render中,我们可以通过使用不同的 Effect 的变量对象来设置着色器中的变量,例如ID3DX11EffectShaderResourceVariable用于着色器的资源变量, ID3DX11EffectSamplerVariable 用于采样对象,而ID3DX11EffectMatrixVariable 用于矩阵,等等。
为了获得变量的指针,我们可以调用函数 GetVariableByName(或 GetVariableByIndex)得到。可以调用后缀函数“ AsType”将变量类型转化为我们知道的类型。例如,“ AsShaderResource”将变量转为一个着色器资源,“ AsSampler”将变量转为一个采样对象,“AsMatrix”将变量转为一个矩阵对象,等等。一旦我们获得这些变量的指针,我们就可以调用变量的函数对它进行数据绑定 (例如,调用变量类型ID3DX11EffectMatrixVariable 的函数 SetMatrix 来将矩阵数据设置给它)。一旦我们设置好着色器变量,我们就可以获得用于渲染的 technique 对象指针,并且遍历 technique 的 pass 过程来绘制网格的几何图形。例如:

    ID3DX11EffectShaderResourceVariable* colorMap;
    colorMap = effect_->GetVariableByName( "colorMap" )->AsShaderResource( );
    colorMap->SetResource( colorMap_ );
    ID3DX11EffectSamplerVariable* colorMapSampler;
    colorMapSampler = effect_->GetVariableByName( "colorSampler" )->AsSampler( );
    colorMapSampler->SetSampler( 0, colorMapSampler_ );
    ID3DX11EffectMatrixVariable* worldMatrix;
    worldMatrix = effect_->GetVariableByName( "worldMatrix" )->AsMatrix( );
    worldMatrix->SetMatrix( ( float* )&worldMat );
    ID3DX11EffectMatrixVariable* viewMatrix;
    viewMatrix = effect_->GetVariableByName( "viewMatrix" )->AsMatrix( );
    viewMatrix->SetMatrix( ( float* )&viewMatrix_ );
    ID3DX11EffectMatrixVariable* projMatrix;
    projMatrix = effect_->GetVariableByName( "projMatrix" )->AsMatrix( );
    projMatrix->SetMatrix( ( float* )&projMatrix_ );

6. 示例Effect文件

接下来创建一个 effect 特效文件,目标是使用反向颜色渲染表面。 意思是原来是白色渲染,现在使用黑色渲染,而黑色变成白色,而其它颜色变成其对立面颜色。 要表现该效果十分的容易,只需在像素着色器中使用 1 减去原来的颜色,即可反转颜色。 因为 1 减去 1(白色)会变成 0(由白色变为黑色),而 1 减去 0 变为 1(由黑色变为白色)。

/* Beginning DirectX 11 Game Programming By Allen Sherrod and Wendy Jones Color Inversion Shader */


Texture2D colorMap : register( t0 );
SamplerState colorSampler : register( s0 );


cbuffer cbChangesEveryFrame : register( b0 )
{
    matrix worldMatrix;
};

cbuffer cbNeverChanges : register( b1 )
{
    matrix viewMatrix;
};

cbuffer cbChangeOnResize : register( b2 )
{
    matrix projMatrix;
};


struct VS_Input
{
    float4 pos  : POSITION;
    float2 tex0 : TEXCOORD0;
};

struct PS_Input
{
    float4 pos  : SV_POSITION;
    float2 tex0 : TEXCOORD0;
};


PS_Input VS_Main( VS_Input vertex )
{
    PS_Input vsOut = ( PS_Input )0;
    vsOut.pos = mul( vertex.pos, worldMatrix );
    vsOut.pos = mul( vsOut.pos, viewMatrix );
    vsOut.pos = mul( vsOut.pos, projMatrix );
    vsOut.tex0 = vertex.tex0;

    return vsOut;
}


float4 PS_Main( PS_Input frag ) : SV_TARGET
{
    return 1.0f - colorMap.Sample( colorSampler, frag.tex0 );
}


technique11 ColorInversion
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_5_0, VS_Main() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_5_0, PS_Main() ) );
    }
}

没有使用Effect的普通效果:

使用了Effect后的效果:

DirectX11 Effect特效文件_第1张图片

你可能感兴趣的:(C++,effect,特效,着色器,directx11)