Friday, February 3, 2012

Game Elements 3: Drawing to a Texture

Introduction

In this tutorial I am going to explain how in XNA we take what would normally be displayed on the screen and instead draw it to a texture. This will be very useful in future tutorials when we want to add complicated effects. Some things that this can be used for is whole screen shaders, mini maps, split screen, etc. I will also show you how to save this image as a file, so you can make screen shots.

For simplicity I will be using the code from the first game elements tutorial as a starting point so that you can see what parts are specifically related to this example.

Creating a Blank Texture
The first step is creating our blank texture, known in XNA as a render target. Add this local variable to the top of your main class:
private RenderTarget2D cameraViewRenderTarget;

Now before we can start drawing to it we need to initialize it to the correct size. We will initialize it to the size of the screen which we can get from the viewport. Add this somewhere in your load content method:
Viewport viewport = GraphicsDevice.Viewport;
cameraViewRenderTarget = new RenderTarget2D(GraphicsDevice, viewport.Width, viewport.Height);
 
Drawing to our Texture
Since we are now drawing to a render target it probably would make more sense to divide up our draw method. I will create a new method for drawing to the render target. Simply take everything from your draw method and put it in this function. Then, at the start of the method set the graphics device's render target to the one we just created, and at the end of the method set the render target to null.
private void DrawTileMapScene()
{
 GraphicsDevice.SetRenderTarget(cameraViewRenderTarget);
 GraphicsDevice.Clear(Color.CornflowerBlue);

 spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend,
  SamplerState.LinearClamp, DepthStencilState.Default,
  RasterizerState.CullCounterClockwise,
  null, Camera.GetTransformation(GraphicsDevice));

 for (int x = 0; x < 10; x++)
 {
  for (int y = 0; y < 10; y++)
  {
   spriteBatch.Draw(sandtile,
    new Vector2(x * sandtile.Width, y * sandtile.Height), null, Color.White, 0,
    Vector2.Zero, 1, SpriteEffects.None, 0);
  }
 }

 spriteBatch.Draw(character, position, null, Color.White, 0,
  new Vector2(character.Width / 2, character.Height / 2),
  1, SpriteEffects.None, 0);

 spriteBatch.End();

 GraphicsDevice.SetRenderTarget(null);
}
 
Drawing Texture to the Screen
Now we go back to our original draw method and add this new method to the beginning. After you run this function we need to clear the graphics device since it still contains what we just had drawn. Once this is done the screen will be blank but the render target will have what used to be drawn to the screen. To prove it, we will treat the render target like a texture and draw it to the screen as we would any other texture.

protected override void Draw(GameTime gameTime)
{
 DrawTileMapScene();

 GraphicsDevice.Clear(Color.Transparent);

 Texture2D tex = (Texture2D)cameraViewRenderTarget;
 spriteBatch.Begin();
 spriteBatch.Draw(tex, new Vector2(0,0), null, Color.White, 0,
  Vector2.Zero, 1, SpriteEffects.None, 0);
 spriteBatch.End();

 base.Draw(gameTime);
}

The result is that you can now manipulate everything that would have been drawn to the screen as a single texture rather than manipulating each individual part.

Writing Texture to a File
On a bit of a side note, something we can do with this texture is save it as a file. This will allow us to take screenshots. 

Add this code somewhere in your update method. Now when you press 'p' it will save this texture as a file called "screenshots" in the programs execution folder "..\bin\x86\Debug", or you can specify an absolute path like "c:\screenshot.png" to save it to c.
if (Keyboard.GetState().IsKeyDown(Keys.P))
{
 FileStream filesteam = new FileStream("screenshot.png", FileMode.Create);
 cameraViewRenderTarget.SaveAsPng(filesteam,
  cameraViewRenderTarget.Width, cameraViewRenderTarget.Height);
 filesteam.Close();
}

Download Package
Source code can be found in the download package for this tutorial:
Download Package

Extensions
In my some of my future tutorials I will explain some uses of this, however if you find a particularly interesting use let me know in the comments.

No comments:

Post a Comment