Hi Folks,
I’ve been having a look around and trying to find a good starting point to make a color replacement shader - but haven’t had any luck to date with either google or forum searches.
Basically what I’m trying to do is start with a grayscale sprite and then be able to replace each individual shade of gray with a different color. I’m using spritemanager 2 - so my theory is that with grayscale sprites I can then make different color schemes etc.
If anybody can give me some prompting in the right direction on this I’d appreciate it.
Cheers,
Matt
This will swap a greyscale value for the colour at that value on another pixel ramp.
It’s important to ensure that the ramp texture is 256 pixels long and that you apply the following settings in the inspector, otherwise you risk getting fuzzy results;
Filter Mode : Point
Wrap Mode : Clamp
Format : Truecolor
Or;
Filter Mode : Point
Wrap Mode : Clamp
Texture Type : Advanced
Generate Mip Maps : Unticked/Off
e.g.
This texture and this ramp give this result;
(greyscale texture to be replaced)
(colour ramp - darker colours swapped with more left hand side colour, brighter colours swapped with more right hand side colour)
(left : greyscale, right : colour replacement)
Shader "ColourReplacement" {
Properties {
_MainTex ("Greyscale (R) Alpha (A)", 2D) = "white" {}
_ColorRamp ("Colour Palette", 2D) = "gray" {}
}
SubShader {
Pass {
Name "ColorReplacement"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata_tan v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
return o;
}
sampler2D _MainTex;
sampler2D _ColorRamp;
float4 frag(v2f i) : COLOR
{
// SURFACE COLOUR
float greyscale = tex2D(_MainTex, i.uv).r;
// RESULT
float4 result;
result.rgb = tex2D(_ColorRamp, float2(greyscale, 0.5)).rgb;
result.a = tex2D(_MainTex, i.uv).a;
return result;
}
ENDCG
}
}
Fallback
Hope that’s somewhere close to where you want to get to.
2 Likes
Hey Farfarer,
That comes close to what I want to do in terms of replacing the colours, but what I was really hoping for was the ability to select the replacement colour programmatically - with the above solution if I wanted to get all of the different replacement combinations for 255 different shades of gray I would need a prohibitive number of replacement colour ramps.
Is it possible to have 255 replacement colours that can be modified programmatically where each replacement colour replaces one shade of gray?
Thanks again for your help!
Cheers,
Matt
It’s technically possible, colour palettes are evidently in use in games.
Not sure how it’d be done via a shader, though. Sorry
Some sort of writeable texture that you could save/load palette info into?
Hey Farfarer,
Turns out there was a fairly easy solution to doing it the way you suggested - programmatically create the “colour ramp” texture
public class PaletteGenerator : MonoBehaviour {
[INDENT]public Color[] colourArray = new Color[256];[/INDENT]
public void Awake()
{
Texture2D colourPalette = new Texture2D(256, 10, TextureFormat.ARGB32, false);
for(int x = 0; x < 256; x++){
for(int y = 0; y < 10; y++){
colourPalette.SetPixel(x,y,colourArray[x]);
}
}
colourPalette.filterMode = FilterMode.Point;
colourPalette.wrapMode = TextureWrapMode.Clamp;
colourPalette.Apply();
renderer.material.SetTexture("_ColorRamp",colourPalette);
}
}
This provides a 256 x 10 colour ramp which applies itself to the shader.
So - that part is sorted!
I have a follow up question - I’m battling a little with understanding your original shader - but what I was wondering is can the complexity of the colour ramp lookup be increased such that it uses the red and green elements of the original texture to look up the colour ramp?
e.g. the colour ramp is 256 x 256 pixels, and colour (R : 0, G : 0, B : 0, A : 0) looks up the top left pixel of the ramp, and colour (R : 255, G : 255, B : 0, A : 0) looks up the bottom right pixel of the ramp (so that it’s a colour map rather than a colour ramp? This would give the opportunity of a 65535 palette size rather than 256 colours - and everybody loves more colours!
Thanks again for your help
Cheers,
Matt
Yeah, that’s possible.
I was just working off the assumption that there are only 256 shades of grey representable in a texture so you only needed a 256 pixel wide texture for a comprehensive lookup.
Replacing the fragment shader part of the original one I posted with this will use the R and G channels as the UV coordinates of the palette look-up. So more red = towards right hand side, more green = towards top.
float4 frag(v2f i) : COLOR
{
// SURFACE COLOUR
float2 greyscale = tex2D(_MainTex, i.uv).rg;
// RESULT
float4 result;
result.rgb = tex2D(_ColorRamp, float2(greyscale.x, greyscale.y)).rgb;
result.a = tex2D(_MainTex, i.uv).a;
return result;
}
Also, I’d make your generated texture height a power of 2 (e.g. 16 or 32 or even 256) rather than 10. Textures like to be powers of two
So I’ve been trying to write this functionality into a variation of the standard Unity particle shaders. I’m having trouble using the _TintColor property. I think this is what allows the Particle animator to change the material’s color/opacity. Any ideas?
Thanks in advance.
After some trial and error I figured it out. If anybody is interested in seeing the results feel free to let me know.
Id love to see a final version of this if there is one please, trying to do a very similar thing
I was using Unity’s Unlit Transparent shader for a material that have some “holes” in it. But I switched for this script and made this modification:
// RESULT
float4 result;
result.a = tex2D(_MainTex, i.uv).a;
if (result.a < 0.5)
result.rgb = tex2D(_MainTex, i.uv).rgb;
else
result.rgb = tex2D(_ColorRamp, float2(greyscale, 0.5)).rgb;
return result;
This way I’m keeping original values in transparent pixels.
But the problem is that when I do this I lose the transparency effect. So how can I implement transparency in this shader, or how can I use both shaders?
I’d strongly recommend using a CG HSVtoRGB shader function, they are awesome… it gives you natural color control like a painter instead of control like a crt monitor, which you arent presumably!
This is a way to make 1 float value. i.e. Grey, into 1000 ds of combinations of color and brightness and saturation etc:
int rand = a global color randomizer that you can control by script/ by a crossfader etc.
float h = perlin(grey,23.3rand)
float s = perlin(grey,54,4rand)
float v = perlin(grey,12.6*rand)
Return float4 HSVtoRGB(h,s,v);
result is AWESOME COLOR RANDOMIZATION! it’s not natural but it uses natural color gradients and it looks organic and controlleably irridescent / pastel parameters.
For perlin, you can use this function, it is a fast zig zag version of perlin.
function zig ( xx : float ): float{ //lfo nz -1,1
xx= xx+32;
var x0 = Mathf.Floor(xx);
var x1 = x0+1;
var v0 = (Mathf.Sin (x0*.014686)31718.927)%1;
var v1 = (Mathf.Sin (x1.014686)*31718.927)%1;
return Mathf.Lerp( v0 , v1 , (xx)%1 )*2-1;
}