Palette Shaders

Richard KainRichard Kain Posts: 32Member

I've been playing around with Tilengine recently. I'm thinking of trying to embed it into Godot, but I'm not familiar enough with Godot's workflow to really implement that at the moment. But thanks to some discussions over on the Unity 3D boards, I started playing around with some custom shaders in an attempt to cook up a palette-based color solution for 3D models. And it worked.

Just for kicks, I started trying to get something similar implemented in Godot as well. And it has also been working. Turns out that it was a lot easier than I had expected. For those not familiar, really old pixel graphics frequently used color palettes in order to optimize performance while allowing for various graphical tricks. Graphics would only be defined once in an image, but then the image could be changed by altering the color mapping for each pixel of a given index. This was especially useful for re-coloring characters, enemies, and environments.

It was specifically a product of raster graphics, and largely got phased out once GPUs started to become popular. With a little shader coding, though, similar support can be added for modern rendering. Basically, you cook up a shader that keys off a pre-defined palette of colors, and then provide a list of input colors that the user can decide on for themselves. Then you create a material for each of the different color variants you want, and apply the material to the node you want to change. The initial tests I've been doing have been with 2D nodes, but the approach should work for 3D models as well. The biggest caveat is that all of your graphics have to be tailored to the palette you are using as a source. For 2D, you have to stick with the colors you choose for your "base" palette. For 3D, you have to map your UV coordinates to your pre-defined and laid-out palette.

I'll be posting a tutorial in the near future. Is anyone interested in this kind of backwards approach to color handling?

Comments

  • Richard KainRichard Kain Posts: 32Member

    Unsatisfied with my previous attempts, I did a little more research into the subject. I ran across a youtube video that did a pretty good job of digging deeper into the subject, and some of the challenges that it presented.

    The biggest issue at hand is that there is a fundamental split between how indexed palettes are traditionally handled, and how modern GPUs approach rendering. Essentially, it's a matter of integers versus floating point values. GPUs, and their various shader languages, are all about those floating points. While classic scan-line rendering, and the palettes that helped to fuel it, were primarily based on integers and integer calculations/logic.

    Writing modern shaders requires that we focus our data and calculations on floats. But traditional palettes are almost entirely centered around integers. The challenge faced is creating an effective and performance-efficient translation layer between the two.

    After much research and a little trial-and-error, it's pretty clear to me that selecting colors individually on the Material level is probably NOT going to happen. Any such assignment will likely have to happen through a GDScript. I will go greater in-depth later.

    In Unity, I was able to utilize array structures from within the shader language to implement some of this. In Godot that will not be possible, at least not with the current state of the shader language. I thought of trying to jury-rig some matrix definitions for a similar effect, but that seems like it will be more trouble than it would be worth. Fortunately, a different solution presented itself, which I believe should offer a reasonable alternative.

  • Richard KainRichard Kain Posts: 32Member

    I was able to cook up a successful version of this shader that is considerably more simple. It requires some very specific set-up ahead of time in terms of the assets preparation. But it works just fine, and has very, very little actual code in the fragment shader. It also works just fine in Godot 2.1. The computer I use at work doesn't play nice with Godot 3.1, so I cooked up version 1 of this on an older version of Godot, successfully. I'm very pleased so far, and I'm going to work on some texture samples with properly adjusted colors.

  • Richard KainRichard Kain Posts: 32Member
    uniform texture colorTexture;
    
    float red_value = tex(TEXTURE, UV).r;
    COLOR = tex(colorTexture, vec2(red_value, 0.5));
    

    In Godot 2.1, this little shader snippet will get you a solid beginning. The real challenge is in determining what greyscale shades correspond to what U coordinates, and how to select your shades accordingly based on what type of image you want to use for the color selection. The current version of the shader assumes you are using a color selection image that is only one pixel tall. I went ahead and generated a series of source images that can be used for sourcing shades for a greyscale texture, or as a guide for creating and mapping the desired colors.

    I got the basic shader running in the Godot 2.1 editor, but had a little trouble when trying to run it. I'm not sure if the shader is to blame, or my hardware though. Once I get home I'm going to port it up to Godot 3.1, shouldn't take long at all. I've already ported a comparable to shader to Unity, with no problems.

Sign In or Register to comment.