Olá pessoal, como estamos?
O TDC 2011 Floripa foi incrível. Foi muito bacana encontrar tantas pessoas legais, com tanto expertise em diferentes tecnologias. Entranto, evento passou e o blog voltou
.
Hoje, vou mostrar como executar um processo chamado Post Processing. Para isso, vou usar um bocado de HLSL.
Considere dar uma olhada nos posts anteriores dessa série. Se desejar, baixe o código-fonte em https://github.com/ElemarJR/VamosAprenderXNA
O que é Post Processing?
Post Processing é uma técnica que permite adicionar bons efeitos visuais aos nossos games. Isso ocorre através do tratamento da imagem resultante do processo de renderização 3D. Ou seja, primeiro criamos a imagem correspondente ao quadro. Depois, aplicamos um “filtro” a imagem gerada chegando ao resultado que desejamos apresentar.
Nosso ponto de partida
Para o post de hoje, usaremos como ponto de partida o código produzido na parte 13.
Como fazer Post Processing?!
O processo para aplicarmos post processing implica no cumprimento das seguintes etapas:
- Criar um atributo “RenderTarget2D” onde será “gerada a textura”;
- Iniciar o objeto "RenderTarget2D” dentro do método LoadContent;
- Dentro do método Draw:
- Alterar, no objeto Device, o destino da renderização da tela (back buffer) para o objeto Render Target;
- Setar o background (cor de fundo) da textura;
- Desenhar todos os objetos normalmente;
- Alterar, no objeto Device, o destino da renderização para a tela (back buffer);
- Setar o background (cor de fundo) da tela;
- Configurar o objeto SpriteBatch para aplicação do efeito de PostProcessing;
- Pintar a textura na tela;
Vejamos agora, como fazer isso na prática.
Desenhando em uma textura
Como indicado, para podermos melhorar as imagens geradas em nosso game através de Post Processing precisamos, antes, direcionar a renderização para uma textura (imagem). Em XNA, fazemos isso através da criação de um objeto RenderTarget2D. Repare:
RenderTarget2D target;
protected override void LoadContent()
{
var device = graphics.GraphicsDevice;
target = new RenderTarget2D(device,
device.Viewport.Width,
device.Viewport.Height,
false,
SurfaceFormat.Color,
DepthFormat.Depth24);
// ... restante do código não é significativo aqui
}
Como pode ver, o objeto RenderTarget2D é inicializado no método LoadContent. Repare que utilizei largura e altura da viewport como indicação para largura e altura do objeto RenderTarget.
Agora, vamos dar uma olhadinha no método Draw:
protected override void Draw(GameTime gameTime)
{
// direcionando a renderização para uma textura
GraphicsDevice.SetRenderTarget(target);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
// desenhando a cena normalmente
GraphicsDevice.Clear(Color.CornflowerBlue);
foreach (IDrawableModel model in models)
model.Draw(camera.View, camera.Projection);
// direcionando a renderização para a tela
GraphicsDevice.SetRenderTarget(null);
// desenhando a textura gerada acima
GraphicsDevice.Clear(Color.White);
spriteBatch.Begin();
spriteBatch.Draw(target, Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
Optei por comentar o código indicando o que etá ocorrendo em cada parte do método. Ficou claro?
A técnica para pintar texturas é a mesma que indiquei na Parte 2.
Escrevendo um efeito para Post Processing
Para começar, vamos escrever um efeito que “não faz nada”. Ou seja, vamos escrever um efeito que não altera a textura que é renderizada (arquivo None.fx).
sampler TextureSampler;
struct PixelInput
{
float2 TexCoord : TEXCOORD0;
};
float4 PixelShaderFunction(PixelInput input) : COLOR
{
float4 color = tex2D(TextureSampler, input.TexCoord);
return color;
}
technique Technique1
{
pass Pass1
{
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
Este efeito é muito simples. Considere:
- criamos um sampler que será “setado” pelo objeto SpriteBatch (que faz os desenhos 2D);
- dispensamos um vertex shader pois estamos desenhando uma textura simples (passada pelo Sprite Batch). Não há coordenadas;
- criamos um PixelShader que obtém o pixel correspondente na textura;
Na prática, não mudamos nada (ainda);
Utilizando o Effect no Game
Para poder utilizar o Effect no Game, precisamos:
- criar um atributo para armazenar o efeito;
- carregar o efeito;
- modificar o método Draw para considerar o efeito.
Comecemos pela carga do efeito:
Effect postProcessingEffect;
protected override void LoadContent()
{
postProcessingEffect = Content.Load<Effect>(@"PostProcessing\None");
// ...
}
Agora, vamos ver a alteração do Draw:
protected override void Draw(GameTime gameTime)
{
// direcionando a renderização para uma textura
GraphicsDevice.SetRenderTarget(target);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
// desenhando a cena normalmente
GraphicsDevice.Clear(Color.CornflowerBlue);
foreach (IDrawableModel model in models)
model.Draw(camera.View, camera.Projection);
// direcionando a renderização para a tela
GraphicsDevice.SetRenderTarget(null);
// desenhando a textura gerada acima
GraphicsDevice.Clear(Color.White);
spriteBatch.Begin(SpriteSortMode.Immediate,
BlendState.Opaque,
null, null, null,
postProcessingEffect)
;
spriteBatch.Draw(target, Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
Como podemos perceber, a única modificação ocorreu na chamada do método Begin. Executando …
Como esperado, nada mudou .. Para isso, precisamos de um efeito que faça alguma coisa.
Night Effect
Vamos começar dando um aspecto de noite para nosso jogo.
O que eu fiz para criar esse aspecto foi descontar muito os elementos ver vermelho e verde de cada pixel e reforçar bastante o azul (Night.fx). Observe:
float4 PixelShaderFunction(PixelInput input) : COLOR
{
float4 color = tex2D(TextureSampler, input.TexCoord);
color.b *= 1.25;
color.rg *= 0.25;
return color;
}
Negative Effect
Para obter uma imagem “negativa”..
Simplesmente … (Negative.fx)
float4 PixelShaderFunction(PixelInput input) : COLOR
{
return 1 - tex2D(TextureSampler, input.TexCoord);
}
Sharpen Effect
float4 PixelShaderFunction(PixelInput input) : COLOR
{
float4 color = tex2D(TextureSampler, input.TexCoord);
color += tex2D(TextureSampler, input.TexCoord - 0.0001) * 15.0f;
color -= tex2D(TextureSampler, input.TexCoord + 0.0001) * 15.0f;
return color;
}
Emboss Effect
(Emboss.fx)
float4 PixelShaderFunction(PixelInput input) : COLOR
{
float4 color = tex2D(TextureSampler, input.TexCoord);
color.rgb = 0.5f;
color.a = 1;
color += tex2D(TextureSampler, input.TexCoord - 0.0001) * 15.0f;
color -= tex2D(TextureSampler, input.TexCoord + 0.0001) * 15.0f;
color = (color.r + color.g + color.b) / 3.0f;
return color;
}
GrayScale Effect
(GrayScale.fx)
float4 PixelShaderFunction(PixelInput input) : COLOR
{
float4 color = tex2D(TextureSampler, input.TexCoord);
color.rgb = dot(color.rgb, float3(0.3, 0.59, 0.11));
return color;
}
Em metade da cena?!
Chalk Effect (giz)
(Chalk.fx)
Wavy Effect
(wavy.fx)
float4 PixelShaderFunction(PixelInput input) : COLOR
{
float y = input.TexCoord.y;
float x = input.TexCoord.x;
y = y + (sin(x * 200) * 0.01);
float4 color = tex2D(TextureSampler, float2(x,y));
return color;
}
Black and White Effect
(bw.fx)
float4 PixelShaderFunction(PixelInput input) : COLOR
{
float4 color = tex2D(TextureSampler, input.TexCoord);
color = (color.r + color.g + color.b) / 3.0f;
if (color.r < 0.2 || color.r > 0.8)
color.rgb = 0.0f;
else
color.rgb = 1.0f;
return color;
}
Por hoje, era isso!
![]()






alinebossi
23/08/2011
Olá, sou aluna do 6º Semestre do Curso de Jogos digitais da Fatec Americana, estamos planejando uma semana de tecnologia voltada para nossa área, gostaria de saber se você tem algum interesse em ministrar algum mini-curso ou palestra sobre XNA.
Qualquer coisa, entre em contato.
Muito obrigada
Aline
Jhon Petter Marques
03/09/2011
Ola amigo,ta sumindo do xna, rs percebi que andas meio ocupado.
to aguardando atualizações