1
votes

I'm working in XNA and I hit the following problem: My goal was to have a number of different textures be drawn to a texture and then draw the final texture to the screen with a given alpha value.

The problem is that the texture I draw to is full screen and filled with a color rather than transparency, so it blocks out everything behind it.

Everything is 2d, here is my code:

public void Draw(SpriteBatch sb)
            {
            if (opacity > 0)
                {
                gd.SetRenderTarget(formTexture);
                gd.Clear(Color.Transparent);
                for (int i=0; i<formItems.Count; i++)
                    {
                    ((FormItem)formItems[i]).draw(sb);
                    }
                sb.Begin(SpriteSortMode.Immediate, BlendState.Additive);
                    for (int i=0; i<formItems.Count; i++)
                        {
                        FormItem fi = (formItems[i] as FormItem);
                        if (fi.glow != null)
                            {
                            sb.Draw(fi.glow, new Rectangle((int) fi.location.X + fi.width/2 - fi.glow.Width/2, (int) fi.location.Y + fi.height/2 - fi.glow.Height/2, fi.glow.Width, fi.glow.Height), Color.White);
                            }
                        }
                sb.End();
                gd.SetRenderTarget(null);
                sb.Begin();
                sb.Draw(formTexture, new Rectangle(0, 0, gd.Viewport.Width, gd.Viewport.Height), Color.White*opacity);
                sb.End();
                }
            }

formTexture is a RenderTarget2D object, gd is the graphics device. This is how formTexture is initialized:

formTexture = new RenderTarget2D(this.gd, this.windowWidth, this.windowHeight);
1

1 Answers

1
votes

The problem that you are experiencing is that setting a render target - and the backbuffer counts as a render target - will clear that render target.

At least if RenderTargetUsage.DiscardContents (MSDN) is set. It's the default because it's the only one that performs well on Xbox 360, where preserving the contents requires an expensive copy. (And it's the default on Windows just to keep the behaviour the same cross-platform).

Your backbuffer is being cleared to the "whoops this is a fresh buffer" colour (usually purple) on this line:

gd.SetRenderTarget(null);

There are two ways to solve this:

The preferred way is to order all your rendering so that you set up your render targets at the beginning of the frame, and then draw your scene to the backbuffer afterwards. Never touching a render target more than once.


The other way is to attach an event to GraphicsDeviceManager.PreparingDeviceSettings that sets the backbuffer to use RenderTargetUsage.PreserveContents:

void SetBackbufferPreserveContents(object sender,
                                   PreparingDeviceSettingsEventArgs e)
{
    e.GraphicsDeviceInformation.PresentationParameters.RenderTargetUsage
            = RenderTargetUsage.PreserveContents;
}

// in your game constructor:
graphics.PreparingDeviceSettings += SetBackbufferPreserveContents;

You should only use this second method if your game exclusivly targets Windows.