In my libgdx project I use a custom shader to create different color variations of a single grayscale character. I have a class "Shader" which holds static String
variables for the vertex and fragment shaders. For each person I basically create a new shader, by replacing strings in the fragment shader string.
For some reason, my characters end up looking good except for an addition at their heads, that looks like its a head turned sideways:
The character itself does not have this "additional head" (I've checked in all images):
What's even weirder, is if I remove the part of the shader that changes the skin color, the problem is gone. All other colors get replaced fine, only the skin seems to be the problem.
My Shader class:
public class Shader {
public static final String vertexShader = "attribute vec4 a_position; \n" +
"attribute vec4 a_color;\n" +
"attribute vec2 a_texCoord0;\n" +
"uniform mat4 u_projTrans;\n" +
"varying vec4 v_color;" +
"varying vec2 v_texCoords;" +
"void main() \n" +
"{ \n" +
" v_texCoords = a_texCoord0; \n" +
" gl_Position = u_projTrans * a_position; \n" +
"} \n" ;
private static final String fragmentShader = "#ifdef GL_ES\n" +
"precision mediump float;\n" +
"#endif\n" +
"varying vec4 v_color;\n" +
"varying vec2 v_texCoords;\n" +
"uniform sampler2D u_texture;\n" +
"void main() \n" +
"{ \n" +
"vec4 color = texture2D(u_texture, v_texCoords).rgba; \n" +
"vec4 newColor; \n" +
"if (color.r == {{trousersold}} && color.g == {{trousersold}} && color.b == {{trousersold}}){ //trousers \n" +
"newColor = vec4({{trousers}}, 1.0); \n" +
"}else if (color.r == {{shirtold}} && color.g == {{shirtold}} && color.b == {{shirtold}}){ \n" +
"newColor = vec4({{shirt}}, 1.0); \n" +
"} \n" +
"else if (color.r == {{skinold}} && color.g == {{skinold}} && color.b == {{skinold}}){ \n" +
"newColor = vec4({{skin}}, 1.0); \n" +
"} \n" +
"else if (color.r == {{hairold}} && color.g == {{hairold}} && color.b == {{hairold}}){ \n" +
"newColor = vec4({{hair}}, 1.0); \n" +
"} \n" +
"else if (color.r == {{shoeold}} && color.g == {{shoeold}} && color.b == {{shoeold}}){ \n" +
"newColor = vec4({{shoe}}, 1.0); \n" +
"} \n" +
"else if (color.r == {{eyeold}} && color.g == {{eyeold}} && color.b == {{eyeold}}){ \n" +
"newColor = vec4({{eye}}, 1.0); \n" +
"} \n" +
"else{ \n" +
"newColor = color; \n" +
"} \n" +
" gl_FragColor = vec4(newColor);\n" +
"}";
public static String getFragmentShader(Color hairColor, Color skinColor, Color shirtColor, Color trousersColor,
Color eyeColor, Color shoeColor){
String trousers = trousersColor.r + "," +trousersColor.g + "," + trousersColor.b;
String hair = hairColor.r + "," + hairColor.g + "," + hairColor.b;
String skin = skinColor.r + "," + skinColor.g + "," + skinColor.b;
String shirt = shirtColor.r + "," + shirtColor.g + "," + shirtColor.b;
String eye = eyeColor.r + "," + eyeColor.g + "," + eyeColor.b;
String shoe = shoeColor.r + "," + shoeColor.g + "," + shoeColor.b;
String fragment = fragmentShader.replace("{{hairold}}", ColorHolder.Hair);
fragment = fragment.replace("{{trousersold}}", ColorHolder.Trousers);
fragment = fragment.replace("{{skinold}}", ColorHolder.Skin);
fragment = fragment.replace("{{shirtold}}", ColorHolder.Shirt);
fragment = fragment.replace("{{eyeold}}", ColorHolder.Eyes);
fragment = fragment.replace("{{shoeold}}", ColorHolder.Shoes);
fragment = fragment.replace("{{hair}}", hair);
fragment = fragment.replace("{{trousers}}", trousers);
fragment = fragment.replace("{{skin}}", skin);
fragment = fragment.replace("{{shirt}}", shirt);
fragment = fragment.replace("{{eye}}", eye);
fragment = fragment.replace("{{shoe}}", shoe);
return fragment;
}
Also my ColorHolder class where I store the color values (grayscale) and possible new Colors:
public class ColorHolder {
public static String Trousers = "64.0/255.0";
public static String Shoes = "45.0/255.0";
public static String Shirt = "113.0/255.0";
public static String Skin = "210.0/255.0";
public static String Eyes = "124.0/255.0";
public static String Hair = "150.0/255.0";
public static List<String> TrouserColors = new ArrayList<String>(Arrays.asList("4649c3ff", "4687c3ff", "6e4208ff", "291004ff"));
public static List<String> ShoesColors = new ArrayList<String>(Arrays.asList("440849ff", "3e1c09ff", "d30000ff", "3e3e3eff"));
public static List<String> ShirtColors = new ArrayList<String>(Arrays.asList("839fdfff", "28caa2ff", "9eca28ff", "7428caff"));
public static List<String> SkinColors = new ArrayList<String>(Arrays.asList("e9c38cff", "dfca83ff", "eec39aff", "3b1d04ff"));
public static List<String> EyesColors = new ArrayList<String>(Arrays.asList("2c86ebff", "2ceba1ff", "611500ff", "248711ff"));
public static List<String> HairColors = new ArrayList<String>(Arrays.asList("e9df3eff", "e9a33eff", "543202ff", "ae4512ff"));
}
And finally, in the class where I instantiate the person, I get a shader like this:
private void setUpShader() {
String vertexShader = Shader.vertexShader;
String fragmentShader = Shader.getFragmentShader(
Color.valueOf(ColorHolder.HairColors.get(MathUtils.random(ColorHolder.HairColors.size() - 1))),
Color.valueOf(ColorHolder.SkinColors.get(MathUtils.random(ColorHolder.SkinColors.size() - 1))),
Color.valueOf(ColorHolder.ShirtColors.get(MathUtils.random(ColorHolder.ShirtColors.size() - 1))),
Color.valueOf(ColorHolder.TrouserColors.get(MathUtils.random(ColorHolder.TrouserColors.size() - 1))),
Color.valueOf(ColorHolder.EyesColors.get(MathUtils.random(ColorHolder.EyesColors.size() - 1))),
Color.valueOf(ColorHolder.ShoesColors.get(MathUtils.random(ColorHolder.ShoesColors.size() - 1))));
this.shader = new ShaderProgram(vertexShader, fragmentShader);
}
EDIT: I've found out, that if I change the texture of the person so that the whole head has a black 1px border, the head won't get drawn in a weird way. The same for the hair, which also gives me a weird look, if the grayscale part is not fully enclosed by black pixels. Can somebody explain this?