Book Image

Microsoft XNA 4.0 Game Development Cookbook

By : Luke Drumm
Book Image

Microsoft XNA 4.0 Game Development Cookbook

By: Luke Drumm

Overview of this book

Microsoft XNA attempts to free game developers from writing "repetitive boilerplate code", allowing them to focus on producing enjoyable gameplay rather than tedious and complicated setup. The Framework has reduced the once steep learning curve for game development, transforming it into something more attainable, and this cookbook will help you to take full advantage of XNA to bring reality into your virtual worlds. "Microsoft XNA 4.0 Game Development Cookbook" is the perfect resource for propelling your game development capabilities from the simple 2D demo towards engaging and exciting, professional looking games. With a diverse selection of game-related topics covered, discover how to create rich 2D and 3D worlds filled with interesting characters, detailed scenery and dynamic special effects applicable to PC, Xbox 360, and Windows Phone 7 game play. There is no shortage of introductory texts available for XNA, a number of which are fantastic for getting started with simple 2D games, but "Microsoft XNA 4.0 Game Development Cookbook"ù will help you take the steps to start producing games that have deeper gameplay, compelling graphics and that little bit of extra polish! The book's recipes will get you up and going quickly with the next level of game features such as 3D graphics, AI, and network play. With this practical cookbook to hand, even the more experienced developer will be better equipped to achieve high level tasks with XNA in a quick and efficient manner.
Table of Contents (15 chapters)
Microsoft XNA 4.0 Game Development Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface

Implementing lens flare within the HiDef profile


Modern GPUs are very good at determining whether one set of polygons is obscured by another set. We can use this to our advantage when creating lens flares within a HiDef profile.

Getting ready

This recipe assumes that you've already got a scene, rendering correctly, albeit without a lens flare.

How to do it...

To create a lens flare within the HiDef profile:

  1. 1. Create a new class to hold the lens flare behavior:

    class HiDefLensFlare
    {
    
  2. 2. Add some instance-level variables to hold the details of the occlusion test, the lighting, and the glow image:

    SpriteBatch spriteBatch;
    GraphicsDevice graphicsDevice;
    public BasicEffect ShadowCaptureEffect;
    OcclusionQuery occlusionQuery;
    bool occlusionQueryActive;
    float occlusionAlpha;
    const float querySize = 50;
    VertexPositionColor[] queryVertices;
    public Vector3 LightDirection = Vector3.Normalize(new Vector3(0.5f, -0.1f, 0.5f));
    Vector2 lightPosition;
    bool lightBehindCamera;
    Texture2D glow;
    Vector2 glowOrigin;
    float glowScale = 400f;
    
  3. 3. Next, add a constructor to set up the occlusion test prerequisites and load the glow texture:

    public HiDefLensFlare(GraphicsDevice graphicsDevice, ContentManager content)
    {
    this.graphicsDevice = graphicsDevice;
    spriteBatch = new SpriteBatch(graphicsDevice);
    ShadowCaptureEffect = new BasicEffect(graphicsDevice)
    {
    View = Matrix.Identity,
    VertexColorEnabled = true
    };
    occlusionQuery = new OcclusionQuery(graphicsDevice);
    queryVertices = new VertexPositionColor[4];
    queryVertices[0].Position = new Vector3(-querySize / 2, -querySize / 2, -1);
    queryVertices[1].Position = new Vector3(querySize / 2, -querySize / 2, -1);
    queryVertices[2].Position = new Vector3(-querySize / 2, querySize / 2, -1);
    queryVertices[3].Position = new Vector3(querySize / 2, querySize / 2, -1);
    glow = content.Load<Texture2D>(@"lensflare/glow");
    glowOrigin = new Vector2(glow.Width, glow.Height) / 2;
    }
    
  4. 4. Create a new BlendState instance-level variable so the ocular test can proceed without changing the visible image:

    static readonly BlendState ColorWriteDisable = new BlendState
    {
    ColorWriteChannels = ColorWriteChannels.None
    };
    
  5. 5. Add a new method to perform the ocular test:

    public void Measure(Matrix view, Matrix projection)
    {
    
  6. 6. Calculate the position of the lens flare on screen, and exit early if it's behind the player's viewpoint:

    var infiniteView = view;
    infiniteView.Translation = Vector3.Zero;
    var viewport = graphicsDevice.Viewport;
    var projectedPosition = viewport.Project(
    -LightDirection, projection,
    infiniteView, Matrix.Identity);
    if ((projectedPosition.Z < 0) ||
    (projectedPosition.Z > 1))
    {
    lightBehindCamera = true;
    return;
    }
    lightPosition = new Vector2(projectedPosition.X, projectedPosition.Y);
    lightBehindCamera = false;
    
  7. 7. Add the calculation for how much of the lens flare test area is occluded by the scene once the previous occlusion test has completed:

    if (occlusionQueryActive)
    {
    if (!occlusionQuery.IsComplete)
    {
    return;
    }
    const float queryArea = querySize * querySize;
    occlusionAlpha = Math.Min(
    occlusionQuery.PixelCount / queryArea, 1);
    }
    
  8. 8. Set up for the next occlusion query:

    graphicsDevice.BlendState = ColorWriteDisable;
    graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
    ShadowCaptureEffect.World = Matrix.CreateTranslation(
    lightPosition.X,
    lightPosition.Y, 0);
    ShadowCaptureEffect.Projection = Matrix.CreateOrthographicOffCenter(0,
    viewport.Width,
    viewport.Height,
    0, 0, 1);
    ShadowCaptureEffect.CurrentTechnique.Passes[0].Apply();
    
  9. 9. Render the lens flare test vertices inside the occlusion test to determine how many pixels were rendered:

    occlusionQuery.Begin();
    graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, queryVertices, 0, 2);
    occlusionQuery.End();
    occlusionQueryActive = true;
    
  10. 10. Complete the class by adding a Draw() method to render the glow:

    public void Draw()
    {
    if (lightBehindCamera || occlusionAlpha <= 0)
    return;
    Color color = Color.White * occlusionAlpha;
    Vector2 origin = new Vector2(glow.Width, glow.Height) / 2;
    float scale = glowScale * 2 / glow.Width;
    spriteBatch.Begin();
    spriteBatch.Draw(glow, lightPosition, null, color, 0,
    origin, scale, SpriteEffects.None, 0);
    spriteBatch.End();
    }
    

How it works...

XNA and the underlying DirectX infrastructure contain a rather handy little diagnostic tool in the form of the occlusion test. With this test, you can count how many pixels were filled when trying to render a particular portion of a scene.

We utilize this in the lens flare example by attempting to render a small rectangle across the opposite side of the scene from the player's viewpoint, and measuring how much of it is obscured by the scene's meshes. With this number, we adjust the opacity of the lens flare's glow texture up or down to simulate the sun disappearing either partially or completely behind an object.