Game Development Community

Very easy way to tint a sprite

by Christopher Perkins · in Torque X 2D · 08/13/2009 (6:01 pm) · 2 replies

So, I developed the need for the ability to tint a sprite on the fly in one of my games and did some research. I found Scott Zarnke's post on the subject, but I dislike his method of changing the texture physically. Anywho, I decided to go with a custom shader, since thats what XNAs spritebatch basically does any way.

Here's my shader(it's a work in process since I dont fully understand HLSL yet and I plan on adding a bunch of techniques as I go):
float4x4 worldViewProjection;
texture baseTexture; 
float4 Tint;

sampler2D baseTextureSampler = sampler_state
{
Texture = <baseTexture>;
};  

struct VertexShaderInput
{
   float4 position : POSITION;  
   float4 color : COLOR;  
   float2 texCoord : TEXCOORD0;  
};

struct VertexShaderOutput
{
    float4 position : POSITION;  
    float4 color : COLOR0;  
    float2 texCoord : TEXCOORD0; 
};

VertexShaderOutput DefaultVertexShader(VertexShaderInput input)
{
    VertexShaderOutput output;

    output.position = mul(input.position, worldViewProjection);  
    output.texCoord = input.texCoord;  
    output.color = input.color;  
  
    return output;  
}

float4 DefaultPixelShader(VertexShaderOutput input) : COLOR0
{
    float4 color = tex2D(baseTextureSampler, input.texCoord);
    return color; 
}

float4 TintPixelShader(VertexShaderOutput input) : COLOR0
{ 
    float4 color = tex2D(baseTextureSampler, input.texCoord);
    color *= Tint;
    return color; 
}

technique DefaultTechnique
{
    pass p0
    {
        VertexShader = compile vs_1_1 DefaultVertexShader();
        PixelShader = compile ps_1_1 DefaultPixelShader();
    }
}

technique TintTechnique
{
     pass p0
     {
	VertexShader = compile vs_1_1 DefaultVertexShader();
	PixelShader = compile ps_1_1 TintPixelShader();
     }
}

Basically, it's got two techniques. The default that basically does nothing, and the Tint Technique which multiplies your tint into your sprite. All this is really simple:) Anywho, you can't do much with this without it's accompanying material:
public class TintMaterial : SimpleMaterial
{
    public Color Tint
    {
        set
        {
            _tint = value.ToVector4();
        }
    }

    public TintMaterial()
    {
        EffectFilename = "data/effects/yumilShader";
    }

    protected override string _SetupEffect(SceneRenderState srs, MaterialInstanceData materialData)
    {
        base._SetupEffect(srs, materialData);
        Effect.Instance.Parameters["Tint"].SetValue(_tint);
        return "TintTechnique";
    }

    private Vector4 _tint = Color.White.ToVector4();
}
OK, this code is really simple. I named my shader yumilShader(yumil being the name I typically go by on forums) and put it in the data/effects folder. Everytime the engine calls it to setup the effect(each time a sprite with this material is called) it will set its tint to the tint defined by the material and the shader will do all the work.

One thing I need to point out if you use this technique. If you have a bunch of the same sprite you want to tint differently, you might find that they end up tinted the same if they all use the same material. Theres a few ways you can get around this. One is you can create a new TintMaterial for each object you create, but this may end up using a lot of memory. Another thing is you can change the color of the material right before each of them render, BUT this will take some editing/overriding of the current T2DStaticSprite/T2DAnimatedSprite if you are going to do this.

Honestly, the effect Im going for likes having multiple instances of a sprite use the same tint, so I'm fine without overriding the Render method in a class that inherits Static/Animated sprite.

#1
08/17/2009 (11:18 pm)
Hi Christopher,

Thanks for sharing! This is great.

The design paradigm of TX2D is as you suggest: you need to create a new material for every tint you want. For most games, you will typically have a small number of "teams" (tints) and thus a small number of materials. Having a few materials is unlikely to be a big issue.

Anyway, thanks again for sharing this code.
#2
08/19/2009 (5:35 am)
Thanks Jason,

I don't see too many cases for one to need a ton of materials, but I put that out there as there probably are a few.

Honestly, all Im using it for is a cool color change effect. I've got a game where you basically stay between two lines that get closer together in a triangular like ship. Basically the walls will be changing color over time to make a trippy like effect. Im probably going to add a full screen effect to it along with lots of particles to make a simple game more fun with its color explosion.

So, this material is perfect for my uses:) I'm sure theres an even more elegant way to do it, but Im happy with my solution.