Implementing a pass-through 3.0 vertex shader for games without vertices

I stated earlier that it was impossible to use a vertex shader in XNA when writing an entirely 2D game. Like a lot of things I fervently believe, this turned out to not actually be the case. When fancying up my original wave-distortion shader to make it work in arbitrary directions, I had to do quite a bit more trigonometry, and this pushed me over the maximum of 64 arithmetic slots allowed by shader version 2.0. I had a choice: I could either get smarter about my math, or figure out how to make shader version 3.0 work with XNA sprite batches. It actually didn’t turn out to be that hard once I believed it must be possible.

The problem I was seeing when attempting to implement a vertex shader naively before is that the entire screen would render one solid color during the duration of the Effect. The key seems to be that you need to create an orthographic projection the size of the screen and then multiply it by a half-pixel offset translation matrix, like so:

Matrix projection = Matrix.CreateOrthographicOffCenter(0, spriteBatch.GraphicsDevice.Viewport.Width, spriteBatch.GraphicsDevice.Viewport.Height, 0, 0, 1);
Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
effect.Parameters["MatrixTransform"].SetValue(halfPixelOffset * projection);

For the record, omitting the half-pixel translation will make your full screen post-processing effect a bit blurry. It’s been too long since I took a graphics course to remember precisely why that is, and I’m too lazy to learn it until I absolutely need to.

Anyway, once you have the above in your XNA code, then your version 3.0 vertex shader is as simple as:

float4x4 MatrixTransform; 

void SpriteVertexShader(inout float4 vColor : COLOR0, inout float2 texCoord : TEXCOORD0, inout float4 position : POSITION0) { 
    position = mul(position, MatrixTransform); 
}

The underlying reason that you need to specify a vertex shader for a version 3.0 pixel shader but not a version 2.0 pixel shader seems to be that, when you don’t specify a vertex shader explitly, XNA will give you the default one supplied by the sprite batch, and it uses some ridiculously old version like 1.1, which is incompatible with 3.0 (but not 2.0). I’m the first to admit I find this whole shader business confusing and often counter-intuitive, but surely that will wane with experience.

2 thoughts on “Implementing a pass-through 3.0 vertex shader for games without vertices

  1. Hi, I did what you suggested, but when I apply my effect in the spriteBatch.Begin() call nothing is displayed. Would you post by any chance a working example of how to apply a PS3.0 efx on a spritebatch in XNA 4.0 ? Thanks in advance.

Leave a Reply