Recordemos que al principio del todo presentamos un gran conjunto de parámetros que están relacionados con el Alpha dentro de la API de XNA:
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;
En esta ocasión nos centraremos en el segundo bloque, este está relacionado con el canal alpha del backbuffer y mucha gente los suele confundir con los del primer grupo. Lo más importante es que todos estos parámetros vienen desactivados por defecto, y seguirán así mientras no pongamos SeparateAlphaBlendEnable a true. En muchos sitios podemos ver como se asignan valores a AlphaSourceBlend y AlphaDestinationBlend sin haber activado SeparateAlphaBlendEnable por lo que esos cambios no tendrán ningún efecto. También hay que tener en cuenta que ciertas instrucciones de XNA modifican el Device.RenderState, como pueden ser la instrucción de SpriteBacth ó la de pass de un effect.
Pasemos a explicar cual es la finalidad de este grupo de instrucciones, cuando queremos pintar objetos translucidos en la escena y estamos realizando un render sobre el framebuffer, este a su vez es también una imagen y por lo tanto tiene sus canales R, G, B, y A. El canal Alpha del framebuffer suele usarse para guardar información extra sobre todo cuando se aplican efectos de postprocessing en los que el render final es una composición de varios renders previos.
Bien con SeparateAlphaBlendEnable = false a la hora de realizar el test de Alpha durante el render se usa la fórmula:
(Source * SourceBlend) (blendFunction) (Destination * DestinationBlend)
esta operación se realiza tanto para los canales RGB como para el canal alpha del framebuffer, sin embargo cuando configuramos SeparateAlphaBlendEnable = true podemos cambiar esto y hacer que se apliquen dos operaciones (una para los canales RGB y otra para el canal alpha).
Operación1 sobre canales RGB
(Source.RGB * SourceBlend) (blendFunction) (Destination.RGB * DestinationBlend)
Operación2 sobre el canal Alpha
(Source.A * AlphaSourceBlend) (AlphaBlendOperation) (Destination.A * AlphaDestinationBlend)
Veamos un ejemplo, utilizaremos la siguiente textura en PNG que contendrá tanto los canales RGB como el canal Alpha, aunque veámoslos por separado:
Canales RGB
Canal Alpha
Vamos a aplicar una operación a los canales RGB con BlendFactor para hacer la vidriera semitranslucida, y por otra parte vamos a usar el canal Alpha de la textura y vamos a mezclarlo con el canal alpha de FrameBuffer. Para poder apreciar la diferencia en todos los canales del FrameBuffer usaremos el siguiente procedimiento, renderizamos sobre un RenderTarget2D y posteriormente pintaremos la textura de este RenderTarget2D usando un SpriteBatch al cual le aplicaremos un Shader:
XNA Code:
//Pintamos sobre un RenderTarget2D
GraphicsDevice.SetRenderTarget(0, rt);
graphics.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer,
Color.Black, 1.0f, 0);
//Background
spritebatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.SaveState);
spritebatch.Draw(background, Vector2.Zero, Color.White);
spritebatch.End();
GraphicsDevice.VertexDeclaration = vertexDeclaration;
GraphicsDevice.RenderState.CullMode = CullMode.None;
//Configuración del RenderState
GraphicsDevice.RenderState.AlphaBlendEnable = true;
GraphicsDevice.RenderState.BlendFactor = new Color(127, 127, 127);
GraphicsDevice.RenderState.BlendFunction = BlendFunction.Add;
GraphicsDevice.RenderState.SourceBlend = Blend.BlendFactor;
GraphicsDevice.RenderState.DestinationBlend = Blend.InverseBlendFactor;
GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = true;
GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;
GraphicsDevice.RenderState.AlphaSourceBlend = Blend.SourceAlpha;
GraphicsDevice.RenderState.AlphaDestinationBlend = Blend.Zero;
//Render
effect.Begin();
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Begin();
effect.Parameters["World"].SetValue(Matrix.CreateRotationY(rotation));
effect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(0, 0, -4), Vector3.Zero, Vector3.Up));
effect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, 1, 10000));
effect.Parameters["DiffuseTexture"].SetValue(textures[index]);
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(
PrimitiveType.TriangleList, boxData, 0, 2);
pass.End();
}
effect.End();
//Ahora pintamos la imágen almacenada en el RenderTarget2D sobre un SpriteBatch
GraphicsDevice.SetRenderTarget(0, null);
spritebatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
alphaEffect.Begin();
alphaEffect.CurrentTechnique.Passes[0].Begin();
alphaEffect.Parameters["Alpha"].SetValue(alpha);
spritebatch.Draw(rt.GetTexture(), Vector2.Zero, Color.White);
alphaEffect.CurrentTechnique.Passes[0].End();
alphaEffect.End();
spritebatch.End();
Shader
sampler ColorMapSampler;
bool Alpha;
float4 PixelShaderFunctionAlpha(float2 TexCoord : TEXCOORD0) : COLOR0
{
if(Alpha)
{
float a = tex2D(ColorMapSampler, TexCoord).a;
return float4(a, a, a, 1);
}
else
{
return tex2D(ColorMapSampler, TexCoord);
}
}
technique Technique1
{
pass Pass1
{
PixelShader = compile ps_2_0 PixelShaderFunctionAlpha();
}
}
Este Shader nos permitirá mediante el valor de un parámetro “Alpha”, ver los canales RGB ó el canal alpha del resultado final.
Resultado en los canales RGB del FrameBuffer:
Resultado en el canal alpha del FrameBuffer
Bueno como veis si antes teníamos muchas posibilidades, ahora con SeparateAlphaBlendEnable estas se incrementan aún más, ya que AlphaSourceBlend y AlphaDestinationBlend pueden ser configurados con cualquiera de los valores Blend vistos anteriormente para SourceBlend y DestinationBlend, de igual modo AlphaBlendOperation puede tomar cualquiera de los valores que vimos para BlendFunction.
************************************************************************************************
– 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)
************************************************************************************************