#Palette Shader Shenanigans

1 messages · Page 1 of 1 (latest)

tranquil jetty
#

Alright, I'm... mildly confused on this one to say the least.

I have a fairly simple palette swap (or so I thought) shader that's rigged up like so currently:

shader_type canvas_item;

uniform sampler2D palette: filter_nearest;

void fragment() {
    float alpha = COLOR.a;
    highp vec4 color = texture(TEXTURE, UV);
    COLOR = texture(palette, vec2(color.r + 0.1, color.g + 0.1));
    COLOR.a *= alpha;
}

Where the origin texture's color steps are spaced out by 0.2. i.e. red channel controls x at 0.2 step size, green channel controls y at 0.2 step size.

The origin image looks exactly like I expect, and has colors exactly like I expect: 0 - 204 in 8 bit uints for the red channel, because we never want to use 255 because that would put us off the right side of the texture, rather than centered in a given texel.

The palette is 5x5, so in theory the COLOR should be being replaced appropriately - red 0 green 0 -> 0.0 + 0.1, 0.0 + 0.1, centered in the 0.0 - 0.2 square in the palette. 0.2 red 0.0 green -> 0.2 + 0.1, 0.0 + 0.1 -> centered in the 0.2 - 0.4 square, etc.

Instead, I'm getting something else entirely. 5 separate colors are being condensed into 3, where somehow we're ending up with:
0.0, 0.0 pointing at the first palette texel. 0.2, 0.0 pointing at the first palette texel. 0.4, 0.0 pointing at the second palette texel, 0.8, 0.0 pointing at the 4th palette texel... and 0.0, 0.1 pointing at the first palette texel?

Am I missing something obvious here? Is texture returning a much lower precision float than I'm expecting somehow? I'm very confused at this point. I've tried swapping out the precision declaration on the color vec4 but that doesn't seem to do anything. I've tried using COLOR directly.

I've also tried doing the same thing with an input of vec4s as source_colors with almost exactly the same output with different texture atlas sizes, a la: int(round(color.r * 5.0 + color.g * 25.0)); (r range is again 0.0 -> 0.8, etc)

First image is the input texture, second image is the palette, third image is the output, fourth image is the output compared to the palette
I've also tried changing the input image to directly try to center the UVs - i.e. input color of r = 0.1, g = 0.1 instead of r = 0.0, g = 0.0
Forward+ renderer, if that makes a difference here. All of my other general effects shaders seem to be working fine
It's also extra weird because if I step through what I think the results should be by doing like:

COLOR = texture(palette, vec2(0.1, 0.1)); manually, I -get the colors I'm expecting-

I've also tried a couple other things:

#

First image is the input input, spread as expected (removed the 0.1 offset)

Palette's the same as before

Second image is COLOR = texture(palette, vec2(COLOR.r + 0.0, COLOR.g + 0.0));

Third is COLOR = texture(palette, vec2(COLOR.r + 0.1, COLOR.g + 0.1));

Fourth is as someone suggested on the cafe as they thought UV was in pixels, COLOR = texture(palette, vec2(COLOR.r * 5.0 + 0.1, COLOR.g * 5.0 + 0.1));

Palette below

#

am I missing something obvious, here? I'm so confused at this point.

#

Input texture for that last case, as it's only shades of red:

#

(where each is offset by 0.04 for a 25x1 palette)

#

The only thing I can think of that explains this is that texture colors in the shader aren't value linear as I'm expecting them to be, but I don't see any documentation that says that

#

But the 5x5 would be 0.2 chunks, nominally, and that isn't even working right

tranquil jetty
#

... Oh

#

You know, that might be exactly the problem

#

Linear vs sRGB

#

Let me try something when I get back

tranquil jetty
#
// Converts a color from linear light gamma to sRGB gamma
vec4 fromLinear(vec4 linearRGB)
{
    bvec3 cutoff = lessThan(linearRGB.rgb, vec3(0.0031308));
    vec3 higher = vec3(1.055)*pow(linearRGB.rgb, vec3(1.0/2.4)) - vec3(0.055);
    vec3 lower = linearRGB.rgb * vec3(12.92);

    return vec4(mix(higher, lower, cutoff), linearRGB.a);
}

void fragment() {
    float alpha = COLOR.a;
    vec4 color = fromLinear(COLOR);
    COLOR = texture(palette, vec2(color.r + 0.02, 0.5));
}
#

🥲

#

All better

#

Now to figure out if there's a way to just ingest the texture as sRGB in the first place, rather than converting

swift hemlock
tranquil jetty
#

the palette texture's fine, for that

swift hemlock
tranquil jetty
#

Hmmm

#

doesn't seem to, getting pure black

#

which implies no texture

#

It works for now, I'll probably see if I can dig a bit more at some point into the autoconversions - probably need to look at the engine code to figure that out though

#

I doubttttttttt I'll have perf problems by adding the toSRGB conversion in, but

#

something to keep in mind for the future I suppose

swift hemlock
#

I take it the palettes aren't for performance? Color remapping/cycling?

tranquil jetty
#

Yeah, just going to have a lot of them

#

Potentially