This seems to crop the images (albeit, not correct once the image starts to move), but my problem is that the images are constantly moving (they aren't static), so this cropping needs to be dynamic.

Is there a way I could alter the shader code to take into account it's position?

Alternatively, I've read about using the Stencil Buffer, but most of the samples seem to hinge on using a rendertarget, which I really don't want to do. (I'm already using 3 or 4 for the rest of the game, and adding another one on top of it seems overkill)

The only tutorial I've found that doesn't use Rendertargets is one from Shawn Hargreaves' blog over here. The issue with that one, though is that it's for XNA 3.1, and doesn't seem to translate well to XNA 4.0.

It seems to me that the pixel shader is the way to go, but I'm unsure of how to get the positioning correct. I believe I would have to change my onscreen coordinates (something like 500, 500) to be between 0 and 1 for the shader coordinates. My only problem is trying to work out how to correctly use the transformed coordinates.

Stencil seems to be the way to go, though, I need to know how to properly use them too :P I'll be waiting for a good answer on the matter.
–
Gustavo MacielOct 2 '12 at 18:00

Heh, most of the good Stencil tutorials are for XNA 3.1, and most of the 4.0 ones use RenderTargets (which seems wasteful, to me). I've updated my question with a link to an old 3.1 tutorial that seemed like it could work, but not in 4.0. Maybe someone knows how to translate it to 4.0 correctly?
–
electroflameOct 2 '12 at 18:05

If you would use pixel shader to accomplish it, I think you would have to unproject your pixel coordinate to screen/world(depends on what you want to do), and this is kinda wasteful(stencil would not have this problem)
–
Gustavo MacielOct 2 '12 at 18:08

This question is an excellent question.
–
ClassicThunderOct 3 '12 at 4:45

Thank you for the answer! This seems like it will work, but currently I'm having an issue with it. Currently, it's being cropped on the left of the image (with a straight edge, as well). Am I supposed to be using screen coordinates for the positions? Or am I already supposed to have converted them to 0 - 1?
–
electroflameOct 3 '12 at 1:24

I see the problem. I should have compiled the pixel shader before sharing it. :P In the maskCoord calculation one of the X's should have been a Y. I've edited my initial answer with the fix and included the UV clamp settings you will need for your MaskTexture sampler.
–
JimOct 3 '12 at 4:40

1

Now it works perfectly, thank you! The only thing that I should mention is that if you have your sprites centered (using Width/2 and Height/2 for the origin) you will need to subtract half of your Width or Height for your X and Y locations (that you pass into the shader). After that it works perfectly, and is extremely fast! Thanks a ton!
–
electroflameOct 3 '12 at 16:54

The first step is to tell the graphics card we need the stencil buffer. To do this when you create GraphicsDeviceManager we set the PreferredDepthStencilFormat to DepthFormat.Depth24Stencil8 so there is actually a stencil to write to.

Next we need to set up two DepthStencilStates. These states dictate when the SpriteBatch renders to the stencil and when the SpriteBatch renders to the BackBuffer. We are primarily interested in two variables StencilFunction and StencilPass.

StencilFunction dictates when the SpriteBatch will draw individual pixels and when they will be ignored.

StencilPass dictates when drawn pixels pixels effect the Stencil.

For the first DepthStencilState we set StencilFunction to CompareFunction. This causes the StencilTest to succeed and when the StencilTest the SpriteBatch renders that pixel. StencilPass is set to StencilOperation. Replace meaning that when the StencilTest succeed that pixel will written to the StencilBuffer with the value of the ReferenceStencil.

In summary the StencilTest always passes, the image is drawn to screen normally, and for pixels drawn to the screen a value of 1 is stored in the StencilBuffer.

The second DepthStencilState is slightly more complicated. This time we want to only draw to the screen when the value in the StencilBuffer is. To achieve this we set the StencilFunction to CompareFunction.LessEqual and the ReferenceStencil to 1. This means that when the value in the stencil buffer is 1 the StencilTest will succeed. Setting StencilPass to StencilOperation. Keep causes the StencilBuffer not to update. This allows us to draw multiple times using the same mask.

In summary the StencilTest only passes when the StencilBuffer is less than 1 (the alpha pixels from the mask) and does not effect the StencilBuffer.

Now that we have our DepthStencilStates set up. We can actually draw using a mask. Simply draw the mask using the first DepthStencilState. This will effect both the BackBuffer and the StencilBuffer. Now that the stencil buffer has a value of 0 where you mask had transparency and 1 where it contained color we can use StencilBuffer to mask later images.

+1 This is one of the most complete StencilBuffer examples I've seen for XNA 4.0. Thanks for this! The only reason I chose the pixel shader answer is because it was easier to set up (and actually faster to boot), but this is a perfectly valid answer as well and may be easier if you already have a lot of shaders in place. Thanks! Now we've got a complete answer for both routes!
–
electroflameOct 3 '12 at 16:52

This is the best real solution for me , and more than the first
–
Mehdi BugnardOct 17 '13 at 14:06