Hi all,
Does anyone know how I could replace the colors on a texture in run-time using OpenGL (more specifically LWJGL)? All the sprites I am loading for my game consist only of 8 shades of grey, and what I want to do is replace each of those shades of grey with a specific color which is retrieved from another 1 x 8 pixel texture. The following picture demonstrates exactly what I want to be able to do.
The reason I want to do this is to be able to reuse the same sprite for multiple things in the game. A sword for example might have the same sprite but have a different color palette applied to it depending on which material it is made of.
I've implemented a system for this once in Java2D, which I obviously can't use now that I'm using OpenGL, but I was wondering whether this could be achieved with shaders or something. Please note that in this case I can't simply use color tinting or changing the hue, since I want to be able to control the specific color for each shade of grey (so that shadows can be bluish/purplish while highlights can be more yellow/orange etc.). In my Java2D game I had implemented it so that I had 4 channels on each sprite, grey, red, green and blue, and each channel could have its own color palette applied.
Thanks in advance.
[Solved] Applying Color Palette to Texture
You can do this with a texture lookup in a shader. Create a bunch of 1d texture containing your palettes. When you're drawing something bind the appropriate palette texture to another texture unit. Do a texture lookup on the greyscale texture to get a 0...1 result, then do a lookup on the palette texture using that result to get the final colour.
You can fine-tune the behaviour by e.g. selecting a GL_NEAREST sampling mode.
That's one way of doing it; there are others, such as loading your palette into an array of uniforms (or using a UBO) and using the greyscale lookup * 255 as an index into that.
You can fine-tune the behaviour by e.g. selecting a GL_NEAREST sampling mode.
That's one way of doing it; there are others, such as loading your palette into an array of uniforms (or using a UBO) and using the greyscale lookup * 255 as an index into that.
This is called gradient mapping, L4D2 used it to create a great variation of zombies. Take a look at their publication Shading a Bigger, Better Sequel: Techniques in Left 4 Dead 2 from valve, which you can download here.
Thanks guys! I've made it work and thought I'd post the relevant code here to help anyone else trying to do this.
So I did basically exactly what mhagain suggested. I load both my sprite and palette as textures (both 2D textures for now) and then I have a fragment shader which gets the brightness value from my sprite and returns a color from the palette based on that value.
Fragment Shader:
In the above code only the red value of the texture color is used to look up the color in the palette. The reason for this is that the sprite is greyscaled, meaning that the R,G and B values are equal, and I felt it would be a waste to calculate the average (brightness) since it would give the same value. The gl_TexCoord[0] is set in the vertex shader which is quite simple as well.
Vertex Shader:
Lastly I'll just include the Java code for actually using these shaders (excluding the loading/compiling part):
Just to summarize the above code: I first bind the palette texture to GL_TEXTURE1 and enable the shader to reference it using the variable name "palette". Then I do the same thing for the sprite texture except I use GL_TEXTURE0 and the variable name "texture". I then simply draw a quad with the sprite texture in immediate mode.
So thanks again for the help and I hope that this helps anyone else with a similar issue.
So I did basically exactly what mhagain suggested. I load both my sprite and palette as textures (both 2D textures for now) and then I have a fragment shader which gets the brightness value from my sprite and returns a color from the palette based on that value.
Fragment Shader:
uniform sampler2D texture;
uniform sampler2D palette;
void main()
{
vec4 textureColor = texture2D(texture, gl_TexCoord[0].xy).rgba;
vec3 paletteColor = texture2D(palette, vec2(textureColor.r, 0)).rgb;
gl_FragColor = vec4(paletteColor.r, paletteColor.g, paletteColor.b, textureColor.a);
}
In the above code only the red value of the texture color is used to look up the color in the palette. The reason for this is that the sprite is greyscaled, meaning that the R,G and B values are equal, and I felt it would be a waste to calculate the average (brightness) since it would give the same value. The gl_TexCoord[0] is set in the vertex shader which is quite simple as well.
Vertex Shader:
void main() {
gl_Position = ftransform();
gl_TexCoord[0] = gl_MultiTexCoord0;
}
Lastly I'll just include the Java code for actually using these shaders (excluding the loading/compiling part):
int paletteLocation = GL20.glGetUniformLocation(shaderProgram, "palette");
GL20.glUniform1i(paletteLocation, 1);
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, palette.getTextureID());
int textureLocation = GL20.glGetUniformLocation(shaderProgram, "texture");
GL20.glUniform1i(textureLocation, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture.getTextureID());
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(0, 0);
GL11.glTexCoord2f(1, 0);
GL11.glVertex2f(64, 0);
GL11.glTexCoord2f(1, 1);
GL11.glVertex2f(64, 128);
GL11.glTexCoord2f(0, 1);
GL11.glVertex2f(0, 128);
GL11.glEnd();
Just to summarize the above code: I first bind the palette texture to GL_TEXTURE1 and enable the shader to reference it using the variable name "palette". Then I do the same thing for the sprite texture except I use GL_TEXTURE0 and the variable name "texture". I then simply draw a quad with the sprite texture in immediate mode.
So thanks again for the help and I hope that this helps anyone else with a similar issue.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement