3
votes

I've been asked to split the question below into multiple questions:

HLSL and Pix number of questions

This is asking the first question, can I in HLSL 3 run a pixel shader without a vertex shader. In HLSL 2 I notice you can but I can't seem to find a way in 3?

The shader will compile fine, I will then however get this error from Visual Studio when calling SpriteBatch Draw().

"Cannot mix shader model 3.0 with earlier shader models. If either the vertex shader or pixel shader is compiled as 3.0, they must both be."

I don't believe I've defined anything in the shader to use anything earlier then 3. So I'm left a bit confused. Any help would be appreciated.

2

2 Answers

7
votes

The problem is that the built-in SpriteBatch shader is 2.0. If you specify a pixel shader only, SpriteBatch still uses its built-in vertex shader. Hence the version mismatch.

The solution, then, is to also specify a vertex shader yourself. Fortunately Microsoft provides the source to XNA's built-in shaders. All it involves is a matrix transformation. Here's the code, modified so you can use it directly:

float4x4 MatrixTransform;

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

And then - because SpriteBatch won't set it for you - setting your effect's MatrixTransform correctly. It's a simple projection of "client" space (source from this blog post). Here's the code:

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

You can try the simple examples here. The greyscale shader is a very good example to understand how a minimal pixel shader works.

Basically, you create a Effect under your content project like this one:

sampler s0;

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0
{
    // B/N
    //float4 color = tex2D(s0, coords);
    //color.gb = color.r;

    // Transparent
    float4 color = tex2D(s0, coords);
    return color;
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

You also need to:

  1. Create an Effect object and load its content.

    ambienceEffect = Content.Load("Effects/Ambient");

  2. Call your SpriteBatch.Begin() method passing the Effect object you want to use

    spriteBatch.Begin( SpriteSortMode.FrontToBack, BlendState.AlphaBlend, null, null, null, ambienceEffect, camera2d.GetTransformation());

  3. Inside the SpriteBatch.Begin() - SpriteBatch.End() block, you must call the Technique inside the Effect

    ambienceEffect.CurrentTechnique.Passes[0].Apply();