Created
January 15, 2018 23:16
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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