Showing posts with label pixel shaders. Show all posts
Showing posts with label pixel shaders. Show all posts

Monday, 13 May 2013

2D Shadows Shader

This article is based on the following theories and samples. My implementation was merely ported across to C++/DirectX for learning purposes. All credit goes to the authors.

Recommended Reading:

Introduction


A couple of weeks ago, I published a post discussing dynamic 2D shadows, the theory regarding how one could create such an effect and provided a DirectX 9 sample for just that. This week I'll be discussing the same subject using a different method, this time based around graphics shaders.

I've already made a brief post dicussing how pixel shaders can be used to achieve some basic effects in XNA, but I'll recap the important bits for this entry. A shader is a small program that runs on the GPU (as opposed to the CPU like normal programs) and is typically used to perform shading and various other post-processing effects on a scene.

Vertex shaders are run once for each vertex in a primitive and usually deal with transforming 3D world coordinates into 2D screen coordinates, as well as processing any data needed for later stages of rendering. Pixel shaders, also known as fragment shaders, are run for each pixel in the rasterised output and deal with the actual shading of pixels as well as other post-processing effects.

The advantage of using shaders for this application is that shaders are much better at graphical operations, making them ideal for processing shadows in our scene. Additionally it frees up some CPU cycles, allowing us to use them for processing other aspects (such as game logic).

Shadow mapping

Scene from a light's view, Wikipedia

It's worth going over how shadows are typically processed in a regular 3D scene to give some insight into why certain steps are taken in this theory. The core idealogy for 3D shadow maps is to create a depth map from the light's perspective, which can then be used to determine whether pixels are in front of the nearest occluding object, or behind it.

One of the basic ways this could be done is to render the scene from the light's perspective, transforming points into the light's local space instead of the camera's. This way the Z value of each pixel can be used as a measure of depth and can be output using the colour (where pixels at one extreme are brighter than the other).

When rendering the final scene, pixels can then be transformed into the light's local space and compared against the value stored in the relevant position of the depth map. Closer pixels receive a colour value from the light and further ones simply receive an ambient value to simulate being in shadow.

Application in 2D


Unfortunately, rendering a scene in 2D from a light's perspective is not as straightforward as it is in 3D. You could probably find some way to convert X and Y coordinates into X and Z coordinates, but that's not the subject of this article. The theory outlined relies on three main steps:

First, the scene is rendered such that each pixel of an object which cast shadows (caster) is coloured based on its distance from the light source. Next, the scene is distorted such that all of the potential rays from the light source are aligned on the horizontal axis. Finally, this distorted image is compressed into a strip which stores the smallest (closest) values for each row, creating a depth map. This map can then be queried when rendering the scene to determine shading values, much like a traditional 3D shadow map.

Distance map

"Distance" map

The initial phase is relatively simple. Each object in your scene just needs to be transformed into the light's local space and then shaded based on its distance from the source. In my case I also scale the scene since my light radiuses may not always be the same size as the shadow map texture.

Here I utilise the fact that the position output from the vertex shader is in the [-1, 1] range, where (0, 0) is the centre of the render target and also where the light is. I simply pass a copy of this calculated position out as a texture coordinate (since Pixel Shader Model 2.0 doesn't support a position input semantic) and then use the length of that to determine the distance of the pixel.

Shader Code:
VS_OUTPUT DefaultVS(float4 position : POSITION, float2 tex : TEXCOORD0)
{
 VS_OUTPUT output;

 output.position = mul(position, g_worldViewProjMatrix);
 output.worldPos = output.position;
 output.tex = tex;

 return output;
}

float4 DistancePS(float2 worldPos : TEXCOORD1) : COLOR0
{
 float depth = length(worldPos);
 return float4(depth, depth, depth, 1);
}

Distorted map


Distorted map
The next phase is an intermediate step where we distort the distance map such that the rays from the light source are aligned along one axis instead of emitting radially. The vertical axes are also rotated 90 degrees and stored in a separate RGB channel such that all rays are horizontal.

The way this works is that coordinates are transformed into the [-1, 1] range (where 0, 0 is the centre of the texture) and the scene is divided into four quadrants (up, down, left and right). One coordinate is then scaled based on the absolute value of the other depending on which quadrant the point is in, causing points to tend towards the origin. These coordinates are then transformed back into the [0, 1] range and used as texture sampling coordinates for our scene.

Distortion plot in horizontal axis
This is difficult to explain in words, so I've provided a simple graph showing some example horizontal distortion coordinates. The brighter, dashed lines are the input coordinates after being transformed into the [-1, 1] range and the darker, solid lines are the output coordinates.

As we can see, when the x value is 1, the y value remains the same. However as x tends towards 0, so too does y. This means that even though we're drawing pixels along a horizontal row, we're actually sampling the texture along a diagonal emitting from the centre (the position of the light). A similar process happens in the vertical quadrants, except that the other coordinate (x) is scaled instead.


Shader Code:
float4 DistortPS(float2 texCoord : TEXCOORD0) : COLOR0
{
 // Transform coordinates into (-1, 1) domain.
 float u0 = (texCoord.x * 2) - 1;
 float v0 = (texCoord.y * 2) - 1;

 // As U approaches 0, V also tends towards 0.
 v0 *= abs(u0);

 // Convert back to (0, 1) domain.
 v0 = (v0 + 1) / 2;

 float2 coords = float2(texCoord.x, v0);

 // Store values in horizontal and vertical axes in separate channels.
 float h = tex2D(inputSampler, coords).r;
 float v = tex2D(inputSampler, coords.yx).r;

 return float4(h, v, 0, 1);
}

Depth map


Depth map, stretched for clarity
The next phase is to compress the distortion map into a 2-pixel wide strip, such that only the closest values in each quadrant remain. If you compare the stretched image to the right to the previous step, you'll see how the closest coordinates (green, red, black) are preserved in each half.

In Catalin's implementation, he goes about this by repeatedly downsampling his texture by half, taking the lowest values from pairs of pixels. One of the problems I see with this approach is that you need to have a different render target for each pass, all the way down to the final 2-pixel wide depth map. Another problem is that you end up sampling the same values multiple times. Not only do you have to sample and compare the entire texture once for the first downsample, but then you have to continually sample the remaining values until you get down to 2. This means that you almost end up sampling each pixel twice for the entire texture.

My implementation differs in that it has more passes, but each pixel is only sampled and compared once for the most part. It also only uses the input and output render targets, without any intermediate ones in between. The way it works is that we repeatedly sample a chunk of 8 pixels from the texture and output the lowest value from that batch of 8. We then then use a minimum blending operation to choose the smallest value between the destination and source pixels and write that to the render target. Finally, we offset the sampling position and repeat this for every chunk in the current section until we've sampled all the pixels and obtained the minimum value.

C++ Code:
// Convert distorted scene into a depth map.
device->SetRenderTarget(0, m_depthMapSurface);
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MIN);

float largeStep = 1.0f / m_shadowResolution;
float smallStep = 1.0f / 2.0f;

effect->SetTechnique(handles[HANDLE_HREDUCTION]);
effect->SetTexture(handles[HANDLE_INPUTTEXTURE], m_tempMap);
effect->SetFloat(handles[HANDLE_HREDUCTIONSTEP], largeStep);

int chunks = (int)(m_shadowResolution / 2) / maxReductionSamples;

device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
 D3DCOLOR_RGBA(255, 255, 255, 255), 1.0f, 0);

effect->Begin(&passes, NULL);

for (int c = 0; c < chunks; c++)
{
 // Calculate start point for this chunk set.
 float start = (largeStep / 2) + (c * (largeStep * 8));
 effect->SetFloat(handles[HANDLE_HREDUCTIONSTART], start);
 effect->CommitChanges();
   
 for (unsigned int i = 0; i < passes; i++)
 {
  effect->BeginPass(i);
  device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
  effect->EndPass();
 }
}

effect->End();
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
Shader Code:
VS_HREDUCT_OUTPUT HReductionVS(
 float4 position : POSITION, float2 tex : TEXCOORD0)
{
 VS_HREDUCT_OUTPUT output;

 output.position = mul(position, g_worldViewProjMatrix);

 for (int i = 0; i < HREDUCTION_MAX_STEPS; i++)
 {
  output.tex[i] = float2(tex.x + g_hReductionStart
   + (g_hReductionStep * i), tex.y);
 }

 return output;
}

float4 HReductionPS(VS_HREDUCT_OUTPUT input) : COLOR0
{
 float2 colour = (float2)tex2D(inputSampler, input.tex[0]);

 for (int i = 1; i < HREDUCTION_MAX_STEPS; i++)
  colour = min(colour, (float2)tex2D(inputSampler, input.tex[i]));

 return float4(colour, 0, 1);
}

Shadow map


Final shadow map for a light
The final phase is to draw our completed shadow map. This step is relatively straightforward, for each pixel we get the relevant value from our depth map and compare the distances from the centre. If the current pixel is closer then we shade it in brightness, if it's further then we shade it as black. We also simulate light attenuation by using the distance of the lit pixels to determine how bright they are.

The only not-so-straightforward bit is determining and getting the relevant depth map value. For this, we simply transform the x and y coordinates into the [-1, 1] range again and compare their absolute values. We then apply the inverse distortion operation to either x or y, depending on which was greater, and sample the relevant depth map channel at those coordinates. Another important thing to note is the inclusion of a 1-point bias to the pixel distance to prevent shadow acne.

Shader Code:
float4 ShadowMapPS(float2 texCoord : TEXCOORD0) : COLOR0
{
 float distance = length(texCoord - 0.5f) * 2;
 distance *= 512.0f;
 distance -= 1;

 // Transform coordinates into (-1, 1) domain.
 float u0 = (texCoord.x * 2) - 1;
 float v0 = (texCoord.y * 2) - 1;

 float shadowMapDistance = abs(u0) > abs(v0)
  ? GetShadowDistanceH(texCoord) : GetShadowDistanceV(texCoord);

 // Render as black or white depending on distance and depth value.
 float light = distance < shadowMapDistance * 512.0f
  ? 1 - (distance / 512.0f) : 0;

 float4 colour = light;
 colour.a = 1;

 return colour;
}

Final light map


Final scene light map
After we have each light's shadow map, we follow a similar process to the previous dynamic 2D shadows method. We draw each shadow map at its relevant position and colour on the light map texture and then we render the light map over the top of our scene using the Zero and Source Colour blending modes for the Source and Destination textures respectively. As before, this means that for each pixel in our scene, only the colours in the light map are rendered at that position.


Source code and comments


I greatly prefer this method over the previous one. Not only is the result of better quality and easier to adjust and add new features to, but the potential FPS gain is massive. The previous implemention of dynamic 2D lights had an FPS count of a little over 800 in release mode. This new approach has roughly 1400 FPS in release, almost double the previous amount. Granted, the GPU in my development computer is pretty decent, but it goes to show what sort of gains you can get by offloading some work from the CPU.

An optimisation that could be made is that currently each light owns its own shadow, distortion and depth map textures. At least for the distortion and depth maps, these textures could be shared among multiple lights with the same shadow resolution since the values are only needed during the rendering of a light's shadow map and can be discarded as soon as it is complete. Another optimisation is that static lights could skip redrawing their shadow map until they move. Only 2 of the 6 lights in the sample ever move, so it would be relatively simple to add a check to skip redrawing if the light hasn't moved since the last call.

In Catalin's sample, he also applies a distance-based gaussian blur to simulate the way that shadows tend to get sharper the closer they are to the light source (as well as smoothing out the rough edges on shadows). My implementation skips this step, as I was aiming for a result comparable to my previous sample. See his article for an implementation for this effect.

Credits:
George - Wizard Sprite
Studio Evil - Ground Texture

Source:
Sample Archive
DirectX 9 Redistributable

Thursday, 3 January 2013

Pixel Shaders for Dummies

The goal of this post will be to give an extremely quick introduction to pixel shaders in XNA, what sort of basic things you can do with them and some other information.

What is a Pixel Shader?

A shader is a program or function that runs on the GPU instead of the CPU. The name comes from its primary use, which is to shade textures on a pixel-by-pixel basis. Typically, they're used for rendering lighting and shadows in a scene, however in recent years they've also been used for producing special effects and other forms of post processing.

The main advantage of pixel shaders is that most of the work is done on the GPU instead of the CPU, and that the GPU is very fast at performing these sorts of operations. In addition, their versatility allows all sorts of effects to be created with the right knowledge.

What sort of effects can you create?

Almost any effect you can think of in a graphics application, such as Photoshop, can be created using shaders. For the subject of this post though, we'll be focusing on alpha blending or "masking".

Image courtesy of Survarium.

Alpha blending is a texture editting technique where certain areas of a texture are made transparent or "masked" using another texture. In the example above, the image on the left had the mask in the middle applied which resulted in the image on the right. The black areas on the mask made the picture transparent, while the white areas did not affect it. The grey areas in between produced a gradual fade from opaque to transparent.

Writing the Shader

Before we can start writing our game code, we need to create our shader. This shader's going to be relatively simple, it will have two external variables (the screen's texture and the mask) and will multiply each pixel in the screen by the equivalent pixel's brightness in the mask.

First, we need to create an effect file. Right-click on your solution's Content project and click "Add", followed by "New Item...". Select "Effect File", call it what you want (alpha_map.fx, for example) and click "Add". XNA automatically generates some boiler plate code, but not much of it is directly relevant to this example so delete it.

Now that we have a clean slate, we're going to create our two external variables.
// The texture we are trying to render.
uniform extern texture ScreenTexture;  
sampler screen = sampler_state 
{
    Texture = <ScreenTexture>;
};

// The texture we are using to mask.
uniform extern texture MaskTexture;  
sampler mask = sampler_state
{
    Texture = <MaskTexture>;
};
This creates two paramaters for our shader, ScreenTexture and MaskTexture. The former is used to reference the current, unshaded texture and the latter will allow us to initialise and even change the mask during runtime.

Next, we need a quick function to determine the brightness of a pixel in our mask texture.
float MaskPixelBrightness(float4 inMaskColour)
{
    // Get the min and max values from the pixel's RGB components.
    float maxValue = max(inMaskColour.r, max(inMaskColour.g, inMaskColour.b));
    float minValue = min(inMaskColour.r, min(inMaskColour.g, inMaskColour.b));

    // Return the average of the two.
    return (maxValue + minValue) / 2;
}
Because shader pixel data is typically stored as a float4 RGBA value, there's no way to directly determine how "bright" the pixel is. This implementation uses the HSL "bi-hexcone" model as described on Wikipedia, which is simply the average of the largest and smallest values from the RGB components.

Now we move onto our main shader function, the part which will actually do the per-pixel shading.
float4 PixelShaderFunction(float2 inCoord: TEXCOORD0,
    float4 inColour : COLOR0) : COLOR
{
    // Get the colour value at the current pixel.
    float4 colour = tex2D(screen, inCoord);

    // Get the brightness value at the current mask pixel.
    float maskBrightness = MaskPixelBrightness(tex2D(mask, inCoord));

    // If a Color argument has been passed into SpriteBatch.Draw(), we
    // multiply the value by it to hue. Then we multiply by the brightness
    // value of the mask to hide the appropriate pixels.
    colour.rgba = colour.rgba * inColour.rgba * maskBrightness;

    // Return the updated pixel.
    return colour;
}
The first couple of lines get the RGBA value of the texture and the brightness value of the mask respectively. The next line multiplies the pixel value by the Color argument (if one has been passed in SpriteBatch.Draw()) and the brightness value. This has the effect of allowing textures to be hued as normal, as well as masking the texture.

