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.
Acepta todos los pixeles que tienen en su canal alpha un valor mayor que el valor de referencia.
Acepta todos los pixeles que tienen en su canal alpha un valor igual o mayor que el valor de referencia.
Acepta todos los pixeles que tienen en su canal alpha un valor menor que el valor de referencia.
Acepta todos los pixeles que tienen un su canal alpha un valor menor o igual que el valor de referencia.
Ningún pixel pasa el test.
Acepta todos los pixeles que tienen en su canal alpha un valor distinto del valor de referencia.
Veamos algunos ejemplos:
Canales RGB
Canal 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;
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;
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:
- Realizar un render de todos los objetos opacos de la escena.
- Ordenar por profundidad de atrás hacia delante los objetos transparentes de la escena.
- 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).
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.
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)
************************************************************************************************
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.
Perdon, queria decir 128 en el canal alpha. Me habia puesto a pensar en «PixelShader Mode» 😛
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
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.
Ey gracias Pablo, me alegro que te haya gustado.
Gracias, de verdad has sido una ayuda (en dos palabras) im-presionante. He dado un salto de gigante en mi juego.
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.