Thursday, February 4, 2010

Video player and stencil buffer

Was checking out what is new in XNA 3.1 and started messing around with the video player and stencil buffers.. could probably make some pretty cool effects!

Here is where you can get the source code:

source code

The most interesting part is the draw function.. prior to this though, make sure you have enabled stencil buffer:


graphics.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;


Here is the draw function and a breakdown of what it does:
  • Clear the stencil buffer to all zeros
  • Enable the stencil buffer
  • Change the function to greater than or equal. We want to do this because the buffer is all zeros and we want the test to pass
  • Change StencilPass to Replace. This will replace the 0's with 1's wherever the texture is drawn
  • Set the reference stencil (the number that will be put in the stencil buffer) to 1
  • Disable writing to the color buffer.. we don't actually want to draw our mask
  • Draw the mask (with alpha testing on so only the mask portion (the white bits) will render. This causes 1's to be written to the stencil buffer where the white bits of our texture are.
  • Change the stencil function to 'Equal' because now we only want to draw where the 1's are in the stencil buffer
  • Turn on drawing to the color buffer
  • Draw the video image



protected override void Draw(GameTime gameTime)
{
GraphicsDevice.VertexDeclaration = quadVertexDecl;

GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.Stencil, Color.CornflowerBlue, 0, 0);

quadEffect.Begin();

GraphicsDevice.RenderState.StencilEnable = true;
GraphicsDevice.RenderState.StencilFunction = CompareFunction.GreaterEqual;
GraphicsDevice.RenderState.StencilPass = StencilOperation.Replace;
GraphicsDevice.RenderState.ReferenceStencil = 1;
GraphicsDevice.RenderState.ColorWriteChannels = ColorWriteChannels.None;

drawMask(true);

GraphicsDevice.RenderState.ReferenceStencil = 1;
GraphicsDevice.RenderState.StencilFunction = CompareFunction.Equal;
GraphicsDevice.RenderState.ColorWriteChannels = ColorWriteChannels.All;

drawVideo();

quadEffect.End();


base.Draw(gameTime);
}

protected void drawVideo()
{
// If the video is playing, get the current frame
// as a texture. (Calling GetTexture on a stopped
// player results in an exception)
if (player.State == MediaState.Playing)
quadEffect.Texture = player.GetTexture();

foreach (EffectPass pass in quadEffect.CurrentTechnique.Passes)
{
pass.Begin();

GraphicsDevice.DrawUserIndexedPrimitives
(
PrimitiveType.TriangleList,
quad.Vertices, 0, 4,
quad.Indexes, 0, 2);


pass.End();
}
}

protected void drawMask(bool doAlpha)
{
quadEffect.Texture = bug;

if (doAlpha)
{
GraphicsDevice.RenderState.AlphaTestEnable = true;
GraphicsDevice.RenderState.ReferenceAlpha = 0;
GraphicsDevice.RenderState.AlphaFunction = CompareFunction.Greater;

}
else
GraphicsDevice.RenderState.AlphaTestEnable = false;

foreach (EffectPass pass in quadEffect.CurrentTechnique.Passes)
{
pass.Begin();

GraphicsDevice.DrawUserIndexedPrimitives
(
PrimitiveType.TriangleList,
quad.Vertices, 0, 4,
quad.Indexes, 0, 2);

pass.End();

}

if (doAlpha)
GraphicsDevice.RenderState.AlphaTestEnable = false;

}