Example 3c: RenderTargets and Blending.

NOTE: This example also uses the GradientBall class from example 3 and 3b. Also the picture Penguins.jpg has been added to Content.
vb.net Code:
  1. Public Class Game1
  2.     Inherits Microsoft.Xna.Framework.Game
  3.  
  4.     Private WithEvents graphics As GraphicsDeviceManager
  5.     Private WithEvents spriteBatch As SpriteBatch
  6.  
  7.     Private background As Texture2D
  8.     Private rtarget As RenderTarget2D
  9.     Private blendsub As BlendState
  10.     Private blendrevsub As BlendState
  11.  
  12.     Public Sub New()
  13.         graphics = New GraphicsDeviceManager(Me)
  14.         Content.RootDirectory = "Content"
  15.  
  16.         Dim rnd As New Random
  17.  
  18.         For i As Integer = 1 To 20 'Generate 20 gray balls of varying sizes moving in random directions.
  19.             Dim B As New GradientBall(Me, rnd.Next(30, 70), Color.Transparent, _
  20.                                       Color.Gray, New Vector2(rnd.Next(2, 10), rnd.Next(2, 10)))
  21.             Components.Add(B)
  22.         Next
  23.     End Sub
  24.  
  25.     Protected Overrides Sub Initialize()
  26.         MyBase.Initialize()
  27.  
  28.         IsMouseVisible = True
  29.         Window.AllowUserResizing = True
  30.         Window.Title = "Example 3c (Various blendstates)" 'Modified title.
  31.  
  32.         blendsub = New BlendState() With {.AlphaBlendFunction = BlendFunction.Subtract, _
  33.                                           .ColorBlendFunction = BlendFunction.Subtract, _
  34.                                           .AlphaSourceBlend = Blend.One, .AlphaDestinationBlend = Blend.One, _
  35.                                           .ColorSourceBlend = Blend.One, .ColorDestinationBlend = Blend.One}
  36.         blendrevsub = New BlendState() With {.AlphaBlendFunction = BlendFunction.ReverseSubtract, _
  37.                                              .ColorBlendFunction = BlendFunction.ReverseSubtract, _
  38.                                              .AlphaSourceBlend = Blend.One, .AlphaDestinationBlend = Blend.One, _
  39.                                              .ColorSourceBlend = Blend.One, .ColorDestinationBlend = Blend.One}
  40.     End Sub
  41.  
  42.     Protected Overrides Sub LoadContent()
  43.         MyBase.LoadContent()
  44.         spriteBatch = New SpriteBatch(GraphicsDevice)
  45.         background = Content.Load(Of Texture2D)("Penguins")
  46.         rtarget = New RenderTarget2D(GraphicsDevice, background.Width, background.Height)
  47.     End Sub
  48.  
  49.     Protected Overrides Sub UnloadContent()
  50.         MyBase.UnloadContent()
  51.         rtarget.Dispose()
  52.     End Sub
  53.  
  54.     Protected Overrides Sub Update(ByVal gameTime As GameTime)
  55.         If Keyboard.GetState.IsKeyDown(Keys.Escape) Then Me.Exit()
  56.         If Keyboard.GetState.IsKeyUp(Keys.P) Then MyBase.Update(gameTime) 'Will allow you to pause
  57.     End Sub
  58.  
  59.     Protected Overrides Sub Draw(ByVal gameTime As GameTime)
  60.         'Draw the balls to the rtarget RenderTarget2D
  61.         GraphicsDevice.SetRenderTarget(rtarget)
  62.         GraphicsDevice.Clear(Color.Transparent)
  63.         MyBase.Draw(gameTime)
  64.         GraphicsDevice.SetRenderTarget(Nothing)
  65.  
  66.         'Blend with the background.
  67.         If Mouse.GetState.LeftButton = ButtonState.Pressed Then
  68.             GraphicsDevice.Clear(Color.Black)
  69.             spriteBatch.Begin(Nothing, blendsub)
  70.             spriteBatch.Draw(background, Vector2.Zero, Color.White)
  71.             spriteBatch.Draw(rtarget, Vector2.Zero, Color.White)
  72.             spriteBatch.End()
  73.         ElseIf Mouse.GetState.RightButton = ButtonState.Pressed Then
  74.             GraphicsDevice.Clear(Color.White)
  75.             spriteBatch.Begin(Nothing, blendrevsub)
  76.             spriteBatch.Draw(background, Vector2.Zero, Color.White)
  77.             spriteBatch.Draw(rtarget, Vector2.Zero, Color.White)
  78.             spriteBatch.End()
  79.         Else
  80.             GraphicsDevice.Clear(Color.Black)
  81.             spriteBatch.Begin(Nothing, BlendState.Additive)
  82.             spriteBatch.Draw(background, Vector2.Zero, Color.White)
  83.             spriteBatch.Draw(rtarget, Vector2.Zero, Color.White)
  84.             spriteBatch.End()
  85.         End If
  86.     End Sub
  87. End Class

In this example, we will begin in the Draw method. Instead of just drawing the 20 balls, that are initialized in the same way as the one in example 3 or the 100 in example 3b, we choose to draw the balls to a target other than the screen. A socalled RenderTarget, which I have added as a global variable named rtarget and initialized/disposed in LoadContent and UnloadContent respectively, is basically nothing more than a bitmap. The GraphicsDevice is instructed to draw into this bitmap with the SetRenderTarget method, and the call to MyBase.Draw will draw all the 20 gray balls into it. The current render-target is then set to Nothing indicating that we wish to draw directly to the screen again.
Now we encounter one of those gray areas, where I did not use Update for user-input, but rather chose to use the Draw method directly, as it makes for shorter code in this example. Apologies in advance for teaching you bad coding-practise. The Draw method is seperated into 3 parts:
Pressing the LMB: On a black background, the blendsub BlendState is used to draw first the gray balls and then the Penguin texture.
Pressing the RMB: On a white background, the blendrevsub BlendState is used to draw first the gray balls and then the Penguin texture.
No buttons pressed: On a black background, additive blending is used to draw first the gray balls and then the Penguin texture.
Basically all blending is done using the following formula:
ResultColor = SourceColor * SourceBlendColor <ColorBlendFunction> DestinationColor * DestinationBlendColor
ResultAlpha = SourceAlpha * SourceBlendAlpha <AlphaBlendFunction> DestinationAlpha * DestinationBlendAlpha
The blendsub BlendState uses Subtraction as function and One for both blends giving a simple subtraction of source-color and destination-color as result. The blendrevsub is identical except it uses reverse subtraction giving subtraction of destination-color and source-color as result. And finally BlendState.Additive has one for both blends and uses addition as function giving source-color plus destination-color as result.
In these 3 examples, the additive blend is the most interesting giving a glow-like effect, but subtraction, min, max etc. have their uses and can be combined to give a wide range of interesting results.
At this point, you could try reloading your example 3b (100 balls) and try drawing the GradientBalls with additive blending (as opposed to the default Alpha). It should give you some odd modern-art type images with a complete blur of bright colors.