Related
I am creating my own game graphics engine. I have looked into using others like Unity, but they don't fit my needs. Anyway, I am using OpenTK (This is a 2D game), and the issue is that when i draw a texture to the screen, then draw a quad to the screen, the color darkens on the texture. Here is the method I am using to draw a texture:
public void Texture(int ID, Vector2 size, Vector2 pos, Vector2 texSize, Vector2 texPos)
{
pos.Y = -pos.Y;
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, ID);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(texPos.X, texPos.Y);
GL.Vertex2(pos.X, pos.Y);
GL.TexCoord2(texPos.X + texSize.X, texPos.Y);
GL.Vertex2(pos.X + size.X, pos.Y);
GL.TexCoord2(texPos.X + texSize.X, texPos.Y + texSize.Y);
GL.Vertex2(pos.X + size.X, pos.Y - size.Y);
GL.TexCoord2(texPos.X, texPos.Y + texSize.Y);
GL.Vertex2(pos.X, pos.Y - size.Y);
GL.End();
}
I am inverting the Y because I am used to the Windows Forms coordinate system, where going down is y++. I am calling it like this:
Texture(backdropTextureID, new Vector2(1f, 1f), new Vector2(-0.5f, -0.5f), new Vector2(1f, 1f), new Vector2(0f, 0f));
As expected, if there is nothing else being drawn, it draws a texture with the GL id of backdropTextureID in the center of the screen. When I draw a colored quad also though, the texture is darkened. Here is the method I am using for drawing a quad:
public void Quad(Vector2 pos1, Vector2 pos2, Vector2 pos3, Vector2 pos4, Color color1, Color color2, Color color3, Color color4)
{
GL.Disable(EnableCap.Texture2D);
pos1.Y = -pos1.Y;
pos2.Y = -pos2.Y;
pos3.Y = -pos3.Y;
pos4.Y = -pos4.Y;
GL.Begin(PrimitiveType.Quads);
GL.Color3(color1);
GL.Vertex2(pos1);
GL.Color3(color2);
GL.Vertex2(pos2);
GL.Color3(color3);
GL.Vertex2(pos3);
GL.Color3(color4);
GL.Vertex2(pos4);
GL.End();
}
Again, inverting the Y, for the reason stated above. Also, notice, I am enabling EnableCap.Texture2D in the method for drawing a texture, and disabling it when I draw a colored quad. I am calling the quad method like this:
Quad(new Vector2(0.0f, 0.0f), new Vector2(0.5f, 0.0f), new Vector2(0.5f, 0.5f), new Vector2(0.0f, 0.5f), Color.Gray, Color.Gray, Color.Gray, Color.Gray);
If anyone could help me, thank you in advance. Basically: How do I stop a texture from darkening after drawing a colored quad in C# OpenTK?
For anyone whose having this problem, I figured it out. The same color I was giving to the colored quad i gave to the texture. You just need to add
GL.Color3(Color.Transparent);
to the start of the texture drawing method.
I have the following code in my MonoGame/Farseer Physics project:
_ball = BodyFactory.CreateCircle(World, 1f, 400f);
_ball.BodyType = BodyType.Dynamic;
_ballSprite = new Sprite(ScreenManager.Content.Load<Texture2D>("Common/ball"));
In my Draw method:
ScreenManager.SpriteBatch.Draw(_ballSprite.Texture, ConvertUnits.ToDisplayUnits(_ball.Position), null, Color.White, _ball.Rotation, _ballSprite.Origin, 1f, SpriteEffects.None, 0f);
The problem is that my texture is 120px by 120px, but when it renders on screen, the _ball body is larger than it in size. What can I do to resize the texture to fit exactly the width and height of the _ball body?
I think I have solved this mystery at last. I needed to compute the scale (_ballTextureScale that is, which was the part that I was stuck on):
var radius = 1f;
_ball = BodyFactory.CreateCircle(World, radius, 400f);
_ball.BodyType = BodyType.Dynamic;
var rectangleTexture = ScreenManager.Content.Load<Texture2D>("Common/ball");
_ballTextureScale = ConvertUnits.ToDisplayUnits(radius * 2) / rectangleTexture.Width;
This converts the radius * 2 (total width of the circle) to display units which it seems the rectangle texture is already in. Then when I go to draw the body, I just needed to make use of this _ballTextureScale property:
ScreenManager.SpriteBatch.Draw(_ballSprite.Texture, ConvertUnits.ToDisplayUnits(_ball.Position), null, Color.White, _ball.Rotation, _ballSprite.Origin, _ballTextureScale, SpriteEffects.None, 0f);
If this is an incorrect approach, I am all ears to a better method for accomplishing this task, however it seems to work very well for my needs.
I'm trying to rotate my shot Texture by 180 degress, but when I do the following:
spriteBatch.Draw(TexTiro, Position, null, Color.White, (float)(180), new Vector2(), Vector2.One, SpriteEffects.None, 0f);
the texture appears like this:
What am I doing wrong?
It is because the rotation is in radians.
You can use MathHelper.ToRadians() to easily convert degrees to radians: see here
spriteBatch.Draw(TexTiro, Position, null, Color.White, MathHelper.ToRadians(180), new Vector2(), Vector2.One, SpriteEffects.None, 0f);
In XNA the unit is radians not degrees. so 180 would be (float)Math.PI
Here's my problem
I am using a depth control but now i have different sprites that need different blendstates
how can i change this if I already started the spritebatch.begin(...)
I was trying to change spritebatch.graphicsDevice.blendstate but it doesn't seems to be working
I cannot turn a black area into transparency
Thank you
Still seems it cannot save the layer order between them by using that code...
I guess the problem might be in using object.draw(spritebatch) and then draw inside the method.
I think theres a problem because i have 2 classes each one with a draw method with input spritebatch
this is what im doing
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.Additive);
d.drawAttack(gameTime, spriteBatch);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
drawObjects(gameTime, spriteBatch);
charactersDraw(gameTime, spriteBatch); // same class as the d.drawAttack
spriteBatch.End();
they both draw but the layer !BETWEEN both spritebatche! doesn't seems to make any difference
the layerdepth works fine with characters and draw objects... only doesn't work with drawAttack
You need to do multiple SpriteBatch.Begin() .. End() calls, each with a different BlendState. You can still preserve your layerDepth among them.
For example:
batch.Begin(SpriteSortMode.FrontToBack, BlendState.Additive);
batch.Draw(tex1, sprite1, null, Color.White, 0.0f, Vector2.Zero, 1.0f,
SpriteEffects.None, layer1);
batch.Draw(tex2, sprite2, null, Color.White, 0.0f, Vector2.Zero, 1.0f,
SpriteEffects.None, layer2);
batch.End();
//new blend state, new begin...end
batch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
batch.Draw(tex3, sprite3, null, Color.White, 0.0f, Vector2.Zero, 1.0f,
SpriteEffects.None, layer3);
batch.Draw(tex4, sprite4, null, Color.White, 0.0f, Vector2.Zero, 1.0f,
SpriteEffects.None, layer4);
batch.End();
I'm writing a simple 2D game for Windows Phone 7 using the XNA Framework.
Basically there are multiple items that the user can drag. I create their texture dynamically by drawing stuff on a RenderTarget2D and then just draw the RenderTarget2D afterwards. I use the stencil buffer to draw a part of a bigger texture onto the render target.
Code snippet
util.GraphicsDevice.SetRenderTarget(result);
util.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.Stencil | ClearOptions.DepthBuffer, Color.Transparent, 0, 0);
// The "mask"
spriteBatch.Begin(SpriteSortMode.Deferred, util.DontWriteColorsState, null, util.StencilAlways, null, alphaTestEffect);
spriteBatch.Draw(maskTexture, destination, Color.White);
spriteBatch.End();
// The actual texture
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, util.StencilKeepWhere1, null, alphaTestEffect);
spriteBatch.Draw(bigTexture, destination, source, Color.White);
spriteBatch.End();
util.GraphicsDevice.SetRenderTarget(null);
(util is just an instance of a helper class which I use in order to not have to copy-paste the same code everywhere, it contains the DepthStencilState objects and some other stuff.)
I would like to create a drop shadow effect behind these textures. I'd like to draw the shadow on the render target.
Unfortunately Windows Phone 7 doesn't support custom shader effects so I can't use any examples from the internet. So I decided to try to implement it in software. But I'm completely inexperienced with this kind of thing, so the result was both slow and ugly.
What I did in software was this:
Created a stroke around the mask texture and drew it with a low opacity
Created a stroke around the previous stroke and drew it with a low opacity
... This step was repeated the same number of times as the size of the drop shadow
However, it was VERY slow and basically unacceptably slow.
So the question is
Is it possible to create a dropshadow or a dropshadow-ish effect using the built-in effects in XNA? Also, if not, is there an algorithm that can create a nice-looking drop shadow in software?
Thanks in advance for your answers! :)
EDIT:
I'm taking about this kind of drop shadow:
Obviously, this is just an illustration, I don't want it to be exactly this size. :)
I took the liberty of adjusting your solution, #Venemo, and here is what I've come up with as a good start:
public Texture2D CreateBlurredTexture(Texture2D originalTexture, SpriteEffects effects)
{
var device = originalTexture.GraphicsDevice;
var rt = new RenderTarget2D(device, originalTexture.Width/2, originalTexture.Height/2);
var rt2 = new RenderTarget2D(device, originalTexture.Width, originalTexture.Height);
Color shadowColor = Color.Lerp(Color.Black, Color.Transparent, 0.9f);
using (var spriteBatch = new SpriteBatch(device))
{
device.SetRenderTarget(rt);
device.Clear(Color.Transparent);
spriteBatch.Begin();
spriteBatch.Draw(originalTexture, new Rectangle(0, 0, rt.Width, rt.Height), null, shadowColor, 0, Vector2.Zero, effects, 0f);
spriteBatch.Draw(originalTexture, new Rectangle(1, 1, rt.Width - 2, rt.Height - 2), null, shadowColor, 0, Vector2.Zero, effects, 0f);
spriteBatch.Draw(originalTexture, new Rectangle(2, 2, rt.Width - 4, rt.Height - 4), null, shadowColor, 0, Vector2.Zero, effects, 0f);
spriteBatch.Draw(originalTexture, new Rectangle(3, 3, rt.Width - 6, rt.Height - 6), null, shadowColor, 0, Vector2.Zero, effects, 0f);
spriteBatch.Draw(originalTexture, new Rectangle(4, 4, rt.Width - 8, rt.Height - 8), null, shadowColor, 0, Vector2.Zero, effects, 0f);
spriteBatch.End();
device.SetRenderTarget(rt2);
device.Clear(Color.Transparent);
spriteBatch.Begin();
spriteBatch.Draw(rt, new Rectangle(0, 0, rt2.Width, rt2.Height), Color.White);
spriteBatch.End();
device.SetRenderTarget(null);
}
return rt2;
}
What it does is creates copies of the texture and adds opacity with each draw, while going towards the center. After this is done, you can offset it and increase its size to create a bigger shadow effect. This should accomplish the task depending on your texture.
To draw a shadow draw the original render target in pure black, maybe with some transparency, offset slightly from the original position.
Next just draw again, as you were before to render the texture over the shadow.
Regarding your edit, create a texture for an edge and a texture for a corner, then draw the drop shadow as 4 edges and 4 corners.
This is what I came up with, eventually.
It is a bit hacky, of course but it sort of does what I needed when I asked the question.
public static Texture2D CreateBlurredTexture(Texture2D originalTexture, SpriteEffects effects)
{
var device = originalTexture.GraphicsDevice;
var rt4 = new RenderTarget2D(device, originalTexture.Width / 4, originalTexture.Height / 4);
using (var rt2 = new RenderTarget2D(device, originalTexture.Width * 3 / 2, originalTexture.Height * 3 / 2))
using (var rt3 = new RenderTarget2D(device, originalTexture.Width / 2, originalTexture.Height / 2))
using (var spriteBatch = new SpriteBatch(device))
{
device.SetRenderTarget(rt2);
device.Clear(Color.Transparent);
spriteBatch.Begin();
spriteBatch.Draw(originalTexture, new Rectangle(0, 0, rt2.Width, rt2.Height), null, Color.White, 0, Vector2.Zero, effects, 0f);
spriteBatch.End();
device.SetRenderTarget(rt3);
device.Clear(Color.Transparent);
spriteBatch.Begin();
spriteBatch.Draw(rt2, new Rectangle(0, 0, rt3.Width, rt3.Height), Color.White);
spriteBatch.End();
device.SetRenderTarget(rt4);
device.Clear(Color.Transparent);
spriteBatch.Begin();
spriteBatch.Draw(rt3, new Rectangle(0, 0, rt4.Width, rt4.Height), Color.White);
spriteBatch.End();
device.SetRenderTarget(null);
}
return rt4;
}