{"id":2740,"date":"2023-08-02T19:14:04","date_gmt":"2023-08-03T00:14:04","guid":{"rendered":"https:\/\/badecho.com\/?p=2740"},"modified":"2024-05-16T13:19:46","modified_gmt":"2024-05-16T18:19:46","slug":"alpha-spritebatch","status":"publish","type":"post","link":"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/","title":{"rendered":"Changing Opacity of an Entire MonoGame SpriteBatch"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"alignleft size-full\"><img loading=\"lazy\" width=\"856\" height=\"448\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/07\/ChangingOpacity.png\" alt=\"Changing Opacity of an Entire MonoGame SpriteBatch\" class=\"wp-image-2741\" srcset=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/07\/ChangingOpacity.png 856w, https:\/\/badecho.com\/wp-content\/uploads\/2023\/07\/ChangingOpacity-300x157.png 300w, https:\/\/badecho.com\/wp-content\/uploads\/2023\/07\/ChangingOpacity-768x402.png 768w, https:\/\/badecho.com\/wp-content\/uploads\/2023\/07\/ChangingOpacity-480x251.png 480w\" sizes=\"(max-width: 856px) 100vw, 856px\" \/><\/figure><\/div>\n\n\n\n<p>Recently, while working on the Bad Echo game framework, I required the ability to apply an overriding amount of transparency to all sprites drawn within a particular <code>SpriteBatch<\/code> batch operation.<\/p>\n\n\n\n<p>Unfortunately, as the first reply to this <a href=\"https:\/\/community.monogame.net\/t\/can-i-apply-an-opacity-setting-to-an-entire-spritebatch\/8521\" target=\"_blank\" rel=\"noreferrer noopener\">forum post<\/a> indicates, MonoGame&#8217;s <code>SpriteBatch<\/code> class does not support such an operation. The suggestion given by the developer was to mess around with the internals of <code>SpriteBatch<\/code> to implement said functionality.<\/p>\n\n\n\n<p>Reading this inspired me to solve this problem by doing just that, albeit in my own way. This article explores the solution I devised for this problem and the things I learned while solving it.<\/p>\n\n\n\n<h2>The Problem<\/h2>\n\n\n\n<p>Let&#8217;s spend some time reviewing what I was having trouble doing. After that, I&#8217;ll demonstrate how to enhance the <code>SpriteBatch<\/code> class to do what we want.<\/p>\n\n\n\n<p>A color&#8217;s transparency is specified by its&nbsp;<em>alpha channel<\/em>, one of its components most commonly represented by the final element in a standard&nbsp;<strong>RGBA<\/strong>&nbsp;tuple.<\/p>\n\n\n\n<p>A minimum alpha value is fully transparent, while a maximum value is fully opaque.<\/p>\n\n\n\n<h3>The Typical Way of Applying Transparency<\/h3>\n\n\n\n<p>Applying some degree of transparency to a texture or sprite we&#8217;re rendering is actually a straightforward affair. All we need to do is multiply the <code>Color<\/code> we&#8217;re applying to the sprite by the desired alpha (the alpha being a float-type value between 0 and 1).<\/p>\n\n\n\n<h6>SpriteBatch.Draw with Transparency<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Half transparency\nspriteBatch.Draw(firstTexture, position1, Color.White * 0.5f);\n\n\/\/ Less transparency\nspriteBatch.Draw(secondTexture, position2, Color.White * 0.8f);\n\n\/\/ Fully opaque\nspriteBatch.Draw(thirdTexture, position3, Color.White);\n<\/pre>\n\n\n<p>If this satisfies all your needs, you can stop reading right now! If your rendering logic is structured so that each component draws to its own sprite batch operation, it probably does.<\/p>\n\n\n\n<h3>No Control Over Hierarchical Rendering Using a Single Batch<\/h3>\n\n\n\n<p>If you&#8217;re using a hierarchy of components, all drawing to the same <code>SpriteBatch<\/code> within a single batch operation, the above approach isn&#8217;t helpful if we wish to exert control from the root.<\/p>\n\n\n\n<p>For example, let&#8217;s say we have a root rendering object that initiates a batch operation via <code>SpriteBatch.Begin<\/code>. After creating the batch operation, the root passes said <code>SpriteBatch<\/code> instance to its children, rendering objects responsible for calling <code>SpriteBatch.Draw<\/code>.<\/p>\n\n\n\n<p>Given something like I just described, each of the children mentioned above will need to play ball if we wish to impose some batch-wide transparency effect.<\/p>\n\n\n\n<h3>Where I Ran Into Issues<\/h3>\n\n\n\n<p>The Bad Echo game framework includes components that match this description exactly. In particular, the user interface system it makes available. It consists of a <code>Screen<\/code> object that contains layers of controls and is responsible for measuring, arranging, and rendering on the screen.<\/p>\n\n\n\n<p>This user interface system is made available as a discrete g<em>ame state<\/em> by the <code>ScreenState<\/code> class, whose <code>Draw<\/code> override is where I originally wanted a way to apply opacity to an entire sprite batch.<\/p>\n\n\n\n<p>A feature of the framework&#8217;s game states system is the animated transitioning of a state&#8217;s activation and deactivation. I desired to have entire user interfaces (controls and all) be able to fade in or out during these transitions (if so configured).<\/p>\n\n\n\n<h6>ScreenState.cs Draw Override<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/\/ &lt;inheritdoc \/&gt;\npublic override void Draw(SpriteBatch spriteBatch)\n{\n    \/\/ Activation progress is exposed by the ActivationPercentage property.\n    spriteBatch.Begin(SpriteSortMode.Immediate,\n                      blendState: BlendState.AlphaBlend,\n                      samplerState: SamplerState.PointClamp,\n                      rasterizerState: new RasterizerState { ScissorTestEnable = true });\n    \n    _screen.Draw(spriteBatch);\n\n    spriteBatch.End();\n}\n<\/pre>\n\n\n<p>The code begins a sprite batch operation and then sends it off to the <code>_screen<\/code>, which will itself be passing it to potentially many, many controls. It would sure be nice to have a way to set the opacity to a value based on the mentioned <code>ActivationPercentage<\/code> property at the top here.<\/p>\n\n\n\n<p>One might quickly notice that SpriteBatch.Begin accepts an <code>effect<\/code> named parameter that allows us to provide a different <code>Effect<\/code> type to use during rendering passes. <\/p>\n\n\n\n<p>What if we try passing an instance of <code>BasicEffect<\/code>, which is a built-in type that exposes a lovely <code>Alpha<\/code> parameter for us to set!<\/p>\n\n\n\n<h6>Using BasicEffect &#8212; Won&#8217;t Work<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/\/ &lt;inheritdoc \/&gt;\npublic override void Draw(SpriteBatch spriteBatch)\n{\n    var alphaEffect = new BasicEffect(spriteBatch.GraphicsDevice)\n                      {   \/\/ Using a power curve for a less boring animation.\n                          Alpha = (float) Math.Pow(ActivationPercentage, 3)\n                      };\n\n    spriteBatch.Begin(SpriteSortMode.Immediate,\n                      blendState: BlendState.AlphaBlend,\n                      samplerState: SamplerState.PointClamp,\n                      rasterizerState: new RasterizerState { ScissorTestEnable = true },\n                      effect: alphaEffect);\n    \n    _screen.Draw(spriteBatch);\n\n    spriteBatch.End();\n}\n<\/pre>\n\n\n<p>Boy, that&#8217;d make for a nice short article if this worked. But, it won&#8217;t. More than likely, nothing will visibly render on the screen when this code runs.<\/p>\n\n\n\n<p>Even though <code>SpriteBatch<\/code> allows us to provide it with an effect, it internally relies on the <code>SpriteEffect<\/code> type for things to actually end up being rendered correctly. <\/p>\n\n\n\n<p>Using a general-purpose effect like <code>BasicEffect<\/code>, while perhaps sensible when dealing with vertex buffers and the like, isn&#8217;t going to fly at all with specialized renderers like <code>SpriteBatch<\/code>.<\/p>\n\n\n\n<h3>How We&#8217;ll Solve This<\/h3>\n\n\n\n<p>We&#8217;re on the right track. What we need is an effect that is like <code>SpriteEffect<\/code>, in that it does what needs to be done in order for <code>SpriteBatch<\/code> to work correctly, but one that also makes available an <code>Alpha<\/code> parameter, like <code>BasicEffect<\/code>.<\/p>\n\n\n\n<p>So, we&#8217;ll make our own effect. This will consist of a managed <code>Effect<\/code>-based class that exposes an <code>Alpha<\/code> property which we can manipulate, and the HLSL code for the actual shader which will be running on our pixels to make them transparent.<\/p>\n\n\n\n<p>Let&#8217;s start with the scary stuff: the HLSL shader code.<\/p>\n\n\n\n<h2>Custom Shader Code<\/h2>\n\n\n\n<p><a href=\"http:\/\/rbwhitaker.wikidot.com\/intro-to-shaders\" target=\"_blank\" rel=\"noreferrer noopener\">Shaders<\/a> are an advanced game development topic that I am starting to scratch the surface of myself. They are programs that (very simply put) run on every vertex\/pixel our graphics card is rendering.<\/p>\n\n\n\n<p>HLSL is the C-like language used when targeting DirectX with our shader; we use GLSL if we&#8217;re targeting OpenGL.<\/p>\n\n\n\n<p>From what I understand, we will always use HLSL with MonoGame, even if we&#8217;re targeting OpenGL. This is because we process shader code with the <a href=\"https:\/\/docs.monogame.net\/articles\/tools\/mgfxc.html\" target=\"_blank\" rel=\"noreferrer noopener\">mgfxc<\/a> utility, which only accepts HLSL, but will convert the HLSL to GLSL using a parser if needed.<\/p>\n\n\n\n<p>This parser used is MojoShader, which translates bytecode as opposed to source code, and one which (from what I understand) MonoGame is moving away from.<\/p>\n\n\n\n<p>The reliance on MojoShader, coupled with the requirement to support many platforms, requires us to code our shaders using the&nbsp;<a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/direct3d11\/d3d11-effect-format\" target=\"_blank\" rel=\"noreferrer noopener\"><em>effect format<\/em><\/a>, which is technically deprecated now with newer versions of DirectX.<\/p>\n\n\n\n<p>While it&#8217;s unfortunate to be learning to code using a deprecated format, we don&#8217;t have much of an option with MonoGame &#8212; it&#8217;s not a waste of time as far as learning goes, however, as the only difference between modern shader setups and effect files is that shaders would be in individual <code>.hlsl<\/code> files as opposed to all of them being in a big <code>.fx<\/code> file, combined through the defining of a&nbsp;<em>technique<\/em>.<\/p>\n\n\n\n<h3>What the Shader Needs to Do<\/h3>\n\n\n\n<p>With that rambling preamble concluded, let&#8217;s look at what our shader has to do:<\/p>\n\n\n\n<ul><li>Everything the stock <code>SpriteEffect<\/code> does in order for <code>SpriteBatch<\/code> draw operations to work.<\/li><li>Expose an <code>Alpha<\/code> parameter we can manipulate from our effect class.<\/li><li>Override the alpha of the color being used to reflect what <code>Alpha<\/code> is set to.<\/li><\/ul>\n\n\n\n<p>The first point directs us to start with the stock <em>SpriteEffect.fx<\/em> as the base for our shader code.<\/p>\n\n\n\n<p>We can then take a look at how MonoGame exposes parameters on its built-in shaders, and use what we learn from that to add our own <code>Alpha<\/code> parameter.<\/p>\n\n\n\n<p>Finally, how do we change the alpha of the colors we are rendering? The color data we&#8217;ll be working with is a <code>float4<\/code> data type with the <code>COLOR[N]<\/code> semantic.<\/p>\n\n\n\n<p>With that in mind, we can directly set the alpha channel&#8217;s value on a <code>COLOR<\/code> like so:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>color.a = Alpha<\/code><\/pre>\n\n\n\n<p>However, this most likely isn&#8217;t going to give us the results we expect. For example, if <code>Alpha<\/code> was 0, you&#8217;d expect the texture being rendered to be completely transparent, yes? At runtime, you would see that is not the case.<\/p>\n\n\n\n<p>The reason is because the default blending MonoGame uses expects opacity to be represented through the use of <em>premulitplied alpha<\/em>.<\/p>\n\n\n\n<h3>Premultiplied Alpha<\/h3>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignright size-full\"><img loading=\"lazy\" width=\"112\" height=\"112\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2022\/02\/sus_112.png\" alt=\"Omni Suspicious...\" class=\"wp-image-2263\"\/><\/figure><\/div>\n\n\n\n<p>I kept seeing the term &#8220;premultiplied alpha&#8221; being thrown around, and I had no idea what anyone was talking about. So, I did some reading.<\/p>\n\n\n\n<p>When we typically think of alpha alongside the other color components, we&#8217;re normally thinking of <em>straight alpha<\/em>. <\/p>\n\n\n\n<p>We interface with straight alpha all the time when using tools such as image editing software. For example, if we wanted to paint a lavender color with 50% transparency in Photoshop we&#8217;d slap <strong>#e6e6fa80<\/strong> or <strong>(230, 230, 250, 0.5) <\/strong>into the color picker. <\/p>\n\n\n\n<p>The 0.5 alpha value achieves 50%  transparency and it is completely separate from the preceding RGB values. RGB and alpha values are independent when using straight alpha.<\/p>\n\n\n\n<p>When using straight alpha:<\/p>\n\n\n\n<ul><li>RGB values specify the color of what&#8217;s being drawn.<\/li><li>The alpha value specifies the opacity.<\/li><\/ul>\n\n\n\n<p>With premultiplied alpha, RGB and alpha values are linked. <strong>In order to make an object transparent, you must not only reduce the alpha value, but also the RGB values<\/strong>.<\/p>\n\n\n\n<p>When using premultiplied alpha:<\/p>\n\n\n\n<ul><li>RGB values specify how much color of what&#8217;s being drawn contributes to the output.<\/li><li>The alpha value specifies how much of what&#8217;s being drawn obscures whatever is behind it.<\/li><\/ul>\n\n\n\n<p>Often we think we dealing with straight alpha, but the program we&#8217;re using may very well be converting it to premultiplied alpha in the background. The reason why premultiplied alpha is used is because it gives better results when working with multiple layers.<\/p>\n\n\n\n<p>By default, the textures and <code>BlendState<\/code> we&#8217;re using are going to be using premultiplied alpha, so we&#8217;ll need to use just that in our shader. If, for some reason, you are using straight alpha, then you&#8217;ll want to change your shader to simply set the alpha member of the <code>color<\/code> and leave the RGB values untouched.<\/p>\n\n\n\n<h3>The Shader Itself<\/h3>\n\n\n\n<p><code>SpriteBatch<\/code> relies on MonoGame&#8217;s built-in&nbsp;<em>SpriteEffect.fx<\/em>&nbsp;shader to function correctly. Because of this,&nbsp;<em>SpriteEffect.fx<\/em>&nbsp;can be used as a template for our code.<\/p>\n\n\n\n<p>For the draw operations of <code>SpriteBatch<\/code> to function correctly, our shader needs to expose a projection matrix parameter that it will use to transform vertex positions being inputted.<\/p>\n\n\n\n<p>These positional transforms are required because <code>SpriteBatch<\/code> operates in 2D instead of the more typical 3D. I&#8217;d recommend consulting MonoGame source for more information.<\/p>\n\n\n\n<p>So, we&#8217;ll write code that does all this, then tack on an additional <code>Alpha<\/code> parameter that will be multiplied against the color&#8217;s RGB values.<\/p>\n\n\n\n<h6>AlphaSpriteEffect.fx<\/h6>\n\n\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#if OPENGL\n    #define _vs(r)  : register(vs, r)\n    #define _ps(r)  : register(ps, r)\n    #define _cb(r)\n    #define VS_MODEL vs_3_0\n    #define PS_MODEL ps_3_0\n\n    #define BEGIN_PARAMETERS\n    #define END_PARAMETERS\n\n    #define SAMPLE(texture, texCoord) tex2D(texture, texCoord) \n\n    sampler2D Texture : register(s0);\n#else\n    #define _vs(r)\n    #define _ps(r)\n    #define _cb(r)\n    #define VS_MODEL vs_4_0_level_9_1\n    #define PS_MODEL ps_4_0_level_9_1\n\n    #define BEGIN_PARAMETERS    cbuffer Parameters : register(b0) {\n    #define END_PARAMETERS      };\n\n    #define SAMPLE(texture, texCoord) texture.Sample(texture##Sampler, texCoord)\n\n    Texture2D&lt;float4&gt; Texture : register(t0);\n    sampler TextureSampler : register(s0);\n#endif\n\nBEGIN_PARAMETERS\n    float4x4 MatrixTransform _vs(c0) _cb(c0);\n    float Alpha _vs(c4) _cb(c4);\nEND_PARAMETERS\n    \nstruct VSOutput\n{\n    float4 position : SV_Position;\n    float4 color    : COLOR0;\n    float2 texCoord : TEXCOORD0;\n};\n\nVSOutput SpriteVertexShader(    float4 position : POSITION0, \n                                float4 color : COLOR0, \n                                float2 texCoord : TEXCOORD0)\n{\n    VSOutput output;\n\n    output.position = mul(position, MatrixTransform);\n    output.color = color;\n    output.color.a *= Alpha;\n    output.color.rgb *= Alpha;    \n    output.texCoord = texCoord;\n    \n    return output;\n}\n\nfloat4 SpritePixelShader(VSOutput input) : SV_Target0\n{\n    return SAMPLE(Texture, input.texCoord) * input.color;\n}\n\ntechnique SpriteBatch\n{\n    pass\n    {\n        VertexShader = compile VS_MODEL SpriteVertexShader();\n        PixelShader = compile PS_MODEL SpritePixelShader();\n    }\n};\n<\/pre>\n\n\n<p>The shader code shown above exposes the needed matrix transformation and alpha value parameters and can be targeted towards either OpenGL or DirectX (<em>note: I tested the code in this article using OpenGL only; however, I&#8217;m reasonably confident it will run on DirectX, but no guarantees!<\/em>).<\/p>\n\n\n\n<p>Lines 42-55 contain our vertex shader: <code>SpriteVertexShader<\/code>. It transforms the input position using the configured projection matrix and then applies the configured alpha to our color in a manner that&#8217;s appropriate when working with colors that use premultiplied alpha (i.e., we&#8217;re multiplying the RGB values by the alpha).<\/p>\n\n\n\n<h3>Compiling the Shaders<\/h3>\n\n\n\n<p>We usually package custom shader effects with our game by adding them as assets through the MonoGame Content Pipeline. The content pipeline will compile the&nbsp;<em>.fx&nbsp;<\/em>files into platform-specific binary shader object files for us and also provide a means to load our custom effect assets as <code>Effect<\/code> instances at runtime.<\/p>\n\n\n\n<p>We don&#8217;t want to do any of that for a few reasons:<\/p>\n\n\n\n<ul><li>Shaders meant to power <code>SpriteBatch<\/code> require additional managed logic in order to function correctly, therefore the base <code>Effect<\/code> class will not suit our purposes.<\/li><li>I created this effect for it to be used by components belonging to a library &#8212; a redistributable library needs to have the binary shader object packed into its assembly, lest we risk violating ye olde principle of least astonishment.<\/li><\/ul>\n\n\n\n<p>Instead of using the content pipeline, we&#8217;ll take matters into our own hands by first using the <a href=\"https:\/\/docs.monogame.net\/articles\/tools\/mgfxc.html\" target=\"_blank\" rel=\"noreferrer noopener\">mgfxc<\/a> utility to compile the shader, and then embedding the output as a resource in our assembly.<\/p>\n\n\n\n<h6>mgfxc Command Line<\/h6>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet tool install --global dotnet-mgfxc\nmgfxc AlphaSpriteEffect.fx AlphaSpriteEffect.mgfxo \/profile:OpenGL<\/code><\/pre>\n\n\n\n<p>Substitute <strong>OpenGL<\/strong> with <strong>DirectX<\/strong> if you&#8217;re targeting that instead.<\/p>\n\n\n\n<p>If you are authoring a library, and want support for multiple platforms baked into your assembly, you can follow MonoGame&#8217;s example and use <em>*.dx11.mgfxo<\/em> for DirectX-targeted shader objects and <em>*.ogl.mgfxo<\/em> for OpenGL-targeted shader objects.<\/p>\n\n\n\n<p>You&#8217;ll then want to ensure your custom effect class (which we&#8217;ll be going over next) loads the proper bytecode based on the platform being targeted by the consuming project. The Bad Echo game framework is only targeting OpenGL (at least for now; who knows what the future has in store for us!).<\/p>\n\n\n\n<h2>Custom Effect Class<\/h2>\n\n\n\n<p>We now need to write a managed class that will be responsible for loading our compiled shader as well as shaping its input.<\/p>\n\n\n\n<p>This is a fairly simple exercise. Our custom effect class needs to do the following:<\/p>\n\n\n\n<ul><li>Everything that the stock <code>SpriteEffect<\/code> class does.<\/li><li>Have support for an <code>Alpha<\/code> parameter, exposed as a writable property.<\/li><li>Load the bytecode compiled from <em>AlphaSpriteEffect.fx<\/em>.<\/li><\/ul>\n\n\n\n<h6>AlphaSpriteEffect.cs<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/\/ &lt;summary&gt;\n\/\/\/ Provides a &lt;see cref=&quot;SpriteBatch&quot;\/&gt; effect that allows control over the alpha channel of all\n\/\/\/ sprites drawn in a batch.\n\/\/\/ &lt;\/summary&gt;\npublic sealed class AlphaSpriteEffect : Effect\n{\n    private EffectParameter _matrixParam;\n    private EffectParameter _alphaParam;\n\n    private Viewport _lastViewport;\n    private Matrix _projection;\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Initializes a new instance of the &lt;see cref=&quot;AlphaSpriteEffect&quot;\/&gt; class.\n    \/\/\/ &lt;\/summary&gt;\n    \/\/\/ &lt;param name=&quot;device&quot;&gt;The graphics device used for sprite rendering.&lt;\/param&gt;\n    public AlphaSpriteEffect(GraphicsDevice device)\n        : base(device, Properties.Effects.AlphaSpriteEffect)\n    {\n        CacheEffectParameters();\n    }\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Initializes a new instance of the &lt;see cref=&quot;AlphaSpriteEffect&quot;\/&gt; class.\n    \/\/\/ &lt;\/summary&gt;\n    \/\/\/ &lt;param name=&quot;cloneSource&quot;&gt;The &lt;see cref=&quot;AlphaSpriteEffect&quot;\/&gt; instance to clone.&lt;\/param&gt;\n    private AlphaSpriteEffect(AlphaSpriteEffect cloneSource)\n        : base(cloneSource)\n    {\n        CacheEffectParameters();\n    }\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Gets or sets an optional matrix used to transform the sprite geometry.\n    \/\/\/ &lt;\/summary&gt;\n    \/\/\/ &lt;remarks&gt;\n    \/\/\/ A &lt;see cref=&quot;Matrix.Identity&quot;\/&gt; value is used if this is null.\n    \/\/\/ &lt;\/remarks&gt;\n    public Matrix? MatrixTransform\n    { get; set; }\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Gets or sets the transparency of all sprites drawn in a batch.\n    \/\/\/ &lt;\/summary&gt;\n    \/\/\/ &lt;remarks&gt;\n    \/\/\/ This is set to be fully opaque by default.\n    \/\/\/ &lt;\/remarks&gt;\n    public float Alpha\n    { get; set; } = 1f;\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Creates a clone of the current &lt;see cref=&quot;AlphaSpriteEffect&quot;\/&gt; instance.\n    \/\/\/ &lt;\/summary&gt;\n    \/\/\/ &lt;returns&gt;A cloned &lt;see cref=&quot;Effect&quot;\/&gt; instance of this.&lt;\/returns&gt;\n    public override Effect Clone()\n        =&gt; new AlphaSpriteEffect(this);\n\n    \/\/\/ &lt;summary&gt;\n    \/\/\/ Lazily computes derived parameter values immediately before applying the effect.\n    \/\/\/ &lt;\/summary&gt;\n    protected override void OnApply()\n    {\n        Viewport viewport = GraphicsDevice.Viewport;\n\n        if (viewport.Width != _lastViewport.Width || viewport.Height != _lastViewport.Height)\n        {   \/\/ 3D cameras look into the -z direction (z = 1 is in front of z = 0).\n            \/\/ Sprite batch layers are ordered in the opposite (z  = 0 is in front of z = 1).\n            \/\/ We correct this by passing 0 for zNearPlane and -1 for zFarPlane; essentially a\n            \/\/ reverse mapping of the two.\n            Matrix.CreateOrthographicOffCenter(\n                0, viewport.Width, viewport.Height, 0, 0, -1, out _projection);\n\n            if (GraphicsDevice.UseHalfPixelOffset)\n            {\n                _projection.M41 -= 0.5f * _projection.M11;\n                _projection.M42 -= 0.5f * _projection.M22;\n            }\n\n            _lastViewport = viewport;\n        }\n\n        if (MatrixTransform.HasValue)\n            _matrixParam.SetValue(MatrixTransform.GetValueOrDefault() * _projection);\n        else\n            _matrixParam.SetValue(_projection);\n\n        _alphaParam.SetValue(Alpha);\n    }\n    \n    [MemberNotNull(nameof(_matrixParam), nameof(_alphaParam))]\n    private void CacheEffectParameters()\n    {\n        _matrixParam = Parameters[nameof(MatrixTransform)];\n        _alphaParam = Parameters[nameof(Alpha)];\n    }\n}\n<\/pre>\n\n\n<h2>Putting It Together<\/h2>\n\n\n\n<p>Let&#8217;s rewrite our <code>ScreenState.Draw<\/code> override to make use of these new goodies.<\/p>\n\n\n\n<h6>Updated ScreenState.cs Draw Override<\/h6>\n\n\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/\/ &lt;inheritdoc \/&gt;\npublic override void Draw(SpriteBatch spriteBatch)\n{\n    var alphaEffect = new AlphaSpriteEffect(_device)\n                      {   \/\/ Using a power curve for a less boring animation.\n                          Alpha = (float) Math.Pow(ActivationPercentage, 3)\n                      };\n\n    spriteBatch.Begin(SpriteSortMode.Immediate,\n                      blendState: BlendState.AlphaBlend,\n                      samplerState: SamplerState.PointClamp,\n                      rasterizerState: new RasterizerState { ScissorTestEnable = true },\n                      effect: alphaEffect);\n\n    _screen.Draw(spriteBatch);\n\n    spriteBatch.End();\n}\n<\/pre>\n\n\n<p>And voil\u00e0! It works.<\/p>\n\n\n\n<p>Don&#8217;t believe me? Hmm&#8230;the hour grows late and I don&#8217;t have a super fancy and involved user interface on hand at the moment. So, lets just whip up an interface with a very simple control layout and throw it into a <code>ScreenState<\/code>:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" width=\"496\" height=\"177\" src=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/08\/AlphaSpriteBatch.gif\" alt=\"Shows opacity being applied to an entire SpriteBatch.\" class=\"wp-image-2763\"\/><figcaption>The increasing alpha shown here is being applied to the entire SpriteBatch.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Even in this simple example, there are numerous components that are actually drawing to the single <code>SpriteBatch<\/code> being managed by the <code>ScreenState<\/code> instance. <\/p>\n\n\n\n<p>And, of course, with more complex control layouts, we&#8217;ll have many, many more components drawing to said <code>SpriteBatch<\/code>. Regardless of the number of objects doing draw calls, the changing opacity we observe in the above image is only applied in one place: the root of the sprite batch operation itself.<\/p>\n\n\n\n<p>Woot.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently, while working on the Bad Echo game framework, I required the ability to apply an overriding amount of transparency [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[10],"tags":[41,42,82,74],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v14.9 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\r\n<title>Changing Opacity of an Entire MonoGame SpriteBatch - omni&#039;s hackpad<\/title>\r\n<meta name=\"description\" content=\"Shows how we can override the alpha channel of everything drawn during a sprite batch operation through the use of a custom shader.\" \/>\r\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\r\n<link rel=\"canonical\" href=\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/\" \/>\r\n<meta property=\"og:locale\" content=\"en_US\" \/>\r\n<meta property=\"og:type\" content=\"article\" \/>\r\n<meta property=\"og:title\" content=\"Changing Opacity of an Entire MonoGame SpriteBatch - omni&#039;s hackpad\" \/>\r\n<meta property=\"og:description\" content=\"Shows how we can override the alpha channel of everything drawn during a sprite batch operation through the use of a custom shader.\" \/>\r\n<meta property=\"og:url\" content=\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/\" \/>\r\n<meta property=\"og:site_name\" content=\"omni&#039;s hackpad\" \/>\r\n<meta property=\"article:published_time\" content=\"2023-08-03T00:14:04+00:00\" \/>\r\n<meta property=\"article:modified_time\" content=\"2024-05-16T18:19:46+00:00\" \/>\r\n<meta property=\"og:image\" content=\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/07\/ChangingOpacity.png\" \/>\r\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\r\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/badecho.com\/#website\",\"url\":\"https:\/\/badecho.com\/\",\"name\":\"omni&#039;s hackpad\",\"description\":\"Game Code Disassembly. Omnified Modification. Madness.\",\"publisher\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/badecho.com\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/badecho.com\/wp-content\/uploads\/2023\/07\/ChangingOpacity.png\",\"width\":856,\"height\":448,\"caption\":\"Changing Opacity of an Entire MonoGame SpriteBatch\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/#webpage\",\"url\":\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/\",\"name\":\"Changing Opacity of an Entire MonoGame SpriteBatch - omni&#039;s hackpad\",\"isPartOf\":{\"@id\":\"https:\/\/badecho.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/#primaryimage\"},\"datePublished\":\"2023-08-03T00:14:04+00:00\",\"dateModified\":\"2024-05-16T18:19:46+00:00\",\"description\":\"Shows how we can override the alpha channel of everything drawn during a sprite batch operation through the use of a custom shader.\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/\"]}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/#webpage\"},\"author\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"headline\":\"Changing Opacity of an Entire MonoGame SpriteBatch\",\"datePublished\":\"2023-08-03T00:14:04+00:00\",\"dateModified\":\"2024-05-16T18:19:46+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/#webpage\"},\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\"},\"image\":{\"@id\":\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/#primaryimage\"},\"keywords\":\".NET,C#,HLSL,MonoGame\",\"articleSection\":\"General Dev\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/badecho.com\/index.php\/2023\/08\/02\/alpha-spritebatch\/#respond\"]}]},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/badecho.com\/#\/schema\/person\/3de67496328be7ae6e1f52faf582e9d2\",\"name\":\"Matt Weber\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/badecho.com\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7e345ac2708b3a41c7bd70a4a0440d41?s=96&d=mm&r=g\",\"caption\":\"Matt Weber\"},\"logo\":{\"@id\":\"https:\/\/badecho.com\/#personlogo\"}}]}<\/script>\r\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2740"}],"collection":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/comments?post=2740"}],"version-history":[{"count":23,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2740\/revisions"}],"predecessor-version":[{"id":2949,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/posts\/2740\/revisions\/2949"}],"wp:attachment":[{"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/media?parent=2740"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/categories?post=2740"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/badecho.com\/index.php\/wp-json\/wp\/v2\/tags?post=2740"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}