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:
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.
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;
}
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.