Color-swapping Shader after using Voxeloptimizer

4.1.4rc (custom version, zylanns voxel tools)

Hello, I have tried getting this to work for some time now without success:

Short version:

I have a .obj model imported into godot with a blurry texture, I assume due to sampling is set to “linear mipmap” while I need “nearest”. I can override the material and solve that, but then I have a palette-swapping shader I want to apply to the re-sampled texture, how do I apply that shader? Is there another way to solve my problem, for example importing with nearest sampling from start?

Long version:

I am creating voxel models using MagicaVoxel. When exporting to obj, I noticed that a huge amount of vertices are created for the model because each voxel gets a vertex when the model has voxels of different colors.

I found a tool to solve this issue: GitHub - davidevofficial/voxel_optimizer: An optimizer for the magicavoxel exports. Written in rust.
which I think does what I want; separating the texture from the model, and then wraps the texture on the model.

The question is: How would I go about creating a shader that swaps the colors on such an optimized model? I managed to create a shader for the previous non-optimized version:

/*
This shader replaces some specific greyscale colors by checking the float value of
their red portion of the color(don't need the entire spectrum at this moment...).
*/
shader_type spatial;

// Most examples names this e.g. "texture", but here I load a png of the palette I use...
uniform sampler2D palette : repeat_enable;

uniform vec3 color_swap[3] : source_color;


// fragment is called on each pixel of a texture
void fragment() 
{
    vec3 input_color = texture(palette, UV).rgb; // get the specific pixel's color

    if (input_color.r == 1.0) // Replace white stuff
        ALBEDO = color_swap[0];// - vec3(0.1, 0.1, 0.1);
    else if (input_color.r > 0.87) // Replace 224 stuff
        ALBEDO = color_swap[1] - vec3(0.05, 0.05, 0.05);
    else if (input_color.r > 0.75) // Replace 192 stuff
        ALBEDO = color_swap[2] - vec3(0.1, 0.1, 0.1);
    else if (input_color.r > 0.65) // Replace 168 stuff
        ALBEDO = color_swap[2] - vec3(0.15, 0.15, 0.15);
    else if (input_color.r > 0.5) // Replace 128 stuff
        ALBEDO = color_swap[1];
}

I apply this shader in surface material override:

And it works fine. It does not work for the optimized model however, because the texture is blurry, here’s an example of a head model without the shader, the texture is supposed to have a darker tone on the ears:

When shader is applied:


It kinda works as I coded it I guess, but since the texture is blurry, I get this result.

According to the tutorial on how to use the voxeloptimizer, I need to add the texture as a material override and set sampling to nearest.

After override material, no shader:


Ears on this head model has slightly darker tone. Nice!

The question is, can I apply the palette-swapping shader on the re-sampled texture somehow? I tried adding the shader in the “next pass” but I don’t know how I can get it to use the re-sampled texture…

I’d suggest you make your shader take two textures; the original texture, and a texture that’s essentially a palette. Take the color of the original texture and use its channel values to create a UV on the palette texture, use the color you get from sampling that UV on the palette texture.

Of course I found my solution as soon as I created the topic…

Setting the palette variable as filter_nearest makes the texture not blurry:

/*
This shader replaces some specific greyscale colors by checking the float value of
their red portion of the color(don't need the entire spectrum at this moment...).
*/
shader_type spatial;

// Most examples names this e.g. "texture", but here I load a png of the palette I use...
uniform sampler2D palette : filter_nearest;

uniform vec3 color_swap[3] : source_color;


// fragment is called on each pixel of a texture
void fragment() 
{
    vec3 input_color = texture(palette, UV).rgb; // get the specific pixel's color

    if (input_color.r == 1.0) // Replace white stuff
        ALBEDO = color_swap[0];// - vec3(0.1, 0.1, 0.1);
    else if (input_color.r > 0.87) // Replace 224 stuff
        ALBEDO = color_swap[1] - vec3(0.05, 0.05, 0.05);
    else if (input_color.r > 0.75) // Replace 192 stuff
        ALBEDO = color_swap[2] - vec3(0.1, 0.1, 0.1);
    else if (input_color.r > 0.65) // Replace 168 stuff
        ALBEDO = color_swap[2] - vec3(0.15, 0.15, 0.15);
    else if (input_color.r > 0.5) // Replace 128 stuff
        ALBEDO = color_swap[1];
}

Now it works as intended!