Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm looking for a way to render every non-transparent pixel in a sprite solid white (to 'flash' the sprite white when the player takes damage etc). This is on Windows Phone 7.

I was using a very simple custom shader to do this under XNA 3.1, but WP7 doesn't support these and finding an alternative's proving difficult.

I'd appreciate any help or suggestions. I'd rather not have to manually create a solid white copy of every sprite in my game though.

share|improve this question
1  
Okay, I did find a way, but it isn't pretty. Let me clean it up a bit and I'll update my answer. –  David Gouveia Mar 1 '12 at 19:26
add comment

1 Answer

up vote 8 down vote accepted

Method 1

You don't have to create a solid white version of every sprite in your game manually - you could just as well automate the process at load time. In other words, you can use Texture2D.GetData() to access the pixels of your texture (and retrieve them as a simple Color[]), iterate over them replacing any non-transparent pixel with solid white , and then save it to a new texture using and Texture2D.SetData().

Method 2

I tried playing around with BlendState but couldn't find a way to render it all white, at least not within Reach profile's limitations. But if someone knows a way let me know. What I did find, however, was a way to to do it using the stencil buffer and the built-in AlphaTestEffect class. The idea is the following:

  1. Create a backbuffer that has a stencil buffer.
  2. Clear the stencil buffer to zero.
  3. Draw the sprites you'd like to tint white and whenever they pass the alpha test, set the stencil buffer at that location to 1.
  4. Draw a white quad covering the entire screen, but only where the value of the stencil buffer is 1.

Here's the code I used:

(Step 1) First make sure the backbuffer is being created with room for a stencil buffer:

graphics = new GraphicsDeviceManager(this) { PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8 };

(Step 2) Create a 1x1 white texture that will be scaled to fill the entire screen:

private Texture2D pixel;
pixel = new Texture2D(GraphicsDevice, 1, 1);
pixel.SetData(new[] { Color.White });

(Step 3) And now the hard part - rendering it. Well, not really hard, but requires two DepthStencilState objects and one AlphaTestEffect object. You should create these only once.

// Clear stencil buffer
GraphicsDevice.Clear(ClearOptions.Stencil, Color.Black, 0f, 0);

// Prepare the alpha test effect object (create it only once on initilization)
AlphaTestEffect alphaTestEffect = new AlphaTestEffect(GraphicsDevice)
{
    DiffuseColor = Color.White.ToVector3(), 
    AlphaFunction = CompareFunction.Greater, 
    ReferenceAlpha = 0, World = Matrix.Identity, 
    View = Matrix.Identity, 
    Projection = Matrix.CreateTranslation(-0.5f, -0.5f, 0) * 
    Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 0, 1)
};

// Prepare the first DepthStencilState (create only once, or put it in a static class)
DepthStencilState beforeDepthStencilState = new DepthStencilState
{
    StencilEnable = true, 
    StencilFunction = CompareFunction.Always,
    StencilPass = StencilOperation.Replace, 
    ReferenceStencil = 1
};

// Draw your sprites using the structures above
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, beforeDepthStencilState, null, alphaTestEffect);
spriteBatch.Draw(sprite, new Vector2(300, 150), Color.White);
spriteBatch.End();

// Prepare the second DepthStencilState (create only once, or put it in a static class)
DepthStencilState afterDepthStencilState = new DepthStencilState
{
    StencilEnable = true, 
    StencilFunction = CompareFunction.Equal, 
    ReferenceStencil = 1
};

// Draw a full screen white quad with the structure above
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, afterDepthStencilState, null);
spriteBatch.Draw(pixel, GraphicsDevice.Viewport.Bounds, Color.White);
spriteBatch.End();

And the result:

enter image description here

share|improve this answer
    
Amazing! Thanks a lot for putting so much effort into this, David. Shame there's not a simpler solution, but this should do the trick nicely. –  Ryan McD Mar 2 '12 at 10:06
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.