Finally, we write the technique for the shader to use when rendering.
technique
{
    pass P0
    {
        // Compile as version 2.0 for compatability with Xbox.
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
A shader can have multiple techniques and each technique can have multiple passes. For our simple shader though, only one technique and pass are needed. We compile as Pixel Shader 2.0 so that the shader is compatible with Xbox as well as PC.

Creating a passive mask

Now that we have our shader, we can start writing some game code. First we'll add the appropriate content variables and load them in LoadContent().
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Effect alphaShader;
        RenderTarget2D wipeRender;
        Texture2D planetTexture;
        Texture2D wipeTexture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// 
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// 
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load our various content.
            alphaShader = Content.Load<Effect>("alpha_map");
            planetTexture = Content.Load<Texture2D>("planet");
            wipeTexture = Content.Load<Texture2D>("wipe");

            // Initialise the render target to be the same size as the planet.
            wipeRender = new RenderTarget2D(graphics.GraphicsDevice,
                planetTexture.Width, planetTexture.Height);

            // Set the render target to be blank initially.
            graphics.GraphicsDevice.SetRenderTarget(wipeRender);
            graphics.GraphicsDevice.Clear(Color.White);

            // Go back to drawing to the screen again.
            graphics.GraphicsDevice.SetRenderTarget(null);
        }
Most of this should be pretty explanatory except for the RenderTarget2D variable. This is going to be our "canvas" when we're creating our mask and will then get passed in as the MaskTexture paramater for our shader. As such we want the render target to be the same size as our planet and we initially want it to be completely white, so that the texture is drawn in full by default.

Next, we're going to write a method to draw our render target. Add this to the bottom of your Game1 class.
        /// 
        /// Update the wipe texture position.
        /// 
        private void UpdateWipeMask()
        {
            // Tell the graphics device we want to draw to our seperate render target,
            // instead of the screen, until we tell it otherwise.
            graphics.GraphicsDevice.SetRenderTarget(wipeRender);

            // Clear the render for the update.
            graphics.GraphicsDevice.Clear(Color.Transparent);

            spriteBatch.Begin();

            // Draw the mask in its current position.
            spriteBatch.Draw(wipeTexture, new Rectangle(-planetTexture.Width,
                    0, planetTexture.Height * 5, planetTexture.Height),
                Color.White);

            spriteBatch.End();

            // Go back to drawing to the screen again.
            graphics.GraphicsDevice.SetRenderTarget(null);
        }
This method simply sets the graphics device to draw to the render target we set up earlier, then draws our mask texture onto it. The X offset is because my mask texture has a white square at the start, so I offset slightly so that the gradient part of the mask is shown instead. My mask is also smaller than my planet texture, so I scale the rectangle to fit it.

Now we call the method in Update() and add the code to Draw() which will allow us to render our masked texture.
        /// 
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// 
        /// Provides a snapshot of timing values.
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            UpdateWipeMask();

            base.Update(gameTime);
        }

        /// 
        /// This is called when the game should draw itself.
        /// 
        /// Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // Set the value of the alpha mask.
            alphaShader.Parameters["MaskTexture"].SetValue(wipeRender);

            // Start SpriteBatch using our custom effect.
            spriteBatch.Begin(SpriteSortMode.Immediate,
                BlendState.AlphaBlend, null, null, null, alphaShader);

            // Draw the planet texture, including any mask in alphaShader.
            spriteBatch.Draw(planetTexture, new Vector2(208, 24), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }
As you can see, this step is relatively straight forward once we've done all the preliminary coding. Each frame the draw method is called to set our mask texture and then this texture is passed during drawing.


This type of mask isn't really that useful though, as you can just bake the mask into the texture you're using rather than having to use pixel shaders. Additionally, some people will notice that having the render target update every frame isn't very optimal for our uses. This leads us onto our next topic.

Creating an active mask

Having a mask that moves is much more practical and will allow us to achieve effects which are not so simple otherwise. This time we'll create a wipe effect, where the texture gradually fades off from left to right and back on again. Thankfully, making the mask move is not so difficult given what we already have.

First, we need to add a variable to our game which will store the position of the mask.
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Effect alphaShader;
        RenderTarget2D wipeRender;
        Texture2D planetTexture;
        Texture2D wipeTexture;

        int wipeX = 0;
Now we need to modify our mask drawing method to update the position each time it draws.
        /// 
        /// Update the wipe texture position.
        /// 
        private void UpdateWipeMask()
        {
            // Wipe from left to right.
            wipeX += 6;

            // Each time we reach the end of the texture, reset to loop the effect.
            if (wipeX >= 0) wipeX = -planetTexture.Width * 4;

            // Tell the graphics device we want to draw to our seperate render target,
            // instead of the screen, until we tell it otherwise.
            graphics.GraphicsDevice.SetRenderTarget(wipeRender);

            // Clear the render for the update.
            graphics.GraphicsDevice.Clear(Color.Transparent);

            spriteBatch.Begin();

            // Draw the mask in its current position.
            spriteBatch.Draw(wipeTexture,
                new Rectangle(wipeX, 0, planetTexture.Width * 5, planetTexture.Height),
                Color.White);

            spriteBatch.End();

            // Go back to drawing to the screen again.
            graphics.GraphicsDevice.SetRenderTarget(null);
        }
You'll notice the only real difference to the method is that the wipeX variable we declared is increased and that value is used as the X position when drawing. I also clamp the variable so that when it reaches 0 (where the left side of the mask is parallel with the left side of the texture) it resets back to the end.


That's all there really is to it. You can improve the performance by moving the call to UpdateWipeMask() to Draw() instead, that way you only update the mask every time it's drawn. The only problem is that this then means that the speed of the wipe is dependant on the framerate (slower machines won't draw as quickly and therefore won't update the mask as fast). This would be suitable for background effects where the speed isn't so important though (like a glowing effect on a powerup).

Creating a dynamic mask

The final piece of the puzzle is to have the mask react to player input. For this part we will make it so that a crater is added to the mask when the player clicks on the planet, hiding that area of it. Since this is slightly different than the last two approaches, it will take a bit more work to implement.

First, we need to add some additional variables and initialise them.
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Effect alphaShader;
        RenderTarget2D craterRender;
        RenderTarget2D wipeRender;
        Texture2D planetTexture;
        Texture2D craterTexture;
        Texture2D wipeTexture;

        MouseState currentMouse;
        MouseState previousMouse;

        Random random = new Random();
        int wipeX = 0;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            IsMouseVisible = true;
        }

        /// 
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// 
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load our various content.
            alphaShader = Content.Load<Effect>("alpha_map");
            planetTexture = Content.Load<Texture2D>("planet");
            craterTexture = Content.Load<Texture2D>("crater");
            wipeTexture = Content.Load<Texture2D>("wipe");

            // Initialise the two render targets to be the same size as the planet.
            craterRender = new RenderTarget2D(graphics.GraphicsDevice,
                planetTexture.Width, planetTexture.Height);
            wipeRender = new RenderTarget2D(graphics.GraphicsDevice,
                planetTexture.Width, planetTexture.Height);

            // Set each of the render targets to be blank initially.
            graphics.GraphicsDevice.SetRenderTarget(craterRender);
            graphics.GraphicsDevice.Clear(Color.White);
            graphics.GraphicsDevice.SetRenderTarget(wipeRender);
            graphics.GraphicsDevice.Clear(Color.White);

            // Go back to drawing to the screen again.
            graphics.GraphicsDevice.SetRenderTarget(null);
        }
You'll see that we've added a render target and texture for the crater part and loaded them as we've done previously. We've also added a random generator, so that we can later draw the craters with a different rotation each time; a current and previous mouse state, for detecting mouse presses and getting the position, and I've set the mouse to be visible in the game's constructor, to make things easier for the player.

Now we need a method to call when a new mouse press is detected. Add this to the bottom of your Game1 class.
        /// 
        /// Add a crater to the render target at the current mouse position.
        /// 
        private void AddCrater()
        {
            // Pick a random rotation for the new crater (between 0 and 360 degrees).
            float rotation = (float)random.NextDouble() * MathHelper.TwoPi;

            Vector2 origin = new Vector2(
                craterTexture.Width / 2, craterTexture.Height / 2);

            // Create a temporary render target to use while we update the mask.
            RenderTarget2D tempRender = new RenderTarget2D(graphics.GraphicsDevice,
                planetTexture.Width, planetTexture.Height);

            // Tell the graphics device we want to draw to our seperate render target,
            // instead of the screen, until we tell it otherwise.
            graphics.GraphicsDevice.SetRenderTarget(tempRender);

            spriteBatch.Begin();

            // Draw the previous render (including existing craters) to the new one.
            spriteBatch.Draw(craterRender, Vector2.Zero, Color.White);

            // Draw a new crater at the cursor's position.
            spriteBatch.Draw(craterTexture,
                new Vector2(currentMouse.X - 208, currentMouse.Y - 24),
                null, Color.White, rotation, origin, 1f, SpriteEffects.None, 1f);

            spriteBatch.End();

            // Go back to drawing to the screen again.
            graphics.GraphicsDevice.SetRenderTarget(null);

            // Update the crater render.
            craterRender = tempRender;
        }
The first couple of lines deal with setting up drawing variables. XNA draws using radians for rotation (where 0 to 2π is 0 to 360 degrees), so we generate a random double between 0 and 1 and multiply that by 2π to get our rotation. Next we set up a temporary render target, draw the current render target to it and then draw our new crater at the mouse's position (using the rotation and origin previously calculated). After that we just update the crater render target and finish.

All that's left to do is call this method when the mouse button is pressed.
        /// 
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// 
        /// Provides a snapshot of timing values.
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            previousMouse = currentMouse;
            currentMouse = Mouse.GetState();

            // If we press the mouse button, add a crater to the render target.
            if (currentMouse.LeftButton == ButtonState.Pressed
                && previousMouse.LeftButton == ButtonState.Released)
            {
                AddCrater();
            }

            base.Update(gameTime);
        }

        /// 
        /// This is called when the game should draw itself.
        /// 
        /// Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // Set the value of the alpha mask.
            alphaShader.Parameters["MaskTexture"].SetValue(craterRender);

            // Start SpriteBatch using our custom effect.
            spriteBatch.Begin(SpriteSortMode.Immediate,
                BlendState.AlphaBlend, null, null, null, alphaShader);

            // Draw the planet texture, including any mask in alphaShader.
            spriteBatch.Draw(planetTexture, new Vector2(208, 24), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }
This is relatively straightforward. We update our current and previous mouse states and if we detect a new mouse click then we call the AddCrater() method we previously created. The Draw() method is largely the same as it was before, just using craterRender instead of wipeRender.


There you go, now you have a planet you can pick apart piece by piece. Some considerations you could think about are how to tell when the planet has been completely masked (the planet is round while the render target is rectangular) and how you might do collision detection with this system (perhaps keeping a list of crater positions and rotations).



You can download my sample here.
Thanks go to Syntax Warriors for their excellent posts.