Actualmente estoy tratando de enmascarar algunos sprites. En lugar de explicarlo en palabras, he inventado algunas imágenes de ejemplo:
El área a enmascarar (en blanco)
Ahora, el sprite rojo que necesita ser recortado.
El resultado final.
Ahora, sé que en XNA puedes hacer dos cosas para lograr esto:
- Usa el tampón de plantilla.
- Utiliza un sombreador de píxeles.
Intenté hacer un sombreador de píxeles, que esencialmente hizo esto:
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);
}
}
Esto parece recortar las imágenes (aunque no es correcto una vez que la imagen comienza a moverse), pero mi problema es que las imágenes se mueven constantemente (no son estáticas), por lo que este recorte debe ser dinámico.
¿Hay alguna forma de alterar el código del sombreador para tener en cuenta su posición?
Alternativamente, he leído sobre el uso del Stencil Buffer, pero la mayoría de las muestras parecen depender del uso de un rendertarget, lo que realmente no quiero hacer. (Ya estoy usando 3 o 4 para el resto del juego, y agregar otro encima parece excesivo)
El único tutorial que he encontrado que no usa Rendertargets es uno del blog de Shawn Hargreaves aquí. Sin embargo, el problema con eso es que es para XNA 3.1 y no parece traducirse bien a XNA 4.0.
Me parece que el sombreador de píxeles es el camino a seguir, pero no estoy seguro de cómo obtener el posicionamiento correcto. Creo que tendría que cambiar mis coordenadas en pantalla (algo así como 500, 500) para estar entre 0 y 1 para las coordenadas del sombreador. Mi único problema es tratar de averiguar cómo usar correctamente las coordenadas transformadas.
¡Gracias de antemano por cualquier ayuda!
Respuestas:
Puede usar su enfoque de sombreador de píxeles, pero está incompleto.
También deberá agregarle algunos parámetros que informan al sombreador de píxeles donde desea ubicar su plantilla.
En su código C #, pasa variables al sombreador de píxeles antes de la
SpriteBatch.Draw
llamada de esta manera:fuente
maskCoord
cálculo, una de las X debería haber sido una Y. Edité mi respuesta inicial con la corrección e incluí la configuración de la abrazadera UV que necesitará para suMaskTexture sampler
.El primer paso es decirle a la tarjeta gráfica que necesitamos el búfer de la plantilla. Para hacer esto cuando crea GraphicsDeviceManager, configuramos PreferredDepthStencilFormat en DepthFormat.Depth24Stencil8 para que realmente haya una plantilla para escribir.
AlphaTestEffect se utiliza para establecer el sistema de coordenadas y filtrar los píxeles con alfa que pasan la prueba alfa. No vamos a establecer ningún filtro y establecer el sistema de coordenadas en el puerto de vista.
A continuación, debemos configurar dos DepthStencilStates. Estos estados dictan cuándo SpriteBatch se procesa en la plantilla y cuándo SpriteBatch se procesa en BackBuffer. Estamos principalmente interesados en dos variables StencilFunction y StencilPass.
Para el primer DepthStencilState configuramos StencilFunction en CompareFunction. Esto hace que el StencilTest tenga éxito y cuando el StencilTest el SpriteBatch procesa ese píxel. StencilPass está establecido en StencilOperation. Reemplazar lo que significa que cuando el StencilTest tenga éxito, ese píxel se escribirá en el StencilBuffer con el valor del ReferenceStencil.
En resumen, el StencilTest siempre pasa, la imagen se dibuja en la pantalla normalmente y para los píxeles dibujados en la pantalla se almacena un valor de 1 en el StencilBuffer.
El segundo DepthStencilState es un poco más complicado. Esta vez solo queremos dibujar en la pantalla cuando el valor en StencilBuffer es. Para lograr esto, configuramos StencilFunction en CompareFunction.LessEqual y ReferenceStencil en 1. Esto significa que cuando el valor en el búfer de stencil es 1, el StencilTest tendrá éxito. Establecer StencilPass en StencilOperation. Keep hace que el StencilBuffer no se actualice. Esto nos permite dibujar varias veces usando la misma máscara.
En resumen, el StencilTest solo pasa cuando el StencilBuffer es menor que 1 (los píxeles alfa de la máscara) y no afecta al StencilBuffer.
Ahora que tenemos configurados nuestros DepthStencilStates. De hecho, podemos dibujar con una máscara. Simplemente dibuje la máscara con el primer DepthStencilState. Esto afectará tanto al BackBuffer como al StencilBuffer. Ahora que el búfer de plantilla tiene un valor de 0 donde la máscara tenía transparencia y 1 donde contenía color, podemos usar StencilBuffer para enmascarar imágenes posteriores.
El segundo SpriteBatch usa el segundo DepthStencilStates. No importa lo que dibuje, solo los píxeles en los que StencilBuffer esté configurado en 1 pasarán la prueba de la plantilla y se dibujarán en la pantalla.
A continuación se muestra la totalidad del código en el método Draw, no olvide establecer PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8 en el constructor del juego.
fuente
En la respuesta anterior , sucedieron algunas cosas raras cuando hice exactamente lo que se explica.
Lo único que necesitaba eran los dos
DepthStencilStates
.Y los sorteos sin el
AlphaTestEffect
.Y todo estuvo bien como se esperaba.
fuente