RenderTarget

Introduction

The RenderTarget property can be set so a given Layer renders to the RenderTarget rather than to screen. The RenderTarget property can be used for a number of reasons:

  • To create textures used in a multi-pass rendering system, such as to apply layer-wide effects like color tinting or bloom

  • To improve the performance of complicated, static visuals on a Layer by eliminating the management of multiple objects and multiple draw calls with a single object and draw call (for a collection of objects which do not require every-frame activity)

If a Layer's RenderTarget is set, then the Layer will does render directly to the screen. The contents of the Layer are rendered to the RenderTarget which must then be rendered to the screen using another graphical object such as a FlatRedBall Sprite, a Gum Sprite, or SpriteBatch.

Setting RenderTarget in the FRB Editor

To set a RenderTarget in the FRB Editor:

  1. Create a RenderTarget object

  2. Create a Layer instance

  3. Set the RenderTarget property on the layer to the previously-created RenderTarget

As mentioned above, the contents of the Layer are rendered to its RenderTarget instead of the screen. The easiest way to see the contents of the RenderTarget is to add a Sprite and use the RenderTarget as its Texture.

Code Example

The following code shows how a RenderTarget2D can be created and assigned to the Layer.RenderTarget property. This Layer contains a single Circle which is drawn with a separate unlayered Sprite:

using Microsoft.Xna.Framework.Graphics;

public partial class GameScreen
{
    // This Layer will contain all of the FRB objects which should be 
    // drawn on the render target:
    Layer layer;
    // This is the render target which will contain the final rendering 
    // of all FRB objects on 'layer'
    RenderTarget2D renderTarget;

    void CustomInitialize()
    {
        // Instantiate the layer:
        layer = SpriteManager.AddLayer();

        // Define the layer. This uses the current game's resolution, but it could be any size
        renderTarget = new RenderTarget2D(
            FlatRedBallServices.GraphicsDevice,
            FlatRedBallServices.GraphicsOptions.ResolutionWidth,
            FlatRedBallServices.GraphicsOptions.ResolutionHeight);
        layer.RenderTarget = renderTarget;

        var circle = new Circle();
        ShapeManager.AddToLayer(circle, layer);
        circle.Visible = true;
        circle.Radius = 40;

        var sprite = SpriteManager.AddSprite(renderTarget);
        // to show that the Sprite is rendering the circle, we'll squash it:
        sprite.Width = 300;
        sprite.Height = renderTarget.Height;
    }

    void CustomActivity(bool firstTimeCalled)
    {
    }

    void CustomDestroy()
    {
        SpriteManager.RemoveLayer(layer);
        renderTarget.Dispose();
    }

    static void CustomLoadStaticContent(string contentManagerName)
    {
    }
}

Note that the above code creates a Layer in code instead of creating one in the FRB editor. This is done purely to keep the example short - Layer instances created in the FRB editor can be used as well.

Update Frequency and One-Time Renders

RenderTarget instances can be updated every-frame, or can be rendered just one time (if the contents of the render target never change). The following code example shows how to create a RenderTarget which is used as the target only one time. This example differs in the following ways compared to the previous example:

  • The layer is only needed temporarily until the render is done.

  • The Renderer needs a temporary camera to perform rendering. While this example only uses a single Layer, multiple layers could be used to sort objects.

  • Any rendered objects (such as entities, sprites, or shapes) are only needed for the Draw call and can be destroyed afterwards.

public partial class GameScreen
{
    // This is the render target which will contain the final rendering of all FRB objects on 'layer'
    RenderTarget2D renderTarget;

    void CustomInitialize()
    {
        // This layer holds whatever we want drawn
        var temporaryLayer = new Layer();
            
        // Define the layer. This uses the current game's resolution, but it could be any size
        renderTarget = new RenderTarget2D(
            FlatRedBallServices.GraphicsDevice,
            FlatRedBallServices.GraphicsOptions.ResolutionWidth,
            FlatRedBallServices.GraphicsOptions.ResolutionHeight);
        temporaryLayer.RenderTarget = renderTarget;

        // This example renders a rectangle, but the layer could have anything
        var rectangle = new AxisAlignedRectangle();
        rectangle.Visible = true;
        rectangle.Width = 50;
        rectangle.Height = 40;
        ShapeManager.AddToLayer(rectangle, temporaryLayer);

        // Rendering requires a camera. We'll create a temporary one (don't add it to the SpriteManager)
        var temporaryCamera = new Camera(null, renderTarget.Width, renderTarget.Height);
        // We only want the camera to draw the layer
        temporaryCamera.DrawsWorld = false;
        temporaryCamera.UsePixelCoordinates();
        temporaryCamera.Z = 40;
        temporaryCamera.AddLayer(temporaryLayer);

        Renderer.DrawCamera(temporaryCamera, null);

        // Anything which was rendered can be destroyed. In this case it's just a 
        // rectangle, but it could be entities, Gum objects, etc
        ShapeManager.Remove(rectangle);

        // This sprite will render our layer
        var sprite = SpriteManager.AddSprite(renderTarget);
        sprite.TextureScale = 1 ;
        // rotate it to show that the rectangle is drawn on the render target
        // (normally rectangles can't be rotated visually)
        sprite.RotationZ = Microsoft.Xna.Framework.MathHelper.ToRadians(45);
    }

    void CustomActivity(bool firstTimeCalled)
    {
    }

    void CustomDestroy()
    {