Skip to content

Instantly share code, notes, and snippets.

@Jjagg
Created January 15, 2018 23:16
Show Gist options
  • Save Jjagg/db34a25897e20dbdb0f16cb5bcb75493 to your computer and use it in GitHub Desktop.
Save Jjagg/db34a25897e20dbdb0f16cb5bcb75493 to your computer and use it in GitHub Desktop.
MonoGame sample that shows how you make a texture use the transparency of another texture by rendering it on top of the first one with a custom blend state.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace BlendStateSample
{
public class Game1 : Game
{
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;
private RenderTarget2D _renderTarget;
private Texture2D _gradient;
private BlendState _blendState;
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
_spriteBatch = new SpriteBatch(GraphicsDevice);
_renderTarget = new RenderTarget2D(GraphicsDevice, 200, 200);
_gradient = new Texture2D(GraphicsDevice, 1, 200);
var gradientData = new Color[_gradient.Height];
for (var y = 0; y < gradientData.Length; y++)
{
var alpha = (float) y / gradientData.Length;
// source color does not matter! We're only using alpha anyway
gradientData[y] = new Color(0, 0, 0, alpha);
}
_gradient.SetData(gradientData);
// input texture is (destination):
// r, g, b, 1
// gradient is (source):
// 0, 0, 0, a
// we want:
// r * a, g * a, b * a, a (because of premultiplied alpha: https://blogs.msdn.microsoft.com/shawnhar/2009/11/06/premultiplied-alpha/)
// so we can use default blending on the back buffer:
// A possible formula for desired (rgb, alpha) is:
// (destination.rgb * source.a, source.a)
// or written in full
// (source.rgb * 0 + destination.rgb * source.a , source.a * 1 + 0 * destination.a)
// = (source.rgb * ColorSourceBlend + destination.rgb * ColorDestinationBlend, source.a * AlphaSourceBlend + AlphaDestinationBlend * destination.a)
// where + is the blend function for color and alpha. It's hard to read if I abstract that out, but that's customizable too.
_blendState = new BlendState
{
ColorSourceBlend = Blend.Zero, // multiplier of the source color
ColorBlendFunction = BlendFunction.Add, // function to combine colors
ColorDestinationBlend = Blend.SourceAlpha, // multiplier of the destination color
AlphaSourceBlend = Blend.One, // multiplier of the source alpha
AlphaBlendFunction = BlendFunction.Add, // function to combine alpha
AlphaDestinationBlend = Blend.Zero, // multiplier of the destination alpha
};
}
protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
// clear the render target to all white
GraphicsDevice.SetRenderTarget(_renderTarget);
GraphicsDevice.Clear(Color.White);
// blend the gradient on top to make it partially transparent
var rect = new Rectangle(0, 0, 200, 200);
_spriteBatch.Begin(blendState: _blendState);
_spriteBatch.Draw(_gradient, rect, Color.White);
_spriteBatch.End();
// draw the result to the back buffer so we can see that it works
GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.Clear(Color.CornflowerBlue);
_spriteBatch.Begin();
_spriteBatch.Draw(_renderTarget, new Vector2(100f), Color.White);
_spriteBatch.End();
base.Draw(gameTime);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment