- Home /
Pixel color replacement - in a pixel art context -.
Hi,
Imagine a basket-ball game made in pixel-art style. I want to give the possibility to change shirts colors on the fly. If the team' shirt is blue and player wants to play with a pink shirt, I want every blue pixels to be changed by a pink one.
I immediately thought to an algorithm using GetPixels / SetPixels on the sprite texture. I've implemented it and it works fine.
BUT, I was wondering if such effect couldn't be done using shaders (I suck at shaders)? And if so, do you guys think using the shader technique would be more efficient / fast / smart than using the GetPixels / SetPixels one?
Answer by sooncat · Feb 06, 2014 at 08:23 AM
A simple demo:
Shader "Custom/MyCharacter" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_ShirtTex ("ShirtTex(RGB)", 2D) = "white" {}
_ShirtColor ("ShirtColor", Color) = (1,1,1,1)
_NameColor ("NameColor", Color) = (1,0,0,1)
_NumColor ("NumColor", Color) = (0,1,0,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _ShirtTex;
float4 _ShirtColor;
float4 _NameColor;
float4 _NumColor;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c1 = tex2D (_MainTex, IN.uv_MainTex);
half4 c2 = tex2D (_ShirtTex, IN.uv_MainTex);
o.Albedo = lerp(c1.rgb, _ShirtColor, c2.r);
o.Albedo = lerp(o.Albedo, _NameColor, c2.g);
o.Albedo = lerp(o.Albedo, _NumColor, c2.b);
o.Alpha = c1.a;
}
ENDCG
}
FallBack "Diffuse"
}
The shader is very similar to Terrain Shader.(Add an tex and record colorArea in R/G/B/A layers)
The MainTex is useless in this shader, change code in "void surf ()" to show colors in MainTex.
Well, it seems to work! Thank you for the hint. However the lighting seems to be broken... how can I use a shader like this on an unlit texture ?
// Unlit_$$anonymous$$yCharacter shader. ($$anonymous$$odified from Unlit/Normal)
// Change _ShirtColor / _NameColor / _NumColor to compose your shirt.
Shader "Unlit/$$anonymous$$yCharacter" {
Properties {
_$$anonymous$$ainTex ("Base (RGB)", 2D) = "white" {}
_ShirtTex ("ShirtTex(RGB)", 2D) = "white" {}
_ShirtColor ("ShirtColor", Color) = (1,1,1,1)
_NameColor ("NameColor", Color) = (1,0,0,1)
_NumColor ("NumColor", Color) = (0,1,0,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 100
Pass {
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
sampler2D _$$anonymous$$ainTex;
float4 _$$anonymous$$ainTex_ST;
sampler2D _ShirtTex;
float4 _ShirtTex_ST;
float4 _ShirtColor;
float4 _NameColor;
float4 _NumColor;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, v.vertex);
o.texcoord = TRANSFOR$$anonymous$$_TEX(v.texcoord, _$$anonymous$$ainTex);
return o;
}
fixed4 frag (v2f i) : COLOR
{
fixed4 col = tex2D(_$$anonymous$$ainTex, i.texcoord);
fixed4 col2 = tex2D (_ShirtTex, i.texcoord);
//change code blow if need any detail in _$$anonymous$$ainTex
col = lerp(col, _ShirtColor, col2.r);//change area in red to _ShirtColor
col = lerp(col, _NameColor, col2.g);//change area in green to _NameColor
col = lerp(col, _NumColor, col2.b);//change area in blue to _NumColor
return col;
}
ENDCG
}
}
}
Hey, thanks again for the answer. I'm not sure it works as I thought :(
As I understand your shader it will look for colors that contain "Red" and change them by "_ShirtColor" value... it only works with full Red, full Blue... right? How to make it work with specific colors to perform something like "Change every pixel of (100,12,168) color by (22,33,55) color" ?
Thanks again for your help.
No, it can change any area not only the solid color. And you dont know why i left the _$$anonymous$$ainTex.
Note that : col = lerp(col, ShirtColor, col2.r); It means : Change color in $$anonymous$$ainTex if point with same coordinate in ShirtTex is red, or keep the color in $$anonymous$$ainTex.
You should Add your origin tex as _$$anonymous$$ainTex, and copy a tex as _ShirtTex. Then Change the shirtArea/nameArea/numArea color to solidcolor in _ShirtTex.
As you said, if you want change all pix of (100,12,168), you should change all pix with the color in _ShirtTex to red, and set _ShirtColor to (22,33,55).
if more than 4 colors need change, Do Not Use"change a as b", conclude them as a func and change "lerp" to you func.
At last, sorry for my poor English.
I'm really sorry but I still don't get it (once again, I suck a shaders :/).
Here is my original plan : having two textures, one of the shirt and another one with a "palette" ( a grid of 16xN color, where 16 is the number of color available on the shirt texture, and N the number shirt color skin possible ie: if [0][0] is white and [1][0] is blue, then every white pixels of the original shirt will be blue when player selects the second set of colors available ).
So yes, there will be obviously more than 4 colors to change.
This is surely why I still don't manage to understand what you're trying to explain me (and I'm sorry about that). Do you think this technique (using a palette and a texture to change) can be doable via shaders ?
(once again thanks for your help)
Answer by GameVortex · Feb 06, 2014 at 08:09 AM
A shader would definitely be the way to go in terms of efficiency and ease of use. Only having to change a color value instead of setting every pixel by hand.
Here is a single color shader provided by @Eric5h5 in this **thread**:
Shader "Unlit Color Only" {
Properties {
_Color ("Color", Color) = (1,1,1)
}
SubShader {
Color [_Color]
Pass {}
}
}
This will color the entire object with the color provided and you can change it in code by doing renderer.material.color = Color.red;
If your team has a logo on their shirt and you only want to color the background you can use this AlphaMask Color shader (provided by TeddyDief in this **thread**) which color the alpha part of the texture with a specific color:
Shader "Custom/VertexLitAlphaColoring" {
Properties {
_AlphaColor ("Alpha Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 80
Pass {
Tags { "LightMode" = "Vertex" }
// Setup Basic
Material {
Diffuse (1,1,1,1)
Ambient (1,1,1,1)
}
Lighting On
// Lerp between AlphaColor and the basic vertex lighting color
SetTexture [_MainTex] {
constantColor [_AlphaColor]
combine previous lerp(texture) constant DOUBLE, previous lerp(texture) constant
}
// Multiply in texture
SetTexture [_MainTex] {
combine texture * previous
}
}
}
}
Your answer
Follow this Question
Related Questions
Why aren't sprite pixels perfectly aligned on android? 1 Answer
Minecraft style pixels 1 Answer
Pixelated Shader 1 Answer
Change Color Based On Pixel X Coordinate in Shader 1 Answer
How to get current color buffer pixel value in shader? 1 Answer