The animation at the beginning of the article comes from in-game screenshots where I use the following palettes to color the body of the character: Finally, we used that to get the color from the palette. The red channel value is a floating-point number between 0 and 1 so we multiply by 255 and we cast to int to retrieve the original gray level between 0 and 255 that is stored in the image. We just use the texture to read the red channel of the current texel. In vec2 TexCoords uniform sampler2D Texture uniform vec4 Palette out vec4 Color void main () Here is my shader, I use GLSL but I think you can easily translate it in another shading language as it is dead simple: I find that it is easier to do it using a uniform array so I use that. There are two strategies to pass the palette to the shader: by using a texture or a uniform array. Now, that we have preprocessed our images, we are ready to write the shader to finally swap the palettes. Nothing fancy, all the hard work is done by Pillow. Then, it reinterprets it as a grayscale image. fromarray ( palette_colors, mode = 'RGBA' ) return grayscale_image, palette_imageįirstly, the function converts the image to the palette mode. astype ( 'uint8' ) palette_image = Image. asarray ( + ] \įor i in range ( palette_size )]]). getpalette () transparency = list ( indexed_image. asarray ( indexed_image ), 'L' ) # Create the palette open ( f ) # Reinterpret the indexed image as a grayscale image save ( f, 'png' ) indexed_image = Image.
Palette swap hyper dbz update#
# Save and load the image to update the info (transparency field in particular)į = io. convert ( mode = 'P', dither = 'NONE', colors = palette_size ) # Be careful it can remove colors Import io import numpy as np from PIL import Image def convert_to_indexed_image ( image, palette_size ): # Convert to an indexed image To do that, I use a little Python function that uses the Pillow library: The image is in grayscale mode and the gray level of each pixel corresponds to the index of its color in the palette. it replaces indices by the corresponding color in the palette.Ĭonsequently, to avoid this problem, I store separately the image and the palette. It uses stb_image under the hood which automatically “depalettizes” images i.e. It is the case of SFML, the library I used. Unfortunately, many libraries that load images will provide an array of colors even if the image was stored in indexed mode. For instance, the PNG image format has an indexed color option. In fact, several image formats support this way of storing images.
This way, we decouple the structure of the image (the areas with the same color) with the real colors. What we would like instead is that each pixel contains the index of its color in a palette. In a raster image, each pixel contains a color. The first step is to prepare your images for palette swapping. Now, it is used in procedural generation to produce new assets, I will show many examples in later devlogs. It was a useful technique in the old days to add variety in the assets without using too much memory.
Here, we will do that at runtime using shaders. Palette swapping is simply changing the palette of a texture. In this devlog, I will show you a technique that I love and that I will abuse in Vagabond: palette swapping.