Getting a Texture2D from a RenderTarget2D object in XNA 4.0 - c#

Im just experimenting with pixel shader. I found a nice blur effect and now Im trying to create an effect of blurring an image over and over.
HOW I want to do that: I want to render my image hellokittyTexture in a RenderTarget applying the blur effect, then replace hellokittyTexture with the result of that render and do it over and over again every Draw iteration:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.SetRenderTarget(buffer1);
// Begin the sprite batch, using our custom effect.
spriteBatch.Begin(0, null, null, null, null, blur);
spriteBatch.Draw(hellokittyTexture , Vector2.Zero, Color.White);
spriteBatch.End();
GraphicsDevice.SetRenderTarget(null);
hellokittyTexture = (Texture2D) buffer1;
// Draw the texture in the screen
spriteBatch.Begin(0, null, null, null, null, null);
spriteBatch.Draw(hellokittyTexture , Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
But I get this error "The render target must not be set on the device when it is used as a texture." Because hellokittyTexture = (Texture2D) buffer1; is not copying the texture but the reference to the RenderTarget (basicly they are the same object after the asignation)
Do you know a nice way to get the Texture inside the RenderTarget? or a more elegant way to do what Im trying?

spriteBatch.Draw(hellokittyTexture , Vector2.Zero, Color.White);
In this line, you're drawing the texture... to itself... That can't happen.
Assuming buffer1 and hellokittyTexture have been properly initialized, replace this line:
hellokittyTexture = (Texture2D) buffer1;
with this:
Color[] texdata = new Color[hellokittyTexture.Width * hellokittyTexture.Height];
buffer1.GetData(texdata);
hellokittyTexture.SetData(texdata);
This way, hellokittyTexture will be set as a copy of buffer1, rather than a pointer to it.

Just a little addition to McMonkey answer:
I get this error: "You may not call SetData on a resource while it is actively set on the GraphicsDevice. Unset it from the device before calling SetData."
I solved it by creating a new texture. Dont know if this could be a performance problem, but its working now:
Color[] texdata = new Color[buffer1.Width * buffer1.Height];
buffer1.GetData(texdata);
hellokittyTexture= new Texture2D(GraphicsDevice, buffer1.Width, buffer1.Height);
hellokittyTexture.SetData(texdata);

Related

XNA Texture2D Billboarding appears as a white block

Very odd problem, when I try and draw my billboard sprite it always appears as a white block, changing the .draw color property still draws it as white, it also doesn't matter it I use a jpeg, or transparent png.
[EDIT]
So I'm now trying to use a Viewport instead of a basic effect to just get an x and y screen coordinate, I'll fix any scaling issue later, however now the image stays in the exact same spot (on the screen, it doesn't change position based on the camera) and doesn't get any bigger or smaller based on how far away it is
My new billboard rendering function:
public void Draw(Camera camera, GraphicsDevice device, SpriteBatch spriteBatch, Texture2D Texture)
{
Viewport viewport = new Viewport(new Rectangle(0, 0, 800, 480));
Vector3 viewSpaceTextPosition = viewport.Project(this.position, camera.Projection, camera.View, camera.World);
spriteBatch.Begin();
spriteBatch.Draw(Texture, new Vector2(viewSpaceTextPosition.X, viewSpaceTextPosition.Y), null, Color.White, 0, new Vector2(Texture.Bounds.Center.X, Texture.Bounds.Center.Y), this.Scale, SpriteEffects.None, viewSpaceTextPosition.Z);
spriteBatch.End();
device.RasterizerState = RasterizerState.CullCounterClockwise;
device.BlendState = BlendState.Opaque;
device.DepthStencilState = DepthStencilState.Default;
}
So is my use of Viewport wrong or do I just need to use it's information differently in spriteBatch.Draw()?
I think this should do the trick using viewport project... taking two projection points and calculating its distance you get a value affected by depth... so if it's deeper that value will be smaller.
public void Draw(Camera camera, GraphicsDevice device, SpriteBatch spriteBatch, Texture2D Texture)
{
Vector3 pos1= device.Viewport.Project(
this.position,
camera.Projection, camera.View, camera.World);
Vector3 pos2= device.Viewport.Project(
this.position+ Vactor3.UnitY*10,
camera.Projection, camera.View, camera.World);
Vector2 pos = new Vector2(pos1.X, pos1.Y);
Vector2 origin = new Vector2(Texture.Bounds.Center.X, Texture.Bounds.Center.Y);
float Scale = Vector3.Distance(pos1, pos2) * CustomRatio;
spriteBatch.Begin();
spriteBatch.Draw(Texture, pos, null, Color.White, 0,
origin, Scale, SpriteEffects.None, 0);
spriteBatch.End();
device.RasterizerState = RasterizerState.CullCounterClockwise;
device.BlendState = BlendState.Opaque;
device.DepthStencilState = DepthStencilState.Default;
}
In other hand... your previous code seems to be extracted from a source that drinks from this article made by the guy behind Xna that explain how to use basiceffect to draw billboards in 3D with spritebatch...
http://blogs.msdn.com/b/shawnhar/archive/2011/01/12/spritebatch-billboards-in-a-3d-world.aspx
I hope it helps you
Figured it out, you have to enable textures on the effect and then set effect.Texture to the Texture2D you want to use just before calling spriteBatch.Begin() in the draw function

XNA screenshot shows pre-Bloom, not final render

I have a windows platform game coded in C# XNA 4.0 using the Reach graphics settings. My project is based on the GameStateManagement sample but I later added Bloom and spriteSheet/spriteBatch functionality to it.
I desire to have a screenshot saved of the final screen output. However, when I save my screenshot it only shows the render before Bloom was applied and before my HUD text is displayed (which I draw after the Bloom). I have my screenshot saved at the end of my Draw method, after these two processes.
I have tried all kinds of things. Andrew's answer here Take screen shot in XNA was helpful and does save out an image; however, it is not saving out the final render.
I have a feeling it has something to do with the bloom process or maybe the spritebatch.
Here is my code:
example {
public override void Draw(GameTime gameTime)
{
ScreenManager.GraphicsDevice.SetRenderTarget(sceneRenderTarget);
// Clear the screen to black
ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
Color.Black, 0, 0);
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
// then i draw all my game stuff
spriteBatch.End();
#region Post-Processing & Bloom
ScreenManager.GraphicsDevice.SamplerStates[1] = SamplerState.LinearClamp;
// Pass 1: draw the scene into rendertarget 1, using a
// shader that extracts only the brightest parts of the image.
bloomExtractEffect.Parameters["BloomThreshold"].SetValue(
Settings.BloomThreshold);
DrawFullscreenQuad(sceneRenderTarget, renderTarget1,
bloomExtractEffect,
IntermediateBuffer.PreBloom);
// Pass 2: draw from rendertarget 1 into rendertarget 2,
// using a shader to apply a horizontal gaussian blur filter.
SetBlurEffectParameters(1.0f / (float)renderTarget1.Width, 0);
DrawFullscreenQuad(renderTarget1, renderTarget2,
gaussianBlurEffect,
IntermediateBuffer.BlurredHorizontally);
// Pass 3: draw from rendertarget 2 back into rendertarget 1,
// using a shader to apply a vertical gaussian blur filter.
SetBlurEffectParameters(0, 1.0f / (float)renderTarget1.Height);
DrawFullscreenQuad(renderTarget2, renderTarget1,
gaussianBlurEffect,
IntermediateBuffer.BlurredBothWays);
// Pass 4: draw both rendertarget 1 and the original scene
// image back into the main backbuffer, using a shader that
// combines them to produce the final bloomed result.
ScreenManager.GraphicsDevice.SetRenderTarget(null);
EffectParameterCollection parameters = bloomCombineEffect.Parameters;
parameters["BloomIntensity"].SetValue(Settings.BloomIntensity);
parameters["BaseIntensity"].SetValue(Settings.BaseIntensity);
parameters["BloomSaturation"].SetValue(Settings.BloomSaturation);
parameters["BaseSaturation"].SetValue(Settings.BaseSaturation);
ScreenManager.GraphicsDevice.Textures[1] = sceneRenderTarget;
Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
DrawFullscreenQuad(renderTarget1,
viewport.Width, viewport.Height,
bloomCombineEffect,
IntermediateBuffer.FinalResult);
#endregion
spriteBatch.Begin();
// Draw HUD
spriteBatch.End();
if (screenShotTake)
{
using (FileStream fs = File.Open(#"screenshot" + (screenshotNumber) + #".png", FileMode.OpenOrCreate))
{
// Right here I try to save out the screen shot Texture2D
sceneRenderTarget.SaveAsPng(fs, (int)viewportSize.X, (int)viewportSize.Y); // save render target to disk
}
}
}
#region PostProcess & Bloom
void DrawFullscreenQuad(Texture2D texture, RenderTarget2D renderTarget,
Effect effect, IntermediateBuffer currentBuffer)
{
ScreenManager.GraphicsDevice.SetRenderTarget(renderTarget);
DrawFullscreenQuad(texture,
renderTarget.Width, renderTarget.Height,
effect, currentBuffer);
}
void DrawFullscreenQuad(Texture2D texture, int width, int height,
Effect effect, IntermediateBuffer currentBuffer)
{
if (showBuffer < currentBuffer)
{
effect = null;
}
spriteBatch.Begin(0, BlendState.Opaque, null, null, null, effect);
spriteBatch.Draw(texture, new Rectangle(0, 0, width, height), Color.White);
spriteBatch.End();
}
}
The problem is here:
ScreenManager.GraphicsDevice.SetRenderTarget(null);
Because of this line, you draw the effects to the back buffer, but the render target you're saving is left alone. You see the results of the effects because it's drawn directly, but what you save is not what you've drawn to the back buffer. To fix this, draw the effects to another RenderTarget, and then draw that as a single texture to the back buffer. Obviously this is another draw call to process, but it's a minimal cost. By doing that, you can then grab the texture from the new RenderTarget and save it however you like.

Colours are wrong when using RenderTarget2D in XNA

I've begun making a 2D sprite based Windows game using XNA. I'm not very experienced with it yet but I'm learning. Let me start off with saying that I'm using XNA game studio 3.1, I haven't updated to 4.0 (yet).
What I'm trying to accomplish is to be able to draw all my sprites to a fixed size buffer which is then, at the end of the rendering pass, scaled to the size of the actual backbuffer and then drawn to that. I'm not sure how multiple resolutions are usually supported, but it seemed like an adequate solution to me.
I tried to achieve this by using a RenderTarget2D object to draw all my stuff to, then get the Texture2D from that and draw that to the backbuffer.
My code looks like this:
private RenderTarget2D RenderTarget;
private DepthStencilBuffer DepthStencilBufferRenderTarget;
private DepthStencilBuffer DepthStencilBufferOriginal;
private SpriteBatch SpriteBatch;
protected override void Initialize()
{
base.Initialize();
RenderTarget = new RenderTarget2D(GraphicsDevice, 1920, 1080, 1, SurfaceFormat.Single);
DepthStencilBufferRenderTarget = new DepthStencilBuffer(GraphicsDevice,
1920, 1080, GraphicsDevice.DepthStencilBuffer.Format);
DepthStencilBufferOriginal = GraphicsDevice.DepthStencilBuffer;
SpriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.DepthStencilBuffer = DepthStencilBufferRenderTarget;
GraphicsDevice.SetRenderTarget(0, RenderTarget);
GraphicsDevice.Clear(Color.Black);
SpriteBatch.Begin();
//drawing all stuff here
SpriteBatch.End();
GraphicsDevice.DepthStencilBuffer = DepthStencilBufferOriginal;
GraphicsDevice.SetRenderTarget(0, null);
GraphicsDevice.Clear(Color.Black);
Texture2D output = RenderTarget.GetTexture();
SpriteBatch.Begin();
Rectangle backbuffer = new Rectangle(0, 0, m_Options.DisplayWidth, m_Options.DisplayHeight);
SpriteBatch.Draw(output, backbuffer, Color.White);
SpriteBatch.End();
base.Draw(gameTime);
}
The problem I encounter is that the colours are all wrong. Below is a picture that shows two screenshots: the first is a screenshot of how it's supposed to look (I wrote my own scaling algorithm before which simply scaled each sprite by itself) and to the right it's how it looks when using the RenderTarget2D.
Does anyone know what I'm doing wrong?
Oh I figured out what I did wrong (kind of). I think I used the wrong SurfaceFormat when creating the new RenderTarget2D instance. I used SurfaceFormat.Single but I should have used SurfaceFormat.Color.
I figured this out by taking a look at the PresentationParameters of the GraphicsDevice through a watch while debugging before setting the new RenderTarget:
GraphicsDevice.PresentationParameters.BackBufferFormat
This told me it was set to SurfaceFormat.Color. So I replaced SurfaceFormat.Single with SurfaceFormat.Color and now it works as it should.

How to draw 2D pixel-by-pixel in XNA?

I'm trying to draw on the screen pixel-by-pixel using XNA, but am having problems with resources. I thought the best way would be to have 1 texture that updates every frame, but I'm having trouble updating it. Here's what I've got so far, just as a test:
Texture2D canvas;
Rectangle tracedSize;
UInt32[] pixels;
protected override void Initialize()
{
tracedSize = GraphicsDevice.PresentationParameters.Bounds;
canvas = new Texture2D(GraphicsDevice, tracedSize.Width, tracedSize.Height, false, SurfaceFormat.Color);
pixels = new UInt32[tracedSize.Width * tracedSize.Height];
base.Initialize();
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
pixels[100] = 0xFF00FF00;
canvas.SetData<UInt32>(pixels, 0, tracedSize.Width * tracedSize.Height);
spriteBatch.Begin();
spriteBatch.Draw(canvas, new Rectangle(0, 0, tracedSize.Width, tracedSize.Height), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
When Draw() is called the second time, I get the following error:
"The operation was aborted. You may not modify a resource that has been set on a device, or after it has been used within a tiling bracket."
If I try to make a new Texture2D in Draw(), I quickly get an out of memory error. This is for Windows Phone. It seems like I'm trying to do it the wrong way, what other options do I have to make it work?
Try setting GraphicsDevice.Textures[0] = null before you call SetData. Depending on the effect you're after there may be a more performant method, you could also consider Silverlights WriteableBitmap.
Edit: This is the code I tested in the emulator:
Texture2D canvas;
Rectangle tracedSize;
UInt32[] pixels;
protected override void Initialize()
{
tracedSize = GraphicsDevice.PresentationParameters.Bounds;
canvas = new Texture2D(GraphicsDevice, tracedSize.Width, tracedSize.Height, false, SurfaceFormat.Color);
pixels = new UInt32[tracedSize.Width * tracedSize.Height];
base.Initialize();
}
Random rnd = new Random();
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.Textures[0] = null;
pixels[rnd.Next(pixels.Length)] = 0xFF00FF00;
canvas.SetData<UInt32>(pixels, 0, tracedSize.Width * tracedSize.Height);
spriteBatch.Begin();
spriteBatch.Draw(canvas, new Rectangle(0, 0, tracedSize.Width, tracedSize.Height), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
You basically need to do as it asks in the exception:
To ensure that the texture is not set on the graphics device, put this at the end of Draw:
GraphicsDevice.Textures[0] = null;
To ensure you are not drawing inside a tiling bracket, do not use SetData inside of Draw at all. Move the call to SetData into Update.
Bonus info: Your out-of-memory error is because you are not releasing the unmanaged resources that Texture2D allocates (the garbage collector can't track them, so it doesn't know you're running out of memory). You need to call Dispose on the texture. However making a new texture each frame is a bad idea anyway (there's no way to avoid the performance and memory fragmentation problems it causes).
Never create or modify a texture in Draw() ** ever.**
The SpriteBatch.Draw() call expects the GraphicsDevice buffer to contain all of the texture data (by extension to the GPU buffer), any changes still happening(from Update and IsRunningSlowly == true) at this point will cause tearing when rendered.
The workaround of GraphicsDevice.Textures[0] = null; blocks the Draw call and the game until transfer to the GPU is complete, thus slowing the entire game loop.
Reuse a Texure2D object declared at the class level.

XNA 4.0 - Using spriteBatch and basicEffect lags

I am currently working on a game, in which we need to combine the use of DrawUserIndexedPrimitives and normal spriteBatch.Draw. Not combined as we use them at the same time, but we first have to draw some 2d sprites using spriteBatch, where after we disable spriteBatch to enable basicEffect and draw the primitives, and at last enable spriteBatch again. The code below shows the section of the code, where the problem is occurring.
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, null, cam.get_transformation(GraphicsDevice));
levelController.Draw(spriteBatch);
if (gameReady)
{
foreach (GameObject.GameObject go in gameObjects)
{
go.Draw(spriteBatch);
}
spriteBatch.End();
foreach (GameObject.GameObject go in gameObjects)
{
if (go is GameObject.Enemy.Enemy)
{
GameObject.Enemy.Enemy enemy = (GameObject.Enemy.Enemy)go;
basicEffect = new BasicEffect(graphics.GraphicsDevice);
basicEffect.VertexColorEnabled = true;
basicEffect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, enemy.GetCollisionTriangle.Triangle, 0, 3, enemy.GetCollisionTriangle.Indices, 0, 1);
}
}
}
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, null, cam.get_transformation(GraphicsDevice));
If the code below gets quoted out, the lag stops.
basicEffect = new BasicEffect(graphics.GraphicsDevice);
basicEffect.VertexColorEnabled = true;
basicEffect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, enemy.GetCollisionTriangle.Triangle, 0, 3, enemy.GetCollisionTriangle.Indices, 0, 1)
Can it really be that we can't both use spriteBatch and basicEffect without the game lags a lot? it has been tested on 3 different computers, from a very old laptop, to a brand new gamer pc.
The game is unplayable with the lag that occurs.
I think you should create the basiceffect elsewhere then your drawing routine. If I guess right you will use the same basiceffect all the time, so put it into inicializing, since "new" per every frame (edit: in a foreach for each and every object) can cost performance.

Categories

Resources