Saturday, December 23, 2017

textures - Creating a retro-style palette swapping effect in OpenGL


I'm working on a Megaman-like game where I need to change the color of certain pixels at runtime. For reference: in Megaman when you change your selected weapon then main character's palette changes to reflect the selected weapon. Not all of the sprite's colors change, only certain ones do.


This kind of effect was common and quite easy to do on the NES since the programmer had access to the palette and the logical mapping between pixels and palette indices. On modern hardware, though, this is a bit more challenging because the concept of palettes is not the same.


All of my textures are 32-bit and do not use palettes.


There are two ways I know of to achieve the effect I want, but I'm curious if there are better ways to achieve this effect easily. The two options I know of are:



  1. Use a shader and write some GLSL to perform the "palette swapping" behavior.


  2. If shaders are not available (say, because the graphics card doesn't support them) then it is possible to clone the "original" textures and generate different versions with the color changes pre-applied.


Ideally I would like to use a shader since it seems straightforward and requires little additional work opposed to the duplicated-texture method. I worry that duplicating textures just to change a color in them is wasting VRAM -- should I not worry about that?


Edit: I ended up using the accepted answer's technique and here is my shader for reference.


uniform sampler2D texture;
uniform sampler2D colorTable;
uniform float paletteIndex;

void main()
{

vec2 pos = gl_TexCoord[0].xy;
vec4 color = texture2D(texture, pos);
vec2 index = vec2(color.r + paletteIndex, 0);
vec4 indexedColor = texture2D(colorTable, index);
gl_FragColor = indexedColor;
}

Both textures are 32-bit, one texture is used as lookup table containing several palettes which are all the same size (in my case 6 colors). I use the source pixel's red channel as an index to the color table. This worked like a charm for achieving Megaman-like palette swapping!



Answer



I wouldn't worry about wasting VRAM for a few character textures.



To me using your option 2. (with different textures or different UV offsets if that fits) is the way to go: more flexible, data-driven, less impact on the code, less bugs, less worries.




This put aside, if you start to accumulate tons of characters with tons of sprite animations in memory, maybe you could start using what's recommended by OpenGL, do-it-yourself palettes:



Paletted textures


Support for the EXT_paletted_texture extension has been dropped by the major GL vendors. If you really need paletted textures on new hardware, you may use shaders to achieve that effect.


Shader example:


//Fragment shader
#version 110
uniform sampler2D ColorTable; //256 x 1 pixels

uniform sampler2D MyIndexTexture;
varying vec2 TexCoord0;

void main()
{
//What color do we want to index?
vec4 myindex = texture2D(MyIndexTexture, TexCoord0);
//Do a dependency texture read
vec4 texel = texture2D(ColorTable, myindex.xy);
gl_FragColor = texel; //Output the color

}

This is simply sampling in ColorTable (a palette in RGBA8), using MyIndexTexture (an 8 bits square texture in indexed colors). Just reproduces the way retro-style palettes work.




The above-quoted example uses two sampler2D, where it could actually use one sampler1D + one sampler2D. I suspect this is for compatibility reasons (no one-dimensional textures in OpenGL ES)... But nevermind, for desktop OpenGL this can be simplified to:


uniform sampler1D Palette;             // A palette of 256 colors
uniform sampler2D IndexedColorTexture; // A texture using indexed color
varying vec2 TexCoord0; // UVs

void main()

{
// Pick up a color index
vec4 index = texture2D(IndexedColorTexture, TexCoord0);
// Retrieve the actual color from the palette
vec4 texel = texture1D(Palette, index.x);
gl_FragColor = texel; //Output the color
}

Palette is a one-dimensional texture of "real" colors (e.g. GL_RGBA8), and IndexedColorTexture is a two-dimensional textures of indexed colors (typically GL_R8, which gives you 256 indices). To create those, there are several authoring tools and image file formats out there that support paletted images, it should be doable to find the one that fits your needs.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...