简体   繁体   中英

How can I create a drop shadow effect on my textures in XNA for Windows Phone?

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.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM