1
\$\begingroup\$

I've been able to implement 9Slice for textures when the texture is just itself, that means, it does not contain any subtexture except itself. This is the code I'm using:

#version 330 core

in vec2 uv;
in vec4 color;
in vec2 size;
in vec4 nine_slice;

uniform sampler2D tex;
uniform float dt;
uniform vec2 mouse_position;
uniform vec2 texture_size;

layout(location = 0) out vec4 out_color;

vec2 uv9slice(vec2 uv, vec2 s, vec4 b) {
    vec2 t = clamp((s * uv - b.xy) / (s - b.xy - b.zw), 0., 1.);
        return mix(uv * s, 1. - s * (1. - uv), t);
}

vec4 draw_nine_slice() {
    vec2 _s = size.xy / texture_size;
    vec4 _b = nine_slice / texture_size.xxyy;
    vec2 _uv = uv9slice(uv, _s, _b);
    vec3 _col = vec3(texture(tex, _uv).x);
    return vec4(_col, 1.0);
}

void main(void) {
        out_color = draw_nine_slice();
}

Where vars are:

  • size: Size of the nine patch (the imgui window shown on the second image)
  • nine_slice: These are the paddings vec4(left, right, bottom, top)
  • texture_size: This is the size of the texture, which will be fixed. If it is a non-atlas texture, the size is the size of the whole image, if it is a subtexture from an atlas, the size will be the size of the subtexture inside the atlas. This is the texture:

Single texture panel

And this is how it looks with the 9-Slice:

Working example

Working example of single texture 9-Slice

So as you can see, it expands nicely. The problem comes when I use my texture atlas (change the white color of the sub textures with gray, as they could not be seen on this site white background):

Texture Atlas

I have a way in my program to get the texture coordinates, size and everything from each subtexture in the atlas, and when I render them as a non-9Slice it works great, the problem is the shader. If I try to run the engine with a subtexture from the atlas, the shader I posted before won't work, as it takes the whole texture as if it was a single one (which works on the previuos case but not this one). This is what happens when running with the shader:

Wrong behaviour

Which make sense (don't mind the transparency not working). I know I have to change how the coordinates are calculated in the shader to be relative to the sub-texture coordinates, but I cannot really seem to understand how to do it, this is what I tried:

#version 330 core

in vec2 uv;
in vec4 color;
in vec2 size;
in vec4 nine_slice;

uniform sampler2D tex;
uniform float dt;
uniform vec2 mouse_position;
uniform vec2 texture_size; // If drawing a subtexture from an atlas, this is the size of the subtexture

layout(location = 0) out vec4 out_color;

vec2 uv9slice(vec2 _uv, vec2 _s, vec4 _b) {
    vec2 _t = clamp((_s * _uv - _b.xz) / (_s - _b.xz - _b.yw), 0.0, 1.0);
    vec2 _t_0 = _uv * _s;
    vec2 _t_1 = 1.0 - _s * (1.0 - _uv);
    return clamp(mix(_t_0, _t_1, _t), vec2(0, 0.75), vec2(0.25, 1.0));
}

vec4 draw_nine_slice() {
    vec2 _s = size.xy / texture_size;
    vec4 _b = nine_slice / texture_size.xxyy;
    vec2 _uv = uv9slice(uv, _s, _b);
    vec3 _col = vec3(texture(tex, _uv).x);
    return vec4(_col, 1.0);
}

void main(void) {
    out_color = draw_nine_slice()
}

Values are hardcoded in here, because I'm doing a test. Bottom-Left coord of the texture in the atlas is (0.0, 0.75) and Top-Right is (0.25, 1.0).

I thought clamping the values within the sub-texture range would work, but it does not. I tried also other combinations but noting seems to be working, does anyone have an idea on how to achieve the first behaviour with the single texture on the second scenario with an atlas?

Tried different ways to convert from whole image coordinates to a sub-space of the image, one of the attempts is in the description of the issue, no success with any of them.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Figured it out guys, the shader is not as compact as the first one, but is working like a charm, with single textures and with atlas textures:

#version 330 core

in vec2 uv;
in vec4 color;
in vec2 size;
in vec4 nine_slice;
in vec4 sub_texture_uv;

uniform sampler2D tex;
uniform vec2 texture_size; // If drawing a subtexture from an atlas, this is the size of the subtexture

layout(location = 0) out vec4 out_color;

vec4 draw_nine_slice() {
    // This is just to make the image scale correctly
    vec2 _prop = vec2(sub_texture_uv.z - sub_texture_uv.x, sub_texture_uv.w - sub_texture_uv.y);

    // Point of start if sub-texture
    vec2 _ratio = (texture_size.xy / size.xy);
    vec2 _border = vec2(nine_slice.x, nine_slice.z);
    vec2 _border_custom_prop = (_border / size) * _prop;
    vec2 _border_original_prop = (_border / texture_size) * _prop;
    vec2 _texture_coord = vec2(0);
    vec2 _atlas_offset = vec2(0);

    //         x padding min   x padding max
    //               |            |
    //        *------*------------*------*
    //        |      |            |      |
    //        |  L/T |       M/T  |  R/T |
    //        |      |            |      |
    //      --*------*------------*------*-- y padding max
    //        |      |            |      |
    //        |  L/M |       M/M  |  R/M |
    //        |      |            |      |
    //      --*------*------------*------*-- y padding min
    //        |      |            |      |
    //        |  L/D |       M/D  |  R/D |
    //        |      |            |      |
    //        *------*------------*------*
    //               |            |

    // --------------------- X Axis
    // Left
    if(uv.x < sub_texture_uv.x + _border_custom_prop.x) {
        _texture_coord.x = (uv.x - sub_texture_uv.x) / _ratio.x;
        _atlas_offset.x = sub_texture_uv.x;
    }

    // Right
    else if(uv.x > (sub_texture_uv.z - _border_custom_prop.x)) {
        _texture_coord.x = (uv.x - sub_texture_uv.z) / _ratio.x;
        _atlas_offset.x = sub_texture_uv.z;
    }

    // Middle
    else {
        _texture_coord.x = uv.x + (_border_original_prop.x - _border_custom_prop.x);
        if(_texture_coord.x > sub_texture_uv.z - _border_original_prop.x) {
                _texture_coord.x = sub_texture_uv.z - _border_original_prop.x;
        }
    }

    // --------------------- Y Axis
    // Down
    if(uv.y < sub_texture_uv.y + _border_custom_prop.y) {
        _texture_coord.y = (uv.y - sub_texture_uv.y) / _ratio.y;
        _atlas_offset.y = sub_texture_uv.y;
    }

    // Up
    else if(uv.y > (sub_texture_uv.w - _border_custom_prop.y)) {
        _texture_coord.y = (uv.y - sub_texture_uv.w) / _ratio.y;
        _atlas_offset.y = sub_texture_uv.w;
    }

    // Middle
    else {
        _texture_coord.y = uv.y + _border_original_prop.y - _border_custom_prop.y;
        if(_texture_coord.y > sub_texture_uv.w - _border_original_prop.y) {
            _texture_coord.y = sub_texture_uv.w - _border_original_prop.y;
        }
    }

    vec4 _color = texture(tex, _texture_coord + _atlas_offset);
    if(_color.w < 0.05) {
            discard;
    }

    return texture(tex, _texture_coord + _atlas_offset) * vec4(color.x / 255.f, color.y / 255.f, color.z / 255.f, color.w / 255.f);
}

void main() {
    out_color = draw_nine_slice();
}
```
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .