What I would like to do is to be able to draw a specific set of sprites within a spriteBatch with additive blending. The problem is that the draw order that they're drawn in needs to be preserved and I can't draw everything else in the SpriteBatch with additive blending, so I can't just do this:
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
//Draw some stuff here
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.Additive);
//Draw stuff with additive blending here
spriteBatch.End();
So my solution would be to write a shader to do what I need and just do this:
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
//Draw some stuff here
foreach(EffectPass pass in AdditiveShader.CurrentTechnique.Passes)
{
pass.Apply()
//Draw stuff with additive shader applied here
}
spriteBatch.End()
But pass.Apply() is literally doing nothing. Even if I try just using a BasicEffect and have it rotate a few degrees, it's doing nothing. The only way I can get it to do anything is to call it like this:
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend,
null, null, null, AdditiveShader);
Then it actually does something to sprites, but that doesn't really help me because I want to only apply it to specific sprites and still retain the draw order.
What am I doing wrong when using pass.Apply()? Is there a way to draw a set of sprites with additive blending and another set with alpha blending and still keep the draw order? Any help would be greatly appreciated. Thanks.
EDIT: For clarification, I'm working in 2D.
OK, so what I found (Thanks to Shawn Hargreaves - http://blogs.msdn.com/b/shawnhar/archive/2010/04/05/spritebatch-and-custom-renderstates-in-xna-game-studio-4-0.aspx) is that I can use GraphicsDevice.BlendState = BlendState.Additive to change the BlendState on the fly.
So what I did was this:
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Alpha);
//Draw with alpha blending here
GraphicsDevice.BlendState = BlendState.Additive;
//Draw specific sprites with additive blending here
GraphicsDevice.BlendState = BlendState.Alpha; //Revert the blendstate to Alpha
//Draw more stuff here with alpha blending
spriteBatch.End();
The problem is that the SpriteSortMode for the spriteBatch has to be set to Immediate or GraphicsDevice.BlendState = BlendState.Additive will do nothing.
So I suppose I'll have to use a custom DepthStencilState to replicate the functionality of SpriteSortMode.FrontToBack.
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.
I'm creating a game where the player is drawn in the middle of the screen. In order to modify the direction that the player is looking towards, I'm using the following lines of code:
In the player class, in the Draw() function:
//Draw player
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(currentTexture, position, null, Color.White, rotation, centre, 1.0f, SpriteEffects.None, 0f);
}
I'm calling that function in the main class, inside the Draw() function:
//Draw
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
//Draw player
player.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
The player is not being drawn, and no error is showing up.
The weird thing is that I've used this exact same code for drawing in a game I made two days ago and it works fine.
If i use the following lines instead, the player is drawn, but i'm left without the ability to modify player rotation unless I use different textures for each direction:
spriteBatch.Draw(currentTexture, position, Color.White);
I'm guessing that the problem is with were you set the origin. Your centre vector. Try using Vector2.Zero instead of centre and see what happens.
For your center vector you probably want the center of the texture. The Vector2 center paramenter in SpriteBatch.Draw is relative to the texture coordinates. This means that to rotate around the center of your object you would need to have a value of
new Vector2(texture.Width/2, texture.Height/2)
I would recommend creating a method in your player class (Or GameObject class if you're doing that sort of thing) that returns your center with a texture coordinate relative Vector2 (like the one I provided above)! Happy Coding!
Okay so hopefully a simple fix that I'm just not seeing: I'm trying to implement transparency on a Billboard for an explosion, however the sprite stays solid where there is a pixel not defined transparent in the png file itself. I'm trying to change the transparency by multiplying Color.White by the transparency (a float between the value of 0 and 1)
spriteBatch.Begin(0, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, RasterizerState.CullNone, basicEffect);
Vector3 viewSpaceTextPosition = Vector3.Transform(this.position, camera.View * invertY);
spriteBatch.Draw(Texture, new Vector2(viewSpaceTextPosition.X, viewSpaceTextPosition.Y), null, Color.White * this.Transparency /*Here's where I try to set the transparency of the image drawn*/, 0, new Vector2(Texture.Bounds.Center.X, Texture.Bounds.Center.Y), this.Scale, SpriteEffects.None, viewSpaceTextPosition.Z);
spriteBatch.End();
Are there any obvious mistakes? Or is XNA for Windows Phone 7 just incapable of rendering transparent things in 3D?
Since you're using BasicEffect instead of the built-in sprite effect, I'm pretty sure you need to set the material alpha:
basicEffect.Alpha = this.Transparency;
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