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):
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:
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.
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.
#2
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.
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.
Torque Owner Jason Cahill
Default Studio Name
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.