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

