Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Microsoft XNA 4.0 Game Development Cookbook

You're reading from   Microsoft XNA 4.0 Game Development Cookbook This book goes further than the basic manuals to help you exploit Microsoft XNA to create fantastic virtual worlds and effects in your 2D or 3D games. Includes 35 essential recipes for game developers.

Arrow left icon
Product type Paperback
Published in Jun 2012
Publisher Packt
ISBN-13 9781849691987
Length 356 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Luke Drumm Luke Drumm
Author Profile Icon Luke Drumm
Luke Drumm
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

Microsoft XNA 4.0 Game Development Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
1. Preface
1. Applying Special Effects FREE CHAPTER 2. Building 2D and 3D Terrain 3. Procedural Modeling 4. Creating Water and Sky 5. Non-Player Characters 6. Playing with Animation 7. Creating Vehicles 8. Receiving Player Input 9. Networking

Implementing lens flare within the Reach profile


A realistic lens flare can be achieved within the Reach profile by being "physically correct" and performing a check of each frame to confirm that the source of light is, at least partially, visible from the camera's point of view. (Tests involving whether something is in line of sight from something else are commonly referred to as occlusion tests.)

The bad news is, that without the benefit of hardware accelerated occlusion testing like the HiDef profile's OcclusionQuery class can offer, this test can be beyond the processing resources available to a game (especially one running on either mobile or older hardware).

The good news is that we do have a relatively cheap alternative that may just provide enough approximation for a game, and no one need be the wiser that the "proper" technique wasn't used, as the following illustration of the approximation in use demonstrates:

Getting ready

For this special effect, we're going to need an image that will form the basis of our glow. The image can be a simple white circle that fades out to complete transparency, as shown in the following illustration, but don't be afraid to make things even more visually interesting and swap it out for a more complex image later.

How to do it...

To create a lens flare within the Reach profile:

  1. 1. Start by creating a new class to hold the lens flare behavior:

    class ReachLensFlare
    {
    
  2. 2. Inside the new class, define some instance variables to hold details concerning the appearance of the glow:

    Texture2D glow;
    Vector2 glowOrigin;
    float glowScale = 0.25f;
    Vector2 lightPosition;
    public Vector3 LightDirection = Vector3.Normalize(new Vector3(0.5f, -0.1f, 0.5f));
    
  3. 3. Next, define some instance variables to hold details concerning the rendering of the glow:

    SpriteBatch spriteBatch;
    GraphicsDevice graphicsDevice;
    RenderTarget2D flareTarget;
    List<RenderTarget2D> blurTargets;
    Viewport flareViewport;
    public BasicEffect ShadowCaptureEffect;
    
  4. 4. Then, add a constructor:

    public ReachLensFlare(GraphicsDevice graphicsDevice, ContentManager content)
    {
    
  5. 5. Next, load the glow image details:

    glow = content.Load<Texture2D>(@"lensflare/glow");
    glowOrigin = new Vector2(glow.Width, glow.Height) / 2;
    
  6. 6. We now need to pre-calculate the size of the screen onto which the glow is going to be displayed:

    flareViewport = new Viewport(0,0,
    graphicsDevice.Viewport.Bounds.Width / 8,
    graphicsDevice.Viewport.Height /8);
    
  7. 7. Define the render targets, which will aid in the final composition of the overall effect:

    this.graphicsDevice = graphicsDevice;
    spriteBatch = new SpriteBatch(graphicsDevice);
    var pp = graphicsDevice.PresentationParameters;
    flareTarget = new RenderTarget2D(graphicsDevice,
    flareViewport.Width,
    flareViewport.Height,
    false,
    pp.BackBufferFormat,
    pp.DepthStencilFormat);
    blurTargets = new List<RenderTarget2D>()
    {
    new RenderTarget2D(graphicsDevice,
    3, 5,
    false,
    pp.BackBufferFormat,
    pp.DepthStencilFormat),
    new RenderTarget2D(graphicsDevice,
    7 ,4,
    false,
    pp.BackBufferFormat,
    pp.DepthStencilFormat),
    new RenderTarget2D(graphicsDevice,
    5, 9,
    false,
    pp.BackBufferFormat,
    pp.DepthStencilFormat),
    new RenderTarget2D(graphicsDevice,
    15, 10,
    false,
    pp.BackBufferFormat,
    pp.DepthStencilFormat),
    new RenderTarget2D(graphicsDevice,
    33, 43,
    false,
    pp.BackBufferFormat,
    pp.DepthStencilFormat),
    new RenderTarget2D(graphicsDevice,
    90, 90,
    false,
    pp.BackBufferFormat,
    pp.DepthStencilFormat)
    };
    
  8. 8. Complete the constructor with the effect that will be used to mask out any portions of the lens flare that are blocked by scenery:

    ShadowCaptureEffect = new BasicEffect(graphicsDevice)
    {
    DiffuseColor = Vector3.Zero,
    };
    
  9. 9. Next, add an Update() method to the class:

    public void Update(GameTime gameTime, Matrix view, Matrix projection)
    {
    
  10. 10. Add in a check to determine if the lens flare is visible at all where the player is currently looking:

    view.Translation = Vector3.Zero;
    var projectedPosition = flareViewport.Project(
    -LightDirection, projection,
    view, Matrix.Identity);
    if ((projectedPosition.Z < 0) || (projectedPosition.Z > 1))
    {
    return;
    }
    
  11. 11. If the lens flare is visible from the player's point of view, complete the Update() method by storing the light's screen space position:

    lightPosition = new Vector2(projectedPosition.X, projectedPosition.Y);
    
  12. 12. Create a method to begin the process of capturing the silhouette of the scene:

    public void BeginShadowCapture()
    {
    
  13. 13. Set the render target, clear the silhouette to black, and place the glow image in the background; ready to be possibly covered by scene elements:

    graphicsDevice.SetRenderTarget(flareTarget);
    graphicsDevice.Clear(Color.Black);
    spriteBatch.Begin();
    spriteBatch.Draw(
    glow, lightPosition, null, Color.White, 0,
    glowOrigin, glowScale, SpriteEffects.None, 0);
    spriteBatch.End();
    
  14. 14. Create the method to complete the silhouette capture and apply some blur:

    public void EndShadowCapture()
    {
    
  15. 15. Paint the captured silhouette scene onto each of the blur targets:

    foreach (var blurTarget in blurTargets)
    {
    graphicsDevice.SetRenderTarget(blurTarget);
    spriteBatch.Begin(
    SpriteSortMode.Deferred,
    BlendState.Opaque,
    SamplerState.AnisotropicClamp, null, null);
    spriteBatch.Draw(
    flareTarget,
    blurTarget.Bounds,
    Color.LightBlue);
    spriteBatch.End();
    }
    
  16. 16. Paint the blur targets onto the final target:

    graphicsDevice.SetRenderTarget(flareTarget);
    graphicsDevice.Clear(Color.Black);
    spriteBatch.Begin(
    SpriteSortMode.Deferred,
    BlendState.Additive,
    SamplerState.AnisotropicClamp,
    null, null);
    foreach (var blurTarget in blurTargets)
    {
    spriteBatch.Draw(
    blurTarget,
    flareTarget.Bounds,
    Color.White);
    }
    spriteBatch.End();
    graphicsDevice.SetRenderTarget(null);
    
  17. 17. Add a Draw() method to render the final target on screen:

    public void Draw()
    {
    spriteBatch.Begin(
    SpriteSortMode.Deferred,
    BlendState.Additive,
    SamplerState.AnisotropicClamp,
    null, null);
    spriteBatch.Draw(
    flareTarget,
    graphicsDevice.Viewport.Bounds,
    Color.White);
    spriteBatch.End();
    }
    
  18. 18. Next in our game code, we add an instance variable for the new lens flare:

    ReachLensFlare reachLensFlare;
    
  19. 19. Initialize an instance of the lens flare in the LoadContent() method:

    reachLensFlare = new ReachLensFlare(GraphicsDevice, Content);
    
  20. 20. Inside the Draw() method, there are two parts. The first part is where we draw the elements of the game scene which are likely to block out the lens flare as the player moves through the scene. Here's an example of the required call to the lens flare, followed by a rendering of the scene with the silhouette effect:

    reachLensFlare.BeginShadowCapture();
    // draw scene here.
    // e.g. here's some building and ground objects
    // being rendered via their own custom Draw methods
    foreach (var building in buildings)
    {
    building.Draw(camera, reachLensFlare.ShadowCaptureEffect);
    }
    ground.Draw(camera, reachLensFlare.ShadowCaptureEffect);
    reachLensFlare.EndShadowCapture();
    
  21. 21. The second part of the rendering process is drawing the scene normally, followed by a call to render the now fully formed lens flare:

    GraphicsDevice.Clear(Color.CornflowerBlue);
    foreach (var building in buildings)
    {
    building.Draw(camera);
    }
    ground.Draw(camera);
    reachLensFlare.Draw();
    

How it works...

The lens flare class works by drawing a silhouette of the scene in front of the light source, our glow image, and then applying various levels of blurring until we achieve a "flare" appearance.

In the constructor of the lens flare class, we can see the elements of this strategy being set up with the loading of the glow image and the creation of some render targets, which will be used later to facilitate blurring.

Also note the creation of the shadow effect, which when used in the drawing of game objects, will make them appear as solid black shapes, perfect for our silhouette needs.

Moving down to the Update() method, the calculation of the glow position in screen space can be spotted. In this case, we're taking advantage of the Project() method on the ViewPort class, which is conveniently designed to do that very calculation for us.

Note

To simulate the sun's distance and size, the view has been altered so that the glow is always just in front of the camera. This step is not required if the lens flare being displayed is in place of something a lot closer, such as a lamp or headlight.

Finally, we have the three methods that form the working heart of the lens flare.

  • The BeginShadowCapture() method sets the render target, so that any rendering performed from this point onwards will be diverted into the flareTarget. It clears the contents of the render target to black, and draws the glow image.

  • Next is the EndShadowCapture() method, which is called by the game after the scene has finished being drawn onto the render target. This is where the blurring process takes place.

    The captured image in the render target is drawn onto each of the blur render targets, and then all of the blur render targets are combined back into one image.

    A combination of the low resolution of each one of the blur render targets, along with the smoothed sampling method used in their combination, gives us our desired blurred effect.

  • The Draw() method performs the important act of actually drawing the newly created blurred glow on the screen.

Note

SpriteBatch alters a few GraphicsDevice settings when it renders to the screen, and this can cause strange side effects in the appearance of any 3D graphics rendered afterwards. If your 3D world stops rendering correctly, be sure to reset the GraphicsDevice.BlendState and GraphicsDevice.DepthStencilState properties upon completing any SpriteBatch operations.

There's more...

This technique for producing lens flares will probably need to be fine-tuned in your game on a case-by-case basis, to achieve the best result and avoid any distracting artifacts produced during its construction.

Altering the resolutions and tints of the blur render targets can make a significant difference to how convincing the effect is.

You have been reading a chapter from
Microsoft XNA 4.0 Game Development Cookbook
Published in: Jun 2012
Publisher: Packt
ISBN-13: 9781849691987
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image