Simulating Palette Swaps with OpenGL Shaders (in LibGDX)

Introduction

Palette swaps are a classic technique in retro gaming for achieving visual variety with limited resources. In this article, we’ll explore how to simulate palette swaps in modern game development using OpenGL shaders in the LibGDX framework.

Understanding Palette Swaps

A palette swap involves replacing colors in an image with different colors from a pre-defined palette. This is achieved by mapping the original color indices to new color indices within the palette.

Setting up LibGDX

  • Create a new LibGDX project.
  • Add a new shader file (e.g., “PaletteSwapShader.glsl”).

Creating the Palette Swap Shader

Vertex Shader (PaletteSwapShader.vert)

#version 100
attribute vec4 a_position;
attribute vec2 a_texCoord0;
varying vec2 v_texCoord;

void main() {
  gl_Position =  u_projTrans * a_position;
  v_texCoord = a_texCoord0;
}

Fragment Shader (PaletteSwapShader.frag)

#version 100
precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_texture;
uniform vec4 u_palette[256];

void main() {
  vec4 color = texture2D(u_texture, v_texCoord);
  int index = int(color.r * 255.0); // Assuming color index is stored in red channel
  gl_FragColor = u_palette[index];
}

Explanation

  • Vertex Shader: Simply passes texture coordinates to the fragment shader.
  • Fragment Shader:
    • Samples the texture color at the current fragment.
    • Extracts the color index (assuming it’s stored in the red channel).
    • Uses the color index to access the corresponding color from the palette.
    • Sets the fragment color to the palette color.

Using the Shader in LibGDX

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;

// ...

// Load texture
Texture texture = new Texture(Gdx.files.internal("texture.png"));

// Create shader program
ShaderProgram shader = new ShaderProgram(Gdx.files.internal("PaletteSwapShader.vert"),
                                      Gdx.files.internal("PaletteSwapShader.frag"));

// Create palette
Pixmap palettePixmap = new Pixmap(Gdx.files.internal("palette.png"));
float[] palette = new float[256 * 4];
palettePixmap.getPixels(palette, 0, 1, 0, 0, palettePixmap.getWidth(),
                              palettePixmap.getHeight());

// Bind texture
texture.bind();

// Bind palette to uniform
shader.setUniformf("u_palette", palette, 0, 256);

// Draw with shader
shader.begin();
shader.setUniformi("u_texture", 0); // Set texture unit
// ... draw your object using a spritebatch ...
shader.end();

Example

texture.png

An 8-bit image with color indices stored in the red channel.

palette.png

A 256×1 image with colors representing the palette.

Output

A visual demonstration of the palette swap applied to the original texture.

Benefits of Using Shaders

  • Dynamic Swaps: You can change the palette at runtime.
  • Performance: Shader operations are often faster than CPU-based palette swapping.
  • Flexibility: Shaders allow for more advanced color manipulation techniques beyond simple swaps.

Conclusion

By leveraging OpenGL shaders, we can efficiently simulate palette swaps in modern game development. This technique opens up creative possibilities for achieving nostalgic visual styles and enhancing gameplay.

Leave a Reply

Your email address will not be published. Required fields are marked *