L’anticrénelage FXAA – Shader de Post-Traitement

image.png

Intro:

Une image numérique est composée de pixels. Lorsqu’elle est redimensionnée, le bord des formes ayant un angle particulier prend la forme d’escalier : c’est le crénelage, ou aliasing.

Explication :

Voici une technique très efficace et très performante en ressource système afin d’effectuer du rendu anti-crénelage. Elle utilise une render target et un Quad de post-traitement.

Voici le fichier ShaderTechique_Declarations.h :

//------------------------------------------------------
// FXAA shader
//------------------------------------------------------
class ShaderTechnique_FXAA : public ShaderTechnique
{
public:
    ShaderTechnique_FXAA();
    virtual ~ShaderTechnique_FXAA();

    virtual bool Initialize() override;

    virtual void SetupShaderVariables();

    virtual void Update(float fTimeSinceLastFrame);

    void SetRenderTarget(RenderTarget* pRT);

    void SetEnabled(bool bEnabled);
    bool GetEnabled();

private:
    D3DXMATRIX m_WorldMatrix;
    D3DXMATRIX m_ViewMatrix;
    D3DXMATRIX m_OrthoMatrix;

    bool m_bEnabled;
};

 

Voici le fichier ShaderTechnique_FXAA.cpp :

#include "ShaderTechnique_Declarations.h"
#include "Defines.h"
#include "D3D10Renderer.h"

ShaderTechnique_FXAA::ShaderTechnique_FXAA() :
ShaderTechnique("FXAA.fx", "Render", VertexLayoutType::PT_VERTEX),
m_bEnabled(true)
{
}

ShaderTechnique_FXAA::~ShaderTechnique_FXAA()
{
}

bool ShaderTechnique_FXAA::Initialize()
{
    if (ShaderTechnique::Initialize() == false)
    {
        return false;
    }

    SetupShaderVariables();

    return true;
}

void ShaderTechnique_FXAA::SetRenderTarget(RenderTarget* pRT)
{
    SetTextureRV("TextureDiffuse", pRT->GetShaderResourceView());
}

void ShaderTechnique_FXAA::SetupShaderVariables()
{
    RegisterMatrixVariable("World", ShaderVariableType::WORLD);
    RegisterMatrixVariable("View", ShaderVariableType::VIEW);
    RegisterMatrixVariable("Projection", ShaderVariableType::PROJECTION);
    RegisterTextureVariable("TextureDiffuse", ShaderVariableType::ANY);
    RegisterVectorVariable("FrameBufferSize", ShaderVariableType::ANY);
    RegisterVectorVariable("Color", ShaderVariableType::ANY);
    RegisterScalarVariable("Enabled", ShaderVariableType::ANY);

    D3DXMatrixIdentity(&m_WorldMatrix);
    D3DXMatrixIdentity(&m_ViewMatrix);
    D3DXMatrixIdentity(&m_OrthoMatrix);

    D3DXVECTOR3 Eye(0.0f, 0.0f, 1.0f);
    D3DXVECTOR3 At(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f);

    D3DXMatrixLookAtLH(&m_ViewMatrix, &Eye, &At, &Up);

    float fViewportWidth = (float)D3D10_RENDERER->GetViewportWidth();
    float fViewportHeigth = (float)D3D10_RENDERER->GetViewportHeight();

    D3DXMatrixOrthoLH(&m_OrthoMatrix, fViewportWidth, fViewportHeigth, 0.01f, 20.0f);

    SetVector("FrameBufferSize", D3DXVECTOR2(fViewportWidth, fViewportHeigth));
}

void ShaderTechnique_FXAA::Update(float fTimeSinceLastFrame)
{
    SetMatrix("World", &m_WorldMatrix);
    SetMatrix("View", &m_ViewMatrix);
    SetMatrix("Projection", &m_OrthoMatrix);

    SetAutoMatrix(ShaderVariableType::ANY);
}

void ShaderTechnique_FXAA::SetEnabled(bool bEnabled)
{
    SetScalar("Enabled", bEnabled);
    m_bEnabled = bEnabled;
}

bool ShaderTechnique_FXAA::GetEnabled()
{
    return m_bEnabled;
}

 

Voici les bouts de code pour initialiser le shader FXAA :


bool D3D10Renderer::Initialize(bool bFullscreen)
{
    [...]
    m_pQuad = new TextureScreenQuad();
    m_pQuad->Initialize();
    m_pQuad->SetShaderTechnique( SHADER_MANAGER->GetShader("FXAA") );
    [...]
}

void D3D10Renderer::Render()
{
    [...]
    // Efface la surface de rendu
    m_pd3dDevice->ClearDepthStencilView(m_pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0 );
    m_pd3dDevice->ClearRenderTargetView(m_pRenderTargetView, afClearColor);

    SCENE_MANAGER->DrawAll(fTimeSinceLastFrame);

    DrawTextInfo();

    RenderToTexture(fTimeSinceLastFrame);

    UpdateWindowTitle();

    m_pQuad->OnRender(fTimeSinceLastFrame);

    m_pSwapChain->Present(0, 0);
    [...]
}

void D3D10Renderer::RenderToTexture(float fTimeSinceLastFrame)
{
    // Set the render target to be the render to texture.
    m_pQuad->GetRT()->SetRenderTarget(m_pDepthStencilView);

    // Clear the render to texture.
    m_pQuad->GetRT()->ClearRenderTarget(m_pDepthStencilView);

    // Render the scene now and it will draw to the render to texture instead of the back buffer.
    SCENE_MANAGER->DrawAll(fTimeSinceLastFrame);

    // Reset the render target back to the original back buffer and not the render to texture anymore.
    m_pd3dDevice->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);
}

 

Voici le fichier shader .fx :

SamplerState SamplerLinear
{
    Filter = ANISOTROPIC;
    AddressU = Wrap;
    AddressV = Wrap;
    MaxAnisotropy = 1;
};

matrix World;
matrix View;
matrix Projection;

Texture2D TextureDiffuse;

float2 FrameBufferSize;

float4 Color;

bool Enabled;

struct VS_INPUT
{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD;
};
 
struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD0;
};

PS_INPUT VS(VS_INPUT input)
{
    PS_INPUT output = (PS_INPUT)0;
     
    output.Pos = mul(input.Pos, World);
    output.Pos = mul(output.Pos, View);
    output.Pos = mul(output.Pos, Projection);
    output.Tex = input.Tex;
    
    return output;
}    

static const float FXAA_SPAN_MAX = 4.0;
static const float FXAA_REDUCE_MUL = 1.0/8.0;
static const float FXAA_REDUCE_MIN = 1.0/128.0;
    
float4 PS(PS_INPUT input) : SV_Target
{
    input.Tex = 1.0f - input.Tex;
    
    float3 rgbNW = TextureDiffuse.Sample(SamplerLinear, input.Tex + (float2(-1.0,-1.0) / FrameBufferSize)).xyz;
    float3 rgbNE = TextureDiffuse.Sample(SamplerLinear, input.Tex + (float2(1.0,-1.0) / FrameBufferSize)).xyz;
    float3 rgbSW = TextureDiffuse.Sample(SamplerLinear, input.Tex + (float2(-1.0,1.0) / FrameBufferSize)).xyz;
    float3 rgbSE = TextureDiffuse.Sample(SamplerLinear, input.Tex + (float2(1.0,1.0) / FrameBufferSize)).xyz;
    float3 rgbM  = TextureDiffuse.Sample(SamplerLinear, input.Tex).xyz;
    
    float3 luma = float3(0.299, 0.587, 0.114);
    
    float lumaNW = dot(rgbNW, luma);
    float lumaNE = dot(rgbNE, luma);
    float lumaSW = dot(rgbSW, luma);
    float lumaSE = dot(rgbSE, luma);
    float lumaM  = dot(rgbM, luma);
    
    float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
    float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
    
    float2 dir;
    dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
    dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));
    
    float dirReduce = max(
          (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL),
           FXAA_REDUCE_MIN);
      
    float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
    
    dir = min(float2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),
              max(float2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
              dir * rcpDirMin)) / FrameBufferSize;
            
    float3 rgbA = (1.0/2.0) * (
            TextureDiffuse.Sample(SamplerLinear, input.Tex + dir * (1.0/3.0 - 0.5)).xyz +
            TextureDiffuse.Sample(SamplerLinear, input.Tex + dir * (2.0/3.0 - 0.5)).xyz);
            
    float3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (
            TextureDiffuse.Sample(SamplerLinear, input.Tex + dir * (0.0/3.0 - 0.5)).xyz +
            TextureDiffuse.Sample(SamplerLinear, input.Tex + dir * (3.0/3.0 - 0.5)).xyz);
            
    float lumaB = dot(rgbB, luma);

    if (Enabled)
    {
        if ((lumaB < lumaMin) || (lumaB > lumaMax))
        {
            return float4(rgbA, 1.0f) * Color;
        }
        else
        {
            return float4(rgbB, 1.0f) * Color;
        }        
    }
    else
    {
        return TextureDiffuse.Sample(SamplerLinear, input.Tex);
    }
}

technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

Résumé :

Nous avons présenté une technique FXAA par un fichier shader .fx afin d’effectuer le rendu d’anti-crénélage.

Références :

–  https://fr.wikipedia.org/wiki/Fast_approximate_anti-aliasing

– http://horde3d.org/wiki/index.php5?title=Shading_Technique_-_FXAA

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *