I'm attempting to change RenderTargets at runtime, so I can draw some elements at runtime, manipulate them and then finally draw the texture to the screen. Problem is, the screen turns purple if I change the RenderTarget at runtime. Here's the code I've got in Draw:
RenderTarget2D tempTarget = new RenderTarget2D(GraphicsDevice, 128, 128, 1,
GraphicsDevice.DisplayMode.Format, GraphicsDevice.PresentationParameters.MultiSampleType,
GraphicsDevice.PresentationParameters.MultiSampleQuality, RenderTargetUsage.PreserveContents);
GraphicsDevice.SetRenderTarget(0, tempTarget);
GraphicsDevice.Clear(ClearOptions.Target, Color.SpringGreen, 0, 0);
GraphicsDevice.SetRenderTarget(0, null);
It doesn't seem to matter how I create the RenderTarget, if I do it at runtime (and I do need to create in-memory textures at runtime and draw on them with SpriteBatch) it results in an entirely purple screen. What can I do to fix this?
It looks like the best option is to create the RenderTarget somewhere other than Draw, draw to it during Update, save the resulting texture (and manipulate as necessary) then draw that texture during Draw.
I know this is late, but the solution is to write to the RenderTarget BEFORE you clear the screen and beginning drawing your other items.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.SetRenderTarget(_renderTarget);
//...
//Perform Rendering to the specified target
//...
GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.Clear(Color.CornflowerBlue);
//...
//Code that draws to the users screen goes here
//...
}
This should prevent you from rendering in the Update method as suggested by others, which is counter-intuitive in many aspects.
When spritebatch.End() is called objects are written to the backbuffer or in your case to tempTarget. To make the texture,
change the target
call begin
call all of the draws
end the spritebatch
set target back to null
then use the render2d
Related
I'm having difficulty figuring how to do something in XNA.
I have something like this:
public void Draw()
{
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
DrawFirstObject(); // Depth = 0.5f
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.Additive);
DrawSecondObject(); // Depth = 0.2f
spriteBatch.End();
}
Basically I need to have 2 different spritebatch begin calls one with AlphaBlend and one with Additive BlendState. But the problem is when I make this the drawn objects from the second call are always drawn on top of the first ones instead behind them where they need to be. I can't reformat my code so the second call is on the top and I need the keep the depth order. So I would be thankful if you have any suggestion.
As you are using transparent images in your first Draw call, ideally you should be using SpriteSortMode.BackToFront.
SpriteSortMode
What I am trying to do is use a spritesheet to create a blended terrain, save that as a texture, and then pass it into a shader for additional effects. However, I am having some issues with the render target:
RenderTarget2D someNewTexture = new RenderTarget2D(GraphicsDevice, 256, 256);
GraphicsDevice.SetRenderTarget(someNewTexture);
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
{
// draw some stuff
}
spriteBatch.End();
GraphicsDevice.SetRenderTarget(null);
Obviously, this creates a black background with my texture on top of it. However, when I actually draw that texture onto my main scene, I don't want the background, only the texture (if I don't use clear, I get the default purple color instead). How do I fix this?
Use a transparent clear color:
GraphicsDevice.Clear(Color.TransparentBlack);
If you handle blending correctly, it will result in transparent pixels not to be drawn.
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
namespace TileEngine
{
class Renderer : DrawableGameComponent
{
public Renderer(Game game) : base(game)
{
}
SpriteBatch spriteBatch ;
protected override void LoadContent()
{
base.LoadContent();
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
public override void Initialize()
{
base.Initialize();
}
public RenderTarget2D new_texture(int width, int height)
{
Texture2D TEX = new Texture2D(GraphicsDevice, width, height); //create the texture to render to
RenderTarget2D Mine = new RenderTarget2D(GraphicsDevice, width, height);
GraphicsDevice.SetRenderTarget(Mine); //set the render device to the reference provided
//maybe base.draw can be used with spritebatch. Idk. We'll see if the order of operation
//works out. Wish I could call base.draw here.
return Mine; //I'm hoping that this returns the same instance and not a copy.
}
public void draw_texture(int width, int height, RenderTarget2D Mine)
{
GraphicsDevice.SetRenderTarget(null); //Set the renderer to render to the backbuffer again
Rectangle drawrect = new Rectangle(0, 0, width, height); //Set the rendering size to what we want
spriteBatch.Begin(); //This uses spritebatch to draw the texture directly to the screen
spriteBatch.Draw(Mine, drawrect, Color.White); //This uses the color white
spriteBatch.End(); //ends the spritebatch
//Call base.draw after this since it doesn't seem to recognize inside the function
//maybe base.draw can be used with spritebatch. Idk. We'll see if the order of operation
//works out. Wish I could call base.draw here.
}
}
}
I solved a previous issue where I wasn't allowed to access GraphicsDevice outside the main Default 'main' class
Ie "Game" or "Game1" etc.
Now I have a new issue. FYi no one told me that it would be possible to use GraphicsDevice References to cause it to not be null by using the drawable class. (hopefully after this last bug is solved it doesn't still return null)
Anyways at present the problem is that I can't seem to get it to initialize as an instance in my main program.
Ie
Renderer tileClipping;
and I'm unable to use it such as
it is to be noted i haven't even gotten to testing these two steps below but before it compiled
but when those functions of this class were called it complained that it can't render to a null device.
Which meant that the device wasn't being initialized. I had no idea why. It took me hours to google this.
I finally figured out the words I needed.. which were "do my rendering in XNA in a seperate class"
now I haven't used the addcomponent function because I don't want it to only run these functions automatically
and I want to be able to call the custom ones.
In a nutshell what I want is:
*access to rendering targets and graphics device OUTSIDE default class
*passing of Rendertarget2D (which contain textures and textures should automatically be passed with a rendering target? )
*the device should be passed to this function as well OR the device should be passed to this function as a byproduct of passing the rendertarget (which is automatically associated with the render device it was given originally)
*I'm assuming I'm dealing with abstracted pointers here so when I pass a class object or instance, I should be recieving the SAME object , I referenced, and not a copy that has only the lifespan of the function running.
*the purpose for all these options: I want to initialize new 2d textures on the fly to customize tileclipping and even the X , y Offsets of where a WHOLE texture will be rendered, and the X and Y offsets of where tiles
will be rendered ON that surface.
This is why. And I'll be doing region based lighting effects per tile or even per 8X8 pixel spaces.. we'll see
I'll also be doing sprite rotations on the whole texture then copying it again to a circular masked
texture, and then doing a second copy for only solid tiles for masked rotated collisions on sprites.
I'll be checking the masked pixels for my collision, and using raycasting possibly to check for collisions on
those areas.
The sprite will stay in the center, when this rotation happens.
Here is a detailed diagram:
http://i.stack.imgur.com/INf9K.gif
I'll be using texture2D for steps 4-6
I suppose for steps 1 as well.
Now ontop of that, the clipping size (IE the sqaure rendered)
will be able to be shrunk or increased, on a per frame basis
Therefore I can't use the same static size for my main texture2d and I can't use just the backbuffer
Or we get the annoying flicker.
Also I will have multiple instances of the renderer class so that I can freely pass textures around
as if they are playing cards (in a sense) layering them ontop of eachother, cropping them how i want
and such.
and then using spritebatch to simply draw them at the locations I want.
Hopefully this makes sense, and yes I will be planning on using alpha blending but only after
all tiles have been drawn..
The masked collision is important and Yes I am avoiding using math on the tile rendering and instead resorting to image manipulation in video memory which is WHY I need this to work the way I'm intending it to work and not in the default way that XNA seems to handle graphics.
Thanks to anyone willing to help.
I hate the code form offered, because then I have to rely on static presence of an update function.
What if I want to kill that update function or that object, but have it in memory, but just have it temporarily inactive? I'm making the assumption here the update function of one of these gamecomponents is automatic ?
Anyways this is as detailed as I can make this post hopefully someone can help me solve the issue. Instead of tell me "derrr don't do it this wayyy" which is what a few people told me (but they don't understand the actual goal I have in mind)
I'm trying to create basically a library where I can copy images freely no matter the size, i just have to specify the size in the function then as long as a reference to that object exists it should be kept alive? right? :/ anyways..
Anything else? I Don't know. I understand object oriented coding but I don't understand this XNA
It's beggining to feel impossible to do anything custom in it without putting ALL my rendering code into the draw function of the main class
tileClipping.new_texture(GraphicsDevice, width, height)
tileClipping.Draw_texture(...)
I have to add a second viewport (= like splitscreen) to allow the player to see an event somewhere else on the current level.
Is there any possibility to draw the event area without redrawing every things already drew ?
[EDIT]
RenderTarget2D is the key. Thx User1459910 for all.
It almost worked.
New questions :
I've searched for a while and still don't find a tutorial about "Xna 2D camera with source and destination rectangle" if you have a link, i'd like to see it, please ♥
Currently, the drawing code looks like this :
protected override void Draw(GameTime gameTime)
{
/*
...
here is the code to "draw" in the renderTarget2D renderAllScene object
...
*/
//Let's draw into the 2 viewports
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, null, null, null, camera1.transform);
spriteBatch.Draw(renderAllScene, viewport1.Bounds, Color.White);
spriteBatch.End();
if (EventIsRunning)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, null, null, null, camera2.transform);
spriteBatch.Draw(renderAllScene, viewport2.Bounds, Color.White);
spriteBatch.End();
}
*The Viewport 1 is great. The camera follows the character but after moving the camera for a short distance, the map is cutted at 1280pixels i think. so it drew only 1280pixels of all the map. I don't know why. Maybe because i failed when i created renderAllScene = new RenderTarget2D. :x
renderAllScene = new RenderTarget2D(GraphicsDevice, GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight);
*For the Viewport 2 : I need the source rectangle. I'll try it tomorrow.
I'll assume you are making a 2D game with NOTHING 3D at all.
Here is what you could do:
You need to render the whole map, and all game objects that appear on it, on a Texture. If you don't know how to render to a Texture, here is the procedure:
Create a RenderTarget2D object
On the Draw function, before you render anything, you must call the graphicsDevice.SetRenderTarget() method, and set the RenderTarget2D you created.
After you are done rendering, call graphicsDevice.SetRenderTarget(null) to reset the render target to the default one. You must do it or you'll have problems!
To render the RenderTarget2D, simply use SpriteBatch.Draw((Texture2D)renderTarget2D, position, color), being "renderTarget2D" of course the name of the RenderTarget2D you created.
Then, you use two 2D Cameras. One will display where the hero is, and the other one will display the event area.
A 2D camera is basically a trick with Source and Destination rectangles. The trick is to use a Source Rectangle to define the area that displays the hero and the area around it and use the main Viewport as the Destination Rectangle, and use another Source Rectangle to define the event area and another Destination Rectangle as the second Viewport.
If you have doubts, google about "XNA 2D Camera", and research about Source and Destination rectangles on the MSDN's article for SpriteBatch.Draw().
My problem is that I'm trying to get two different RenderTarget2Ds drawn to the screen, but only the last-drawn one is shown. The code is similar to the following, and gets called twice, once by each instance of a class that needs to be drawn with the Draw method:
public override void Draw()
{
gfxDevice.SetRenderTarget(myRenderTarget);
gfxDevice.Clear(Thistle);
//pseudocode
foreach (something in a list)
{ spritebatch.begin();
spritebatch.DrawString(something);
spritebatch.end();
}
//reset to the backbuffer
gfxDevice.SetRenderTarget(null);
gfxDevice.Clear(backgroundColor) //Here is what I thought the offending line was
//draw the RenderTarget to backbuffer
spriteBatch.Begin();
spriteBatch.Draw(myRenderTarget, rect, this.backgroundColor);
spriteBatch.End();
What I thought would be the solution is to stop clearing the graphicsDevice each time the Draw() method gets called, but if I don't, everything but the freshly drawn rendertarget gets drawn an ugly purple. Like this:
The purple results from End()ing the spritebatch, I think, thanks to this question
What do I need to change in order for both RenderTargets get drawn properly, meaning both widgets get drawn, along with the proper Color.Thistle background?
Ideally, the screen would look like a combination of both: and
You're correct that you need to remove the gfxDevice.Clear(backgroundColor) call. If you draw to the backbuffer, and then clear the backbuffer, then obviously anything you drew before clearing isn't going to show up!
You want to clear the backbuffer before rendering anything, i.e. at the beginning of the frame.
EDIT
After taking a more thorough look at the source code provided, I think I know what's actually going on here--it's one of the tricky things about using render targets.
The weird purple you're seeing is not, in fact, caused by SpriteBatch.End(). That's the color that render targets are automatically cleared to when XNA resets their memory.
When does XNA clear out a render target's memory? Whenever that render target is set onto the graphics device as an active target. So whenever you call SetRenderTarget(null), XNA is obliterating the backbuffer's memory and resetting it to that lovely purple.
To avoid this, you need to draw all of your render targets before drawing anything to the backbuffer. Then, set the backbuffer as your active render target, and draw all of the render targets you updated previously in a single pass.