Atualmente, estou tentando mascarar alguns sprites. Em vez de explicá-lo em palavras, criei algumas fotos de exemplo:
A área a mascarar (em branco)
Agora, o sprite vermelho que precisa ser cortado.
O resultado final.
Agora, eu sei que no XNA você pode fazer duas coisas para fazer isso:
- Use o buffer de estêncil.
- Use um Pixel Shader.
Eu tentei fazer um pixel shader, que basicamente fez isso:
float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{
float4 tex = tex2D(BaseTexture, texCoord);
float4 bitMask = tex2D(MaskTexture, texCoord);
if (bitMask.a > 0)
{
return float4(tex.r, tex.g, tex.b, tex.a);
}
else
{
return float4(0, 0, 0, 0);
}
}
Isso parece cortar as imagens (embora não seja correto quando a imagem começa a se mover), mas meu problema é que as imagens estão em movimento constante (não são estáticas), portanto esse corte precisa ser dinâmico.
Existe uma maneira de alterar o código do sombreador para levar em consideração sua posição?
Como alternativa, eu li sobre o uso do Stencil Buffer, mas a maioria das amostras parece depender de um rendertarget, o que realmente não quero fazer. (Eu já estou usando 3 ou 4 para o resto do jogo, e adicionar outro em cima dele parece exagero)
O único tutorial que descobri que não usa os Rendertargets é o do blog de Shawn Hargreaves aqui. O problema com esse, no entanto, é que é para o XNA 3.1 e não parece ser bem traduzido para o XNA 4.0.
Parece-me que o pixel shader é o caminho a seguir, mas não tenho certeza de como obter o posicionamento correto. Acredito que precisaria alterar minhas coordenadas na tela (algo como 500, 500) para ficar entre 0 e 1 para as coordenadas do sombreador. Meu único problema é tentar descobrir como usar corretamente as coordenadas transformadas.
Agradecemos antecipadamente por qualquer ajuda!
Respostas:
Você pode usar sua abordagem de pixel shader, mas ela está incompleta.
Você também precisará adicionar alguns parâmetros que informam ao pixel shader onde você deseja que seu estêncil esteja localizado.
No seu código C #, você passa variáveis para o pixel shader antes da
SpriteBatch.Draw
chamada, da seguinte maneira:fonte
maskCoord
cálculo, um dos X deveria ser um Y. Editei minha resposta inicial com a correção e incluí as configurações da braçadeira UV que você precisará para o seuMaskTexture sampler
.O primeiro passo é dizer à placa gráfica que precisamos do buffer de estêncil. Para fazer isso quando você cria GraphicsDeviceManager, definimos PreferredDepthStencilFormat como DepthFormat.Depth24Stencil8, para que haja realmente um estêncil no qual gravar.
O AlphaTestEffect é usado para definir o sistema de coordenadas e filtrar os pixels com alfa que passa no teste alfa. Não vamos definir nenhum filtro e definir o sistema de coordenadas para a porta de visualização.
Em seguida, precisamos configurar dois DepthStencilStates. Esses estados determinam quando o SpriteBatch é renderizado no estêncil e quando o SpriteBatch é renderizado no BackBuffer. Estamos interessados principalmente em duas variáveis StencilFunction e StencilPass.
Para o primeiro DepthStencilState, definimos StencilFunction como CompareFunction. Isso faz com que o StencilTest tenha êxito e quando o StencilTest, o SpriteBatch, processa esse pixel. StencilPass está definido como StencilOperation. Substitua o significado de que, quando o StencilTest for bem-sucedido, o pixel será gravado no StencilBuffer pelo valor do ReferenceStencil.
Em resumo, o StencilTest sempre passa, a imagem é desenhada para a tela normalmente e, para os pixels desenhados na tela, um valor de 1 é armazenado no StencilBuffer.
O segundo DepthStencilState é um pouco mais complicado. Desta vez, queremos desenhar apenas na tela quando o valor no StencilBuffer for. Para isso, definimos StencilFunction como CompareFunction.LessEqual e ReferenceStencil como 1. Isso significa que quando o valor no buffer de estêncil for 1, o StencilTest será bem-sucedido. Definindo StencilPass como StencilOperation. Manter faz com que o StencilBuffer não atualize. Isso nos permite desenhar várias vezes usando a mesma máscara.
Em resumo, o StencilTest só passa quando o StencilBuffer é menor que 1 (os pixels alfa da máscara) e não afeta o StencilBuffer.
Agora que temos nossos DepthStencilStates configurados. Podemos realmente desenhar usando uma máscara. Simplesmente desenhe a máscara usando o primeiro DepthStencilState. Isso afetará o BackBuffer e o StencilBuffer. Agora que o buffer de estêncil tem um valor de 0 onde você mascara tinha transparência e 1 onde continha cor, podemos usar StencilBuffer para mascarar imagens posteriores.
O segundo SpriteBatch usa o segundo DepthStencilStates. Não importa o que você desenhe, apenas os pixels em que o StencilBuffer está definido como 1 passarão no teste de estêncil e serão atraídos para a tela.
Abaixo está a totalidade do código no método Draw, não se esqueça de definir PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8 no construtor do jogo.
fonte
Na resposta acima , algumas coisas estranhas aconteceram quando eu fiz exatamente o que é explicado.
A única coisa que eu precisava era dos dois
DepthStencilStates
.E os empates sem o
AlphaTestEffect
.E tudo estava bem como o esperado.
fonte