Alpha blending en XNA (III de IV)

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

Glass

Canal Alpha

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:

Capture2

 

Resultado en el canal alpha del FrameBuffer

Capture1

 

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)

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

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.

Deja un comentario

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