Alpha blending en XNA (IV de IV)

Vayamos ahora a por el último bloque de instrucciones presentado en el primer post:

 

GraphicsDevice.RenderState.AlphaBlendEnable = false;

GraphicsDevice.RenderState.BlendFunction = BlendFunction.Add;

GraphicsDevice.RenderState.BlendFactor = new Color(255, 255, 255, 255);

GraphicsDevice.RenderState.SourceBlend = Blend.One;

GraphicsDevice.RenderState.DestinationBlend = Blend.Zero;

 

GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = false;

GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;

GraphicsDevice.RenderState.AlphaSourceBlend = Blend.One;

GraphicsDevice.RenderState.AlphaDestinationBlend = Blend.Zero;

 

GraphicsDevice.RenderState.AlphaTestEnable = false;

GraphicsDevice.RenderState.AlphaFunction = CompareFunction.Always;

GraphicsDevice.RenderState.ReferenceAlpha = 0;

 

Este bloque es independiente de los dos anteriores, mientras los anteriores se referían al Alpha Blending este hace referencia al Alpha Test, lo he escrito dentro del mismo artículo ya que mucha gente los suele confundir.

GraphicsDevice.RenderState.AlphaTestEnable = false;

GraphicsDevice.RenderState.AlphaFunction = CompareFunction.Always;

GraphicsDevice.RenderState.ReferenceAlpha = 0;

Estas tres instrucciones están ligadas de forma que mientras no pongamos AlphaTestEnable = true los valores asignados a AlphaFunction y ReferenceAlpha no tendrán efecto, (por defecto AlphaTestEnable = false).

Estas instrucciones configuran un test basado en el canal alpha que es mucho más rápido que el test de Alpha Blending, su funcionamiento es el siguiente, asignamos un valor de referencia (ReferenceAlpha) con un valor entre 0 y 255 y luego seleccionamos una operación de comparación la cual descartará todos los pixeles que no la cumplan.

Las posibles funciones que podemos seleccionar son:

Always

Todos los pixeles pasan el test.

Equal

Acepta todos los pixeles que tienen en su canal alpha un valor igual que el de referencia.

Greater

Acepta todos los pixeles que tienen en su canal alpha un valor mayor que el valor de referencia.

GreaterEqual

Acepta todos los pixeles que tienen en su canal alpha un valor igual o mayor que el valor de referencia.

Less

Acepta todos los pixeles que tienen en su canal alpha un valor menor que el valor de referencia.

LessEqual

Acepta todos los pixeles que tienen un su canal alpha un valor menor o igual que el valor de referencia.

Never

Ningún pixel pasa el test.

NotEqual

Acepta todos los pixeles que tienen en su canal alpha un valor distinto del valor de referencia.

Veamos algunos ejemplos:

Canales RGB

grass

Canal Alpha

Alpha

Code 1

 

//Desactivado test de alpha blending

GraphicsDevice.RenderState.AlphaBlendEnable = false;

 

//Activamos el Alpha Test

GraphicsDevice.RenderState.AlphaTestEnable = true;

//Pasan todos los pixeles cuyo valor en el canal alpha super 200

GraphicsDevice.RenderState.AlphaFunction = CompareFunction.Greater;

//El valor de referencia será 200

GraphicsDevice.RenderState.ReferenceAlpha = 200;

 

Capture

Code 2

//Desactivado test de alpha blending

GraphicsDevice.RenderState.AlphaBlendEnable = false;

 

//Activamos el Alpha Test

GraphicsDevice.RenderState.AlphaTestEnable = true;

//Pasan todos los pixeles cuyo valor en el canal alpha sea inferior a 200

GraphicsDevice.RenderState.AlphaFunction = CompareFunction.Less;

//El valor de referencia será 200

GraphicsDevice.RenderState.ReferenceAlpha = 200;

Capture1

Este test es interesante ya que es mucho más rápido que el Alpha Blending, el cual además presenta un problema bastante grande junto al ZBuffer, y es que este algoritmo no funciona muy bien con objetos transparentes ya que recordemos que en este algoritmo íbamos marcando en un buffer la profundidad de los objetos para no pintar aquellos que estuviese eclipsados, pero en este caso cuando tenemos objetos transparentes si nos interesa que se pinten todos aquellos objetos que están por detrás de estos, mucha gente decide simplemente desactivar la escritura en el Zbuffer pero esto es un error, lo correcto sería:

  1. Realizar un render de todos los objetos opacos de la escena.
  2. Ordenar por profundidad de atrás hacia delante los objetos transparentes de la escena.
  3. Pintar los objetos transparentes en orden (desactivando la escritura del zbuffer).

Como se puede intuir esta solución es bastante lenta si pretendemos pintar muchos planos con transparencias pero casualmente una de las situaciones que más se presentan en los videojuegos es el pintado de vegetación usando billboards (que son planos con texturas de hierba).

SpeedTree

 

Para mejorar el rendimiento de esto el equipo de SpeedTree fue el primero en combinar el Alpha Blending con el Alpha test, de forma que los billboard más cercanos se pintaban usando AlphaBlending +  pre-ordenado y el resto usando Alpha Test sin ordenar pero usando un truco, modificando los canales alpha de las texturas de hierba con otras texturas de ruido para ocultar la apreciación de errores de profundidad en el dibujado.FractalTexture

Por último comentar que todos estos atributos relacionados con Alpha del RenderState también pueden ser configurados directamente desde el código HLSL, por ejemplo:

technique Feathering

{

    pass Pass0

    {

      VertexShader = compile vs_1_1 vs11();

      PixelShader  = compile ps_1_1 ps11();

 

      AlphaBlendEnable = true;

      SrcBlend = SrcAlpha;

      DestBlend = InvSrcAlpha;

      ZWriteEnable = false;

    }

    

    pass Pass1

    {

      VertexShader = compile vs_1_1 vs11();

      PixelShader  = compile ps_1_1 ps11();

      

      AlphaTestEnable = true;

      AlphaRef = 0x00000040;

      AlphaFunc = GreaterEqual;

      ZWriteEnable = true;

    } 

}

************************************************************************************************

Alpha Blending en XNA (I de IV)

Alpha Blending en XNA (II de IV)

Alpha Blending en XNA (III de IV)

Alpha Blending en XNA (IV de IV)

************************************************************************************************

Publicado por

jcanton

Javier is a Computer Science Engineer who has always had a passion for 3D graphics and software architecture. He learned C# almost at the same time as he learned to talk, and his first word was "base". He enjoys imparting talks about technology and has contributed in many important software and video game events. He has participated in multitude of software projects involving multitouch technologies, innovative user interfaces, augmented reality, and video games. Some of these projects were developed for companies such as Microsoft, Syderis, and nVidia. His professional achievements include being MVP for Windows DirectX and DirectX XNA for the last eight years, Xbox Ambassador, as well as Microsoft Student Partner and Microsoft Most Valuable Student during his years at college. Currently he works at Plainconcepts and he is the development team lead at WaveEngine project.

7 comentarios sobre “Alpha blending en XNA (IV de IV)”

  1. Hay algo que no me ha quedado del todo claro.
    Me gustaria hacerte una pregunta.

    Un pixel con 0.5 en el canal alpha pasa el Alpha test y se convierte en candidato para escribirse en el RenderTarget de turno. ¿Se aplica la funcion de AlphaBlending al escribirse en el RenderTarget? en otras palabras ¿Se ve semitransparente?

    Gracias.

  2. No, el Alpha Blending (si está activado) es el que se encagaría de hacer que ese objeto sea semitransparente, el Alpha test es un test previo que solo es capaz de aceptar o descartar pixeles que cumplan o no una condición.

    Saludos

  3. Hola Javi, me ha gustado mucho tu serie de Alpha Blending 🙂 a ver si pronto nos deleitas con algún otro tema técnico de XNA.

    Saludos.

  4. Una explicación muy detallada… pero no he conseguido usar el canal alpha para mezclar renders de dos objetos 3D sin que el fondo de uno me tape el primer render.

    Algo así como un sprite 2D creado a partir de un objeto 3d en tiempo de ejecución.

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *