pixel shader question

I have a simple pixel shader that replaces one color with another. Before calling the shader, I have to set two parameters. One parameter is the color to replace the second parameter is the color to replace it with. Works fine for a single sprite. Let’s say I’m drawing four sprites. Three of the sprites I want to replace blue with pink. One of the sprites I want to replace black with gray. This will not work. Below is the pseudo code. Is it possible to do what I’m trying?

spritebatch.begin setting effect and SpriteSortMode.BackToFront

effect.parameter.setvalue of blue.
effect.parameter.setvalue of pink.
draw sprite1

effect.parameter.setvalue of blue.
effect.parameter.setvalue of pink
draw sprite2

effect.parameter.setvalue of black.
effect.parameter.setvalue of gray.
draw sprite3

effect.parameter.setvalue of blue.
effect.parameter.setvalue of pink.
draw sprite4

spritebatch.end

I can do what I need if I do a bunch of spritebatch begin and end. I’m trying to avoid that because in future because I may draw a lot more sprites and I need the sprites sorted by the layer.

I believe the problem is that using SpriteSortMode.BackToFront results in deferral of the actual drawing until spriteBatch.End() is called, so the effect will apply to all sprites at once using whatever its parameters were last set to.

Try setting the SpriteSortMode to Immediate. That should cause each sprite to draw immediately when Draw() is called. The downside is that sorting is ignored. You’ll have to manually sort by calling Draw in the desired order.

Also, if you know you’re going to have multiple sprites with the same effect values, it might be more efficient to group them into their own SpriteBatches and use a deferred SpriteSortMode, only changing the parameters of the effect once before drawing the group.

You are correct changing the sort mode would work but I need my sprites sorted by the layer value.

So you need per instance data for your sprites. Instance data for sprites usually goes into the vertex buffer. The standard SpriteBatch vertex buffer format only has vertex position, texture coordinate and color. Unless you create a custom sprite solution, you’re stuck with those.

Color is the only field you could potentially use for your porpuse. Doing color replacement sounds like you wouldn’t need the standard sprite color. That only gives you one color though, while you need two. You could work around that by encoding two color indices instead of an actual color. In the shader you then use those indices to lookup the actual color from a color array. This assumes that you only need a limited number of colors.

Do you have an example of how to do this? I’m pretty new to pixel shaders. Thank you.

There’s not much to it.

1.) Add a color array to the shader containing all the colors you want to replace from/to.
Keeping things simple I’ll use red, green and blue here:

static float3 Colors[3] = 
{
    float3(1,0,0),
    float3(0,1,0),
    float3(0,0,1)
};

Those are now hardcoded in the shader, later you could upload this array from the CPU.

2.) Encode the color indices into the sprite color
Lets say we want to replace green with blue, we have to put index 1 (green) and index 2 (blue) into the sprite color, then draw the sprite using that color.

Color spriteCol = new Color(1, 2, 0); // third value doesn't matter, we don't need it 
spriteBatch.Draw(texture, pos, spriteCol);

3.) Extracat indices in pixel shader and lookup color values

int sourceIndex = (int)(input.Color.r * 255);
int destinationIndex = (int)(input.Color.g * 255);

float3 sourceColor = Colors[sourceIndex];
float3 destinationColor = Colors[destinationIndex];
1 Like

Thank you for the code. It’s always great to learn something new.