Vertex/Pixelshader

I am new to shader programming and I am trying to get my shader to display something. However, the result of the vertex nor pixel shader is visible.

#if OPENGL
	#define SV_POSITION POSITION
	#define VS_SHADERMODEL vs_3_0
	#define PS_SHADERMODEL ps_3_0
#else
	#define VS_SHADERMODEL vs_4_0_level_9_1
	#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

float4x4 xViewProjection;
float4 g_fTime;

struct PixelShaderOutput
{
	float4 Color: COLOR0;
};

struct VertexShaderOutput
{
	float4 Position : SV_POSITION;
	float2 TexCoord : TEXCOORD0;
	float4 Color: COLOR0;
};

VertexShaderOutput VertexShaderLogic(float4 position : SV_POSITION, float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
	VertexShaderOutput output = (VertexShaderOutput)0;

	output.Position = mul(position, xViewProjection);
	output.TexCoord = texCoord;
	output.Color = color;

	output.Position.x = g_fTime;

	return output;
}

PixelShaderOutput PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	PixelShaderOutput output = (PixelShaderOutput)0;

	output.Color = input.Color;
	output.Color = 0.1;

	return output;
}

technique SpriteDrawing
{
	pass P0
	{
		VertexShader = compile VS_SHADERMODEL VertexShaderLogic();
		PixelShader = compile PS_SHADERMODEL PixelShaderFunction();
	}
};`

And the calling code

WindShader.Parameters["xViewProjection"].SetValue(Program.GetComponent<CameraComponent>().ViewMatrix);
            WindShader.Parameters["g_fTime"].SetValue((float)Math.Sin(gameTime.ToFloat()) * 2.5f);
            WindShader.CurrentTechnique.Passes[0].Apply();

            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, transformMatrix: Program.GetComponent<CameraComponent>().ViewMatrix);

            spriteBatch.Draw(Tree, new Vector2(512, 200), Color.White);
            spriteBatch.End();

The drawn texture2d doesn’t appear to lack any color (alpha) nor does it move along the X axis. Actually I think just nothing happens at all. Am I missing something?

Thanks in advance

Need this parameter in the shader:

    Texture2D SpriteTexture;
    sampler2D SpriteTextureSampler = sampler_state
    {
    	Texture = <SpriteTexture>;
    };

And so in the pixel shader you’d need to sample from the texture (set by spritebatch for you or you can provide a texture as a parameter):

PixelShaderOutput PixelShaderFunction(VertexShaderOutput input) : COLOR0 {
    return tex2D(SpriteTextureSampler,input.TexCoord) * input.Color;
}

The input.Color is actually a color that is blended between each vertex (optional) and usually set to White (1,1,1,1) which is because the red, green, blue, and alpha are 100% value of whatever’s sampled.
If it sampled from the tree image something like: 0.7, 0.4, 0.5, 0.9 and color was say 1,0,0.2,1 then the resulting color would be 100% red: 0.7, 0% green: 0, 20% blue: 0.5*0.2, and 0.9 transparency. It’s possible to make a version of spritebatch that could blend different colors between vertices.
So… if you never use colorization on your sprites you could probably get away with just using the tex2D sample.

g_fTime probably better to set as float instead of float4

In pixel shader I notice you set Color to 0.1 which I believe sets all components ie: r= 0.1,g= 0.1, b=0.1, a=0.1
and that would be nearly black and almost completely see-thru.

Also I wouldn’t pass in the transform matrix into the spriteBatch Begin but instead just pass in the effect and set the parameter when needed (maybe in Update) ie:

timer += 0.025f;
WindShader.Parameters["g_fTime"].SetValue((float)Math.Sin((double)timer)*0.05f);    //  0.05 is Sway amount
WindShader.Parameters["xViewProjection"].SetValue(xViewProj);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, effect: WindShader);            
spriteBatch.Draw(Tree, Vector2.Zero, Color.White);
spriteBatch.End();

Not sure if this is a possible issue - in init I usually set xViewProj to something like this (not sure how your camera component works):

PresentationParameters pp = GraphicsDevice.PresentationParameters;
xViewProj = Matrix.CreateOrthographicOffCenter(0, pp.BackBufferWidth, pp.BackBufferHeight, 0, -2000.0f, 2000.0f);

Also in vertex shader I’d set the position.x as something like this(instead of = if you did using math like I used above)
output.Position.x += g_fTime;

The pixel and vertex shader’s content is just for the sake of “Hello world!” thus, don’t make much sense. But thanks for your detailed feedback; It is working now :slight_smile:

Why is it working if I pass the effect within the spriteBatch.Begin and isn’t working with effect.Apply()? Moreover, lets assume the game will have thousands of objects (theoretically) and each one of them has a shader applied: How am I supposed to call the drawing? I guess, with calling for each texture a Begin and End the performance will suffer. I could bundle all objects with the same shader and call for each bundle a .Begin and .End. But that would require a complicated logic in terms of draw order and would screw up (just as an example) the entity component model.

Can you recommend me any online shader programming resources? I did search for hours, but found only obsolete tutorials (mostly pixel shaders) for XNA which won’t compile on MonoGame.

Am I right with the assumption, that on Windows the .fx will compile for HLSL and on Linux/Mac for GLSL? I noticed, that the MonoGame.ContentPipeline compiles with DX_9 but when I do start the .exe it compiles with OpenGL. (Because I target .netcore3.0, instead of .NET4.whatever)

Just for the sake of documentation the working source

#if OPENGL
	#define SV_POSITION POSITION
	#define VS_SHADERMODEL vs_3_0
	#define PS_SHADERMODEL ps_3_0
#else
	#define VS_SHADERMODEL vs_4_0_level_9_1
	#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

Texture2D SpriteTexture;
sampler2D SpriteTextureSampler = sampler_state
{
	Texture = <SpriteTexture>;
};

float4x4 xViewProjection;
float g_fTime;

struct PixelShaderOutput
{
	float4 Color: COLOR0;
};

struct VertexShaderOutput
{
	float4 Position : SV_POSITION;
	float2 TexCoord : TEXCOORD0;
	float4 Color: COLOR0;
};

VertexShaderOutput VertexShaderLogic(float4 position : SV_POSITION, float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
	VertexShaderOutput output = (VertexShaderOutput)0;

	output.Position = mul(position, xViewProjection);
	output.TexCoord = texCoord;
	output.Color = color;

	output.Position.x += g_fTime;

	return output;
}

PixelShaderOutput PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	PixelShaderOutput output = (PixelShaderOutput)0;

	output.Color = tex2D(SpriteTextureSampler, input.TexCoord) * input.Color;

	return output;
}

technique SpriteDrawing
{
	pass P0
	{
		VertexShader = compile VS_SHADERMODEL VertexShaderLogic();
		PixelShader = compile PS_SHADERMODEL PixelShaderFunction();
	}
};

WindShader.Parameters["xViewProjection"].SetValue(Program.GetComponent<CameraComponent>().ProjectionMatrix);
WindShader.Parameters["g_fTime"].SetValue((float)Math.Sin(gameTime.ToFloat() * 0.005f) * .005f);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, effect:WindShader, transformMatrix: Program.GetComponent<CameraComponent>().ViewMatrix);

spriteBatch.Draw(Tree, new Vector2(512, 200), Color.White);
spriteBatch.End();

The tree gets drawn always at the same position, kinda ignoring the ViewMatrix. However, without the effect the ViewMatrix kicks in and the tree gets drawn in relation to the Cameras ViewMatrix. Why is that?

You need effect.Apply() when drawing your own geometry. SpriteBatch applies it’s own effect internally, overriding the currently active effect.

As for drawing many objects efficiently, you want to batch your objects into as few draw calls as possible. That usually means grouping objects together that share the same shader, and as a second step grouping objects together that share the same textures. Since you can pass more than one texture to a shader, you don’t neccessarily need a separate draw call for every single texture. You should probably also use the depth buffer (blend states) for depth sorting, because then you don’t need to draw your objects back to front.

Looks like you are not setting a view matrix but only a projection matrix, which doesn’t include camera movements.

Your shader doesn’t use the transformMatrix, it only uses the xViewProjection parameter. As the name implies you need to get your view as well as your projection matrix in there, not just the projection matrix. You can multiply your view and projection matrix to get the combined matrix, if your CameraComponent doesn’t already provide that.

Ok, that did the trick. But I did already pass it into the draw.Begin.Why doesn’t this affect the drawn sprite?

Probably because the transformMatrix parameter is applied in the default SpriteBatch shader, and since you are using a custom effect that’s not happening anymore. For this to still work with custom shaders every corner of every sprite would need to be transformed on the CPU rather than the GPU. It could be done, but it would be less efficient than doing it in the shader.

Maybe this will help a little. This was originally a shader made on a whim to see if i could make a scissors cliping shader with a fade along the edges.

So i altered it really quick to demonstrate moving everything via a world matrix
Or by adding to the vertice positions directly in the shader. It does some stuff to clip in the pixel shader to
It has two shaders the Game2_Alterposition class ( look to the update method ) and the similarly named altered position shader is the one to look at ( the vertex shader ).

The transformation matrix is basically basicEffects world matrix.

I probably should of named it how to vertex shader with spritebatch.

While what you are doing is ok for learning you don’t normally want to try to use these transforms on individual draws its just not rigged that way its mean to be applied to all your draws.

Normally the shaders work off texture positions not pixel positions i know it seems funny but that is actually what makes the gpu a parallel processor that goes so fast.

If you want to see something that is useful and done on a shader just under that idea. This basically demonstrates scrolling textures in place while also alphablending and shading a texture against a second masking texture that makes a cool effect.

If you want to see a equivalent of spritebatch with vertice data completely from the ground up.

Maybe this might help to get a better idea of what spritebatch is doing.
In this post i bypass spritebatch altogether to just draw with quads.
but the effect is just like you would see with spritebatch,

Hope some of this helps